22 Commits

Author SHA1 Message Date
Piotr Miller
6ac486a32c Merge pull request #12 from nwg-piotr/chrome
bug fixes and new arguments
2021-08-17 01:25:13 +02:00
piotr
875e7cac4a Quit immediately on window click 2021-08-17 00:54:52 +02:00
piotr
2960603462 -nofs to disable file search 2021-08-16 23:26:30 +02:00
piotr
f9e9608dfc -nocats to disable filtering by categories 2021-08-16 22:41:13 +02:00
piotr
83caef28f6 fresh binary 2021-08-15 14:37:06 +02:00
piotr
d732e9e070 support env vars at the end of the command #11 2021-08-15 14:34:25 +02:00
piotr
033a4d59ae refresh binary 2021-07-21 02:07:35 +02:00
Piotr Miller
97be33094f Merge pull request #9 from nwg-piotr/force_img
SetAlwaysShowImage(true) in category button
2021-07-21 01:55:17 +02:00
piotr
e5246a13cd force image in category button #8 2021-07-21 01:52:19 +02:00
piotr
897d693d62 force image in category button 2021-07-21 01:49:34 +02:00
Piotr Miller
955429d809 Merge pull request #7 from nwg-piotr/fix_pinned
Fix GTK critical warnings on pinned cache file not found
2021-07-12 23:32:41 +02:00
piotr
4bf82d9a98 bump to 0.1.4 2021-07-12 23:26:26 +02:00
piotr
eb358b24cd fix GTK warnings on pinned cache not found #6 2021-07-12 23:23:52 +02:00
Piotr Miller
27718a9f1b Merge pull request #5 from nwg-piotr/nodisplay
More appearance personalization
2021-06-29 23:54:17 +02:00
piotr
b4f0f70f56 default css modified 2021-06-29 17:32:44 +02:00
piotr
f54be1c13a name property for category buttons 2021-06-29 17:24:48 +02:00
piotr
cd6e544adc wrong resources path fixed 2021-06-26 23:45:46 +02:00
piotr
f2bda0e496 bump to 0.1.1 2021-06-23 02:55:02 +02:00
piotr
fd8b9ba128 uninstall fixed 2021-06-23 02:53:56 +02:00
Piotr Miller
94aba90f3d Merge pull request #3 from nwg-piotr/sammiev
basic X11 support
2021-06-23 02:48:14 +02:00
piotr
fa9d9c3425 basic X11 support 2021-06-23 02:44:56 +02:00
Piotr Miller
4711c36c56 Update README.md 2021-06-22 12:00:15 +02:00
8 changed files with 131 additions and 57 deletions

View File

@@ -16,6 +16,7 @@ install:
cp bin/nwg-drawer /usr/bin cp bin/nwg-drawer /usr/bin
uninstall: uninstall:
rm -r /usr/share/nwg-drawer
rm /usr/bin/nwg-drawer rm /usr/bin/nwg-drawer
run: run:

View File

@@ -18,6 +18,8 @@ and `nwggrid`.
[more screenshots](https://scrot.cloud/album/nwg-drawer.Bogd) | [see on YouTube](https://youtu.be/iIgxJQhCQf0) [more screenshots](https://scrot.cloud/album/nwg-drawer.Bogd) | [see on YouTube](https://youtu.be/iIgxJQhCQf0)
[![Packaging status](https://repology.org/badge/vertical-allrepos/nwg-drawer.svg)](https://repology.org/project/nwg-drawer/versions)
## Installation ## Installation
### Dependencies ### Dependencies
@@ -62,7 +64,7 @@ Usage of nwg-drawer:
-lang string -lang string
force lang, e.g. "en", "pl" force lang, e.g. "en", "pl"
-o string -o string
name of the Output to display the menu on (sway only) name of the Output to display the drawer on (sway only)
-ovl -ovl
use OVerLay layer use OVerLay layer
-s string -s string

Binary file not shown.

View File

@@ -3,11 +3,12 @@ window {
color: #eeeeee color: #eeeeee
} }
/* search entry */
entry { entry {
background-color: rgba (0, 0, 0, 0.2) background-color: rgba (0, 0, 0, 0.2)
} }
button { button, image {
background: none; background: none;
border: none border: none
} }
@@ -16,6 +17,11 @@ button:hover {
background-color: rgba (255, 255, 255, 0.1) background-color: rgba (255, 255, 255, 0.1)
} }
/* in case you wanted to give category buttons a different look */
#category-button {
margin: 0 10px 0 10px
}
#pinned-box { #pinned-box {
padding-bottom: 5px; padding-bottom: 5px;
border-bottom: 1px dotted gray border-bottom: 1px dotted gray

2
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/nwg-piotr/nwg-menu module github.com/nwg-piotr/nwg-drawer
go 1.16 go 1.16

120
main.go
View File

@@ -19,7 +19,7 @@ import (
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
) )
const version = "0.1.0" const version = "0.1.5"
var ( var (
appDirs []string appDirs []string
@@ -90,6 +90,7 @@ var (
fileSearchResultWrapper *gtk.Box fileSearchResultWrapper *gtk.Box
pinnedFlowBox *gtk.FlowBox pinnedFlowBox *gtk.FlowBox
pinnedFlowBoxWrapper *gtk.Box pinnedFlowBoxWrapper *gtk.Box
categoriesWrapper *gtk.Box
catButtons []*gtk.Button catButtons []*gtk.Button
statusLabel *gtk.Label statusLabel *gtk.Label
status string status string
@@ -98,7 +99,7 @@ var (
// Flags // Flags
var cssFileName = flag.String("s", "drawer.css", "Styling: css file name") var cssFileName = flag.String("s", "drawer.css", "Styling: css file name")
var targetOutput = flag.String("o", "", "name of the Output to display the menu on (sway only)") var targetOutput = flag.String("o", "", "name of the Output to display the drawer on (sway only)")
var displayVersion = flag.Bool("v", false, "display Version information") var displayVersion = flag.Bool("v", false, "display Version information")
var overlay = flag.Bool("ovl", false, "use OVerLay layer") var overlay = flag.Bool("ovl", false, "use OVerLay layer")
var iconSize = flag.Int("is", 64, "Icon Size") var iconSize = flag.Int("is", 64, "Icon Size")
@@ -109,6 +110,8 @@ var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"")
var fileManager = flag.String("fm", "thunar", "File Manager") var fileManager = flag.String("fm", "thunar", "File Manager")
var term = flag.String("term", "alacritty", "Terminal emulator") var term = flag.String("term", "alacritty", "Terminal emulator")
var nameLimit = flag.Int("fslen", 80, "File Search name length Limit") var nameLimit = flag.Int("fslen", 80, "File Search name length Limit")
var noCats = flag.Bool("nocats", false, "Disable filtering by category")
var noFS = flag.Bool("nofs", false, "Disable file search")
func main() { func main() {
timeStart := time.Now() timeStart := time.Now()
@@ -172,6 +175,7 @@ func main() {
if err != nil { if err != nil {
pinned = nil pinned = nil
} }
println(fmt.Sprintf("Found %v pinned items", len(pinned)))
cssFile := filepath.Join(configDirectory, *cssFileName) cssFile := filepath.Join(configDirectory, *cssFileName)
@@ -204,39 +208,50 @@ func main() {
log.Fatal("Unable to create window:", err) log.Fatal("Unable to create window:", err)
} }
layershell.InitForWindow(win) if wayland() {
layershell.InitForWindow(win)
var output2mon map[string]*gdk.Monitor var output2mon map[string]*gdk.Monitor
if *targetOutput != "" { if *targetOutput != "" {
// We want to assign layershell to a monitor, but we only know the output name! // We want to assign layershell to a monitor, but we only know the output name!
output2mon, err = mapOutputs() output2mon, err = mapOutputs()
if err == nil { if err == nil {
monitor := output2mon[*targetOutput] monitor := output2mon[*targetOutput]
layershell.SetMonitor(win, monitor) layershell.SetMonitor(win, monitor)
} else { } else {
println(err) println(err)
}
} }
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_BOTTOM, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_TOP, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_LEFT, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_RIGHT, true)
if *overlay {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_OVERLAY)
layershell.SetExclusiveZone(win, -1)
} else {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP)
}
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
} }
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_BOTTOM, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_TOP, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_LEFT, true)
layershell.SetAnchor(win, layershell.LAYER_SHELL_EDGE_RIGHT, true)
if *overlay {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_OVERLAY)
layershell.SetExclusiveZone(win, -1)
} else {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP)
}
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
win.Connect("destroy", func() { win.Connect("destroy", func() {
gtk.MainQuit() gtk.MainQuit()
}) })
win.Connect("button-release-event", func(sw *gtk.Window, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 || btnEvent.Button() == 3 {
gtk.MainQuit()
return true
}
return false
})
win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool { win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool {
key := &gdk.EventKey{Event: event} key := &gdk.EventKey{Event: event}
switch key.KeyVal() { switch key.KeyVal() {
@@ -251,7 +266,6 @@ func main() {
return false return false
case gdk.KEY_downarrow, gdk.KEY_Up, gdk.KEY_Down, gdk.KEY_Left, gdk.KEY_Right, gdk.KEY_Tab, case gdk.KEY_downarrow, gdk.KEY_Up, gdk.KEY_Down, gdk.KEY_Left, gdk.KEY_Right, gdk.KEY_Tab,
gdk.KEY_Return, gdk.KEY_Page_Up, gdk.KEY_Page_Down, gdk.KEY_Home, gdk.KEY_End: gdk.KEY_Return, gdk.KEY_Page_Up, gdk.KEY_Page_Down, gdk.KEY_Home, gdk.KEY_End:
//searchEntry.SetText("")
return false return false
default: default:
@@ -274,6 +288,17 @@ func main() {
cancelClose() cancelClose()
}) })
/*
In case someone REALLY needed to use X11 - for some stupid Zoom meeting or something, this allows
the drawer to behave properly on Openbox, and possibly somewhere else. For sure not on i3.
This feature is not really supported and will stay undocumented.
*/
if !wayland() {
println("Not Wayland, oh really?")
win.SetDecorated(false)
win.Maximize()
}
// Set up UI // Set up UI
outerVBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) outerVBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
win.Add(outerVBox) win.Add(outerVBox)
@@ -285,10 +310,12 @@ func main() {
searchEntry.SetMaxWidthChars(30) searchEntry.SetMaxWidthChars(30)
searchBoxWrapper.PackStart(searchEntry, true, false, 0) searchBoxWrapper.PackStart(searchEntry, true, false, 0)
categoriesWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) if !*noCats {
categoriesButtonBox := setUpCategoriesButtonBox() categoriesWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
categoriesWrapper.PackStart(categoriesButtonBox, true, false, 0) categoriesButtonBox := setUpCategoriesButtonBox()
outerVBox.PackStart(categoriesWrapper, false, false, 0) categoriesWrapper.PackStart(categoriesButtonBox, true, false, 0)
outerVBox.PackStart(categoriesWrapper, false, false, 0)
}
pinnedWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) pinnedWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
outerVBox.PackStart(pinnedWrapper, false, false, 0) outerVBox.PackStart(pinnedWrapper, false, false, 0)
@@ -298,10 +325,19 @@ func main() {
pinnedFlowBox = setUpPinnedFlowBox() pinnedFlowBox = setUpPinnedFlowBox()
resultWindow, _ = gtk.ScrolledWindowNew(nil, nil) resultWindow, _ = gtk.ScrolledWindowNew(nil, nil)
resultWindow.SetEvents(int(gdk.ALL_EVENTS_MASK))
resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
resultWindow.Connect("enter-notify-event", func() { resultWindow.Connect("enter-notify-event", func() {
cancelClose() cancelClose()
}) })
resultWindow.Connect("button-release-event", func(sw *gtk.ScrolledWindow, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 || btnEvent.Button() == 3 {
gtk.MainQuit()
return true
}
return false
})
outerVBox.PackStart(resultWindow, true, true, 10) outerVBox.PackStart(resultWindow, true, true, 10)
resultsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) resultsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
@@ -328,11 +364,13 @@ func main() {
resultsWrapper.PackStart(placeholder, true, true, 0) resultsWrapper.PackStart(placeholder, true, true, 0)
placeholder.SetSizeRequest(20, 20) placeholder.SetSizeRequest(20, 20)
wrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) if !*noFS {
fileSearchResultWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) wrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
fileSearchResultWrapper.SetProperty("name", "files-box") fileSearchResultWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
wrapper.PackStart(fileSearchResultWrapper, true, false, 0) fileSearchResultWrapper.SetProperty("name", "files-box")
resultsWrapper.PackEnd(wrapper, false, false, 10) wrapper.PackStart(fileSearchResultWrapper, true, false, 0)
resultsWrapper.PackEnd(wrapper, false, false, 10)
}
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
outerVBox.PackStart(statusLineWrapper, false, false, 10) outerVBox.PackStart(statusLineWrapper, false, false, 10)
@@ -340,9 +378,13 @@ func main() {
statusLineWrapper.PackStart(statusLabel, true, false, 0) statusLineWrapper.PackStart(statusLabel, true, false, 0)
win.ShowAll() win.ShowAll()
fileSearchResultWrapper.SetSizeRequest(appFlowBox.GetAllocatedWidth(), 1) if !*noFS {
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.GetAllocatedHeight()*2) fileSearchResultWrapper.SetSizeRequest(appFlowBox.GetAllocatedWidth(), 1)
fileSearchResultWrapper.Hide() fileSearchResultWrapper.Hide()
}
if !*noCats {
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.GetAllocatedHeight()*2)
}
t := time.Now() t := time.Now()
println(fmt.Sprintf("UI created in %v ms. Thank you for your patience.", t.Sub(timeStart).Milliseconds())) println(fmt.Sprintf("UI created in %v ms. Thank you for your patience.", t.Sub(timeStart).Milliseconds()))

View File

@@ -20,6 +20,10 @@ import (
"github.com/joshuarubin/go-sway" "github.com/joshuarubin/go-sway"
) )
func wayland() bool {
return os.Getenv("WAYLAND_DISPLAY") != "" || os.Getenv("XDG_SESSION_TYPE") == "wayland"
}
/* /*
Window leave-notify-event event quits the program with glib Timeout 500 ms. Window leave-notify-event event quits the program with glib Timeout 500 ms.
We might have left the window by accident, so let's clear the timeout if window re-entered. We might have left the window by accident, so let's clear the timeout if window re-entered.
@@ -264,7 +268,7 @@ func listDesktopFiles() []string {
} }
func setUpCategories() { func setUpCategories() {
path := filepath.Join(getDataHome(), "nwg-menu/desktop-directories") path := filepath.Join(getDataHome(), "nwg-drawer/desktop-directories")
var other category var other category
for _, cName := range categoryNames { for _, cName := range categoryNames {
@@ -579,17 +583,19 @@ func launch(command string, terminal bool) {
envVarsNum := strings.Count(command, "=") envVarsNum := strings.Count(command, "=")
var envVars []string var envVars []string
cmdIdx := 0 cmdIdx := -1
lastEnvVarIdx := 0
if envVarsNum > 0 { if envVarsNum > 0 {
for idx, item := range elements { for idx, item := range elements {
if strings.Contains(item, "=") { if strings.Contains(item, "=") {
lastEnvVarIdx = idx
envVars = append(envVars, item) envVars = append(envVars, item)
} else if !strings.HasPrefix(item, "-") && cmdIdx == -1 {
cmdIdx = idx
} }
} }
cmdIdx = lastEnvVarIdx + 1 }
if cmdIdx == -1 {
cmdIdx = 0
} }
cmd := exec.Command(elements[cmdIdx], elements[1+cmdIdx:]...) cmd := exec.Command(elements[cmdIdx], elements[1+cmdIdx:]...)

View File

@@ -17,7 +17,7 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
flowBox, _ := gtk.FlowBoxNew() flowBox, _ := gtk.FlowBoxNew()
if uint(len(pinned)) >= *columnsNumber { if uint(len(pinned)) >= *columnsNumber {
flowBox.SetMaxChildrenPerLine(*columnsNumber) flowBox.SetMaxChildrenPerLine(*columnsNumber)
} else { } else if len(pinned) > 0 {
flowBox.SetMaxChildrenPerLine(uint(len(pinned))) flowBox.SetMaxChildrenPerLine(uint(len(pinned)))
} }
@@ -78,16 +78,17 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
}) })
flowBox.Add(btn) flowBox.Add(btn)
} }
pinnedFlowBoxWrapper.PackStart(flowBox, true, false, 0)
//While moving focus with arrow keys we want buttons to get focus directly
flowBox.GetChildren().Foreach(func(item interface{}) {
item.(*gtk.Widget).SetCanFocus(false)
})
} }
flowBox.Connect("enter-notify-event", func() { flowBox.Connect("enter-notify-event", func() {
cancelClose() cancelClose()
}) })
pinnedFlowBoxWrapper.PackStart(flowBox, true, false, 0)
//While moving focus with arrow keys we want buttons to get focus directly
flowBox.GetChildren().Foreach(func(item interface{}) {
item.(*gtk.Widget).SetCanFocus(false)
})
flowBox.ShowAll() flowBox.ShowAll()
return flowBox return flowBox
@@ -113,6 +114,7 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
eventBox.Add(hBox) eventBox.Add(hBox)
button, _ := gtk.ButtonNewWithLabel("All") button, _ := gtk.ButtonNewWithLabel("All")
button.SetProperty("name", "category-button")
button.Connect("clicked", func(item *gtk.Button) { button.Connect("clicked", func(item *gtk.Button) {
searchEntry.SetText("") searchEntry.SetText("")
appFlowBox = setUpAppsFlowBox(nil, "") appFlowBox = setUpAppsFlowBox(nil, "")
@@ -126,8 +128,11 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
for _, cat := range categories { for _, cat := range categories {
if isSupposedToShowUp(cat.Name) { if isSupposedToShowUp(cat.Name) {
button, _ = gtk.ButtonNewFromIconName(cat.Icon, gtk.ICON_SIZE_MENU) button, _ = gtk.ButtonNewFromIconName(cat.Icon, gtk.ICON_SIZE_MENU)
button.SetProperty("name", "category-button")
catButtons = append(catButtons, button) catButtons = append(catButtons, button)
button.SetLabel(cat.DisplayName) button.SetLabel(cat.DisplayName)
// fix #8
button.SetAlwaysShowImage(true)
hBox.PackStart(button, false, false, 0) hBox.PackStart(button, false, false, 0)
name := cat.Name name := cat.Name
b := *button b := *button
@@ -314,13 +319,17 @@ func setUpSearchEntry() *gtk.SearchEntry {
phrase, _ = searchEntry.GetText() phrase, _ = searchEntry.GetText()
if len(phrase) > 0 { if len(phrase) > 0 {
// search apps
appFlowBox = setUpAppsFlowBox(nil, phrase) appFlowBox = setUpAppsFlowBox(nil, phrase)
if len(phrase) > 2 { // search files
if !*noFS && len(phrase) > 2 {
if fileSearchResultFlowBox != nil { if fileSearchResultFlowBox != nil {
fileSearchResultFlowBox.Destroy() fileSearchResultFlowBox.Destroy()
} }
fileSearchResultFlowBox = setUpFileSearchResultContainer() fileSearchResultFlowBox = setUpFileSearchResultContainer()
for key := range userDirsMap { for key := range userDirsMap {
if key != "home" { if key != "home" {
fileSearchResults = nil fileSearchResults = nil
@@ -332,17 +341,25 @@ func setUpSearchEntry() *gtk.SearchEntry {
statusLabel.SetText("0 results") statusLabel.SetText("0 results")
} }
} else { } else {
// search phrase too short
if fileSearchResultFlowBox != nil { if fileSearchResultFlowBox != nil {
fileSearchResultFlowBox.Destroy() fileSearchResultFlowBox.Destroy()
} }
fileSearchResultWrapper.Hide() if fileSearchResultWrapper != nil {
fileSearchResultWrapper.Hide()
}
} }
} else { } else {
// clear search results
appFlowBox = setUpAppsFlowBox(nil, "")
if fileSearchResultFlowBox != nil { if fileSearchResultFlowBox != nil {
fileSearchResultFlowBox.Destroy() fileSearchResultFlowBox.Destroy()
} }
appFlowBox = setUpAppsFlowBox(nil, "")
fileSearchResultWrapper.Hide() if fileSearchResultWrapper != nil {
fileSearchResultWrapper.Hide()
}
} }
}) })
searchEntry.Connect("focus-in-event", func() { searchEntry.Connect("focus-in-event", func() {