Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac486a32c | ||
|
|
875e7cac4a | ||
|
|
2960603462 | ||
|
|
f9e9608dfc | ||
|
|
83caef28f6 | ||
|
|
d732e9e070 | ||
|
|
033a4d59ae | ||
|
|
97be33094f | ||
|
|
e5246a13cd | ||
|
|
897d693d62 | ||
|
|
955429d809 | ||
|
|
4bf82d9a98 | ||
|
|
eb358b24cd | ||
|
|
27718a9f1b | ||
|
|
b4f0f70f56 | ||
|
|
f54be1c13a | ||
|
|
cd6e544adc | ||
|
|
f2bda0e496 | ||
|
|
fd8b9ba128 | ||
|
|
94aba90f3d | ||
|
|
fa9d9c3425 | ||
|
|
4711c36c56 |
1
Makefile
1
Makefile
@@ -16,6 +16,7 @@ install:
|
||||
cp bin/nwg-drawer /usr/bin
|
||||
|
||||
uninstall:
|
||||
rm -r /usr/share/nwg-drawer
|
||||
rm /usr/bin/nwg-drawer
|
||||
|
||||
run:
|
||||
|
||||
@@ -18,6 +18,8 @@ and `nwggrid`.
|
||||
|
||||
[more screenshots](https://scrot.cloud/album/nwg-drawer.Bogd) | [see on YouTube](https://youtu.be/iIgxJQhCQf0)
|
||||
|
||||
[](https://repology.org/project/nwg-drawer/versions)
|
||||
|
||||
## Installation
|
||||
|
||||
### Dependencies
|
||||
@@ -62,7 +64,7 @@ Usage of nwg-drawer:
|
||||
-lang string
|
||||
force lang, e.g. "en", "pl"
|
||||
-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
|
||||
use OVerLay layer
|
||||
-s string
|
||||
|
||||
BIN
bin/nwg-drawer
BIN
bin/nwg-drawer
Binary file not shown.
@@ -3,11 +3,12 @@ window {
|
||||
color: #eeeeee
|
||||
}
|
||||
|
||||
/* search entry */
|
||||
entry {
|
||||
background-color: rgba (0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
button {
|
||||
button, image {
|
||||
background: none;
|
||||
border: none
|
||||
}
|
||||
@@ -16,6 +17,11 @@ button:hover {
|
||||
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 {
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px dotted gray
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
||||
module github.com/nwg-piotr/nwg-menu
|
||||
module github.com/nwg-piotr/nwg-drawer
|
||||
|
||||
go 1.16
|
||||
|
||||
|
||||
52
main.go
52
main.go
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
)
|
||||
|
||||
const version = "0.1.0"
|
||||
const version = "0.1.5"
|
||||
|
||||
var (
|
||||
appDirs []string
|
||||
@@ -90,6 +90,7 @@ var (
|
||||
fileSearchResultWrapper *gtk.Box
|
||||
pinnedFlowBox *gtk.FlowBox
|
||||
pinnedFlowBoxWrapper *gtk.Box
|
||||
categoriesWrapper *gtk.Box
|
||||
catButtons []*gtk.Button
|
||||
statusLabel *gtk.Label
|
||||
status string
|
||||
@@ -98,7 +99,7 @@ var (
|
||||
|
||||
// Flags
|
||||
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 overlay = flag.Bool("ovl", false, "use OVerLay layer")
|
||||
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 term = flag.String("term", "alacritty", "Terminal emulator")
|
||||
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() {
|
||||
timeStart := time.Now()
|
||||
@@ -172,6 +175,7 @@ func main() {
|
||||
if err != nil {
|
||||
pinned = nil
|
||||
}
|
||||
println(fmt.Sprintf("Found %v pinned items", len(pinned)))
|
||||
|
||||
cssFile := filepath.Join(configDirectory, *cssFileName)
|
||||
|
||||
@@ -204,6 +208,7 @@ func main() {
|
||||
log.Fatal("Unable to create window:", err)
|
||||
}
|
||||
|
||||
if wayland() {
|
||||
layershell.InitForWindow(win)
|
||||
|
||||
var output2mon map[string]*gdk.Monitor
|
||||
@@ -232,11 +237,21 @@ func main() {
|
||||
}
|
||||
|
||||
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
|
||||
}
|
||||
|
||||
win.Connect("destroy", func() {
|
||||
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 {
|
||||
key := &gdk.EventKey{Event: event}
|
||||
switch key.KeyVal() {
|
||||
@@ -251,7 +266,6 @@ func main() {
|
||||
return false
|
||||
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:
|
||||
//searchEntry.SetText("")
|
||||
return false
|
||||
|
||||
default:
|
||||
@@ -274,6 +288,17 @@ func main() {
|
||||
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
|
||||
outerVBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
win.Add(outerVBox)
|
||||
@@ -285,10 +310,12 @@ func main() {
|
||||
searchEntry.SetMaxWidthChars(30)
|
||||
searchBoxWrapper.PackStart(searchEntry, true, false, 0)
|
||||
|
||||
categoriesWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
if !*noCats {
|
||||
categoriesWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
categoriesButtonBox := setUpCategoriesButtonBox()
|
||||
categoriesWrapper.PackStart(categoriesButtonBox, true, false, 0)
|
||||
outerVBox.PackStart(categoriesWrapper, false, false, 0)
|
||||
}
|
||||
|
||||
pinnedWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
outerVBox.PackStart(pinnedWrapper, false, false, 0)
|
||||
@@ -298,10 +325,19 @@ func main() {
|
||||
pinnedFlowBox = setUpPinnedFlowBox()
|
||||
|
||||
resultWindow, _ = gtk.ScrolledWindowNew(nil, nil)
|
||||
resultWindow.SetEvents(int(gdk.ALL_EVENTS_MASK))
|
||||
resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
resultWindow.Connect("enter-notify-event", func() {
|
||||
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)
|
||||
|
||||
resultsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||
@@ -328,11 +364,13 @@ func main() {
|
||||
resultsWrapper.PackStart(placeholder, true, true, 0)
|
||||
placeholder.SetSizeRequest(20, 20)
|
||||
|
||||
if !*noFS {
|
||||
wrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
fileSearchResultWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
fileSearchResultWrapper.SetProperty("name", "files-box")
|
||||
wrapper.PackStart(fileSearchResultWrapper, true, false, 0)
|
||||
resultsWrapper.PackEnd(wrapper, false, false, 10)
|
||||
}
|
||||
|
||||
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
outerVBox.PackStart(statusLineWrapper, false, false, 10)
|
||||
@@ -340,9 +378,13 @@ func main() {
|
||||
statusLineWrapper.PackStart(statusLabel, true, false, 0)
|
||||
|
||||
win.ShowAll()
|
||||
if !*noFS {
|
||||
fileSearchResultWrapper.SetSizeRequest(appFlowBox.GetAllocatedWidth(), 1)
|
||||
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.GetAllocatedHeight()*2)
|
||||
fileSearchResultWrapper.Hide()
|
||||
}
|
||||
if !*noCats {
|
||||
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.GetAllocatedHeight()*2)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
println(fmt.Sprintf("UI created in %v ms. Thank you for your patience.", t.Sub(timeStart).Milliseconds()))
|
||||
|
||||
16
tools.go
16
tools.go
@@ -20,6 +20,10 @@ import (
|
||||
"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.
|
||||
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() {
|
||||
path := filepath.Join(getDataHome(), "nwg-menu/desktop-directories")
|
||||
path := filepath.Join(getDataHome(), "nwg-drawer/desktop-directories")
|
||||
var other category
|
||||
|
||||
for _, cName := range categoryNames {
|
||||
@@ -579,17 +583,19 @@ func launch(command string, terminal bool) {
|
||||
envVarsNum := strings.Count(command, "=")
|
||||
var envVars []string
|
||||
|
||||
cmdIdx := 0
|
||||
lastEnvVarIdx := 0
|
||||
cmdIdx := -1
|
||||
|
||||
if envVarsNum > 0 {
|
||||
for idx, item := range elements {
|
||||
if strings.Contains(item, "=") {
|
||||
lastEnvVarIdx = idx
|
||||
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:]...)
|
||||
|
||||
@@ -17,7 +17,7 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
|
||||
flowBox, _ := gtk.FlowBoxNew()
|
||||
if uint(len(pinned)) >= *columnsNumber {
|
||||
flowBox.SetMaxChildrenPerLine(*columnsNumber)
|
||||
} else {
|
||||
} else if len(pinned) > 0 {
|
||||
flowBox.SetMaxChildrenPerLine(uint(len(pinned)))
|
||||
}
|
||||
|
||||
@@ -78,16 +78,17 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
|
||||
})
|
||||
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() {
|
||||
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()
|
||||
|
||||
return flowBox
|
||||
@@ -113,6 +114,7 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
|
||||
hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
|
||||
eventBox.Add(hBox)
|
||||
button, _ := gtk.ButtonNewWithLabel("All")
|
||||
button.SetProperty("name", "category-button")
|
||||
button.Connect("clicked", func(item *gtk.Button) {
|
||||
searchEntry.SetText("")
|
||||
appFlowBox = setUpAppsFlowBox(nil, "")
|
||||
@@ -126,8 +128,11 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
|
||||
for _, cat := range categories {
|
||||
if isSupposedToShowUp(cat.Name) {
|
||||
button, _ = gtk.ButtonNewFromIconName(cat.Icon, gtk.ICON_SIZE_MENU)
|
||||
button.SetProperty("name", "category-button")
|
||||
catButtons = append(catButtons, button)
|
||||
button.SetLabel(cat.DisplayName)
|
||||
// fix #8
|
||||
button.SetAlwaysShowImage(true)
|
||||
hBox.PackStart(button, false, false, 0)
|
||||
name := cat.Name
|
||||
b := *button
|
||||
@@ -314,13 +319,17 @@ func setUpSearchEntry() *gtk.SearchEntry {
|
||||
phrase, _ = searchEntry.GetText()
|
||||
if len(phrase) > 0 {
|
||||
|
||||
// search apps
|
||||
appFlowBox = setUpAppsFlowBox(nil, phrase)
|
||||
|
||||
if len(phrase) > 2 {
|
||||
// search files
|
||||
if !*noFS && len(phrase) > 2 {
|
||||
if fileSearchResultFlowBox != nil {
|
||||
fileSearchResultFlowBox.Destroy()
|
||||
}
|
||||
|
||||
fileSearchResultFlowBox = setUpFileSearchResultContainer()
|
||||
|
||||
for key := range userDirsMap {
|
||||
if key != "home" {
|
||||
fileSearchResults = nil
|
||||
@@ -332,18 +341,26 @@ func setUpSearchEntry() *gtk.SearchEntry {
|
||||
statusLabel.SetText("0 results")
|
||||
}
|
||||
} else {
|
||||
// search phrase too short
|
||||
if fileSearchResultFlowBox != nil {
|
||||
fileSearchResultFlowBox.Destroy()
|
||||
}
|
||||
if fileSearchResultWrapper != nil {
|
||||
fileSearchResultWrapper.Hide()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// clear search results
|
||||
appFlowBox = setUpAppsFlowBox(nil, "")
|
||||
|
||||
if fileSearchResultFlowBox != nil {
|
||||
fileSearchResultFlowBox.Destroy()
|
||||
}
|
||||
appFlowBox = setUpAppsFlowBox(nil, "")
|
||||
|
||||
if fileSearchResultWrapper != nil {
|
||||
fileSearchResultWrapper.Hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
searchEntry.Connect("focus-in-event", func() {
|
||||
searchEntry.SetText("")
|
||||
|
||||
Reference in New Issue
Block a user