37 Commits

Author SHA1 Message Date
piotr
f3b3635bd8 trim comments > 120 chars #61 2022-05-08 01:43:16 +02:00
nwg-piotr
bb84a2fc46 remove accidental binary 2022-04-13 11:36:19 +02:00
Piotr Miller
93587a47db Merge pull request #60 from nwg-piotr/dark
prefer dark theme
2022-04-13 11:22:15 +02:00
nwg-piotr
afe99b42f0 prefer dark theme 2022-04-13 11:14:50 +02:00
Piotr Miller
e052a86b12 Merge pull request #58 from jovanlanik/main
Support for changing GTK theme
2022-03-18 02:06:06 +01:00
Jovan Lanik
45cb286296 Add support for changing GTK theme 2022-03-13 21:58:42 +01:00
piotr
d050598086 bump to 0.2.8 2022-02-15 01:19:30 +01:00
Piotr Miller
42ddd815f4 Merge pull request #52 from snehrbass/main
Add flags to set window margins
2022-02-15 01:02:48 +01:00
Ste_ph_en
8b1dfb4421 Add flags to set margins 2022-02-14 18:57:57 -05:00
Piotr Miller
d572c1125d Merge pull request #54 from Raffy23/51-fix-zombie-processes
Collect signals of child processes
2022-02-15 00:42:56 +01:00
Piotr Miller
6b055bb48a Merge pull request #53 from Raffy23/50-fix-high-cpu-load
Fix high CPU load
2022-02-15 00:37:54 +01:00
Raphael Ludwig
165b925da2 Fix regression that prevented pinned items to update while window is open 2022-02-14 20:59:48 +01:00
Raphael Ludwig
058f8684aa Use a channel and a goroutine instead of busy polling a boolean flag to reduce cpu consumption 2022-02-13 23:21:21 +01:00
Raphael Ludwig
f4b51b0706 Collect signals of child processes
Add logging if starting of a process was not successful
2022-02-13 23:06:21 +01:00
Piotr Miller
b781f58097 Update README.md 2022-02-04 11:45:17 +01:00
Piotr Miller
68137935a9 Update README.md 2022-02-02 14:47:28 +01:00
piotr
98efb36614 bump version 2022-01-15 23:58:23 +01:00
Piotr Miller
59533536b1 Merge pull request #48 from nightly-brew/detach-from-process-group
Use Setsid when spawning programs
2022-01-15 03:58:52 +01:00
nightly-brew
07821f39b7 Use Setsid when starting the selected program to make sure it's not killed by signals sent to the drawer's process group. 2022-01-15 02:36:07 +01:00
piotr
4ea160e524 update README 2022-01-13 23:58:10 +01:00
Piotr Miller
8fb4209a97 Merge pull request #47 from nwg-piotr/not-wayland
remove bin
2022-01-13 23:46:57 +01:00
piotr
c85c364ba5 ignore bin 2022-01-13 23:42:42 +01:00
piotr
8239254485 remove bin #40 2022-01-13 23:42:31 +01:00
Piotr Miller
7681055a23 Merge pull request #46 from nwg-piotr/fix45
check if fileSearchResultWrapper exists #45
2022-01-12 11:17:56 +01:00
piotr
15c9029935 check if fileSearchResultWrapper exists #45 2022-01-12 11:06:05 +01:00
Piotr Miller
f67f1a9950 Merge pull request #43 from nwg-piotr/fix-pinned
create empty pinned file if not found on startup (and some other minor issues)
2022-01-11 14:41:39 +01:00
piotr
2c63e256ba update readme 2022-01-11 14:36:56 +01:00
piotr
92095d5b97 hide fileSearchResultWrapper initially 2022-01-11 12:31:47 +01:00
piotr
a6977d9444 create pinned file if not found 2022-01-11 11:45:05 +01:00
Piotr Miller
378aa33cf3 Merge pull request #42 from nwg-piotr/issue36
Close on RMB only #36
2022-01-10 23:55:16 +01:00
piotr
9d6d572f72 close on LMB only #36 2022-01-10 22:04:58 +01:00
piotr
8fdb174643 hot fix to #41 2022-01-09 23:20:48 +01:00
piotr
0810250b3a default temt -> foot; consume Esc key event 2022-01-09 14:30:59 +01:00
piotr
6e76f49729 fix #38 @nightly-brew 2022-01-09 12:56:51 +01:00
Piotr Miller
94c94b8b1e Merge pull request #35 from nwg-piotr/term
support for the foot terminal
2021-11-29 00:47:33 +01:00
piotr
3d6f3e6e3c support foot terminal 2021-11-21 21:45:10 +01:00
piotr
2e305e6e52 bump to 0.2.1 2021-10-01 00:40:27 +02:00
7 changed files with 143 additions and 53 deletions

4
.gitignore vendored
View File

@@ -11,5 +11,9 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Binaries built with make
bin
nwg-drawer
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/

View File

@@ -15,16 +15,18 @@ You may pin applications by right-clicking them. Pinned items will appear above
a pinned item to unpin it. The pinned items cache is shared with [nwg-menu](https://github.com/nwg-piotr/nwg-menu) a pinned item to unpin it. The pinned items cache is shared with [nwg-menu](https://github.com/nwg-piotr/nwg-menu)
and `nwggrid`. and `nwggrid`.
![screenshot-01.png](https://scrot.cloud/images/2021/05/30/screenshot-01.png) ![screenshot.png](https://raw.githubusercontent.com/nwg-piotr/nwg-shell-resources/master/images/nwg-shell/nwg-drawer.png)
[more screenshots](https://scrot.cloud/album/nwg-drawer.Bogd) | [see on YouTube](https://youtu.be/iIgxJQhCQf0) [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) [![Packaging status](https://repology.org/badge/vertical-allrepos/nwg-drawer.svg)](https://repology.org/project/nwg-drawer/versions)
To close the window w/o running a program, you may use `Esc` key, or right-click the window next to the icons.
## v0.2.x note ## v0.2.x note
1. Placing config files in the nwg-panel config directory was a mistake, sorry. The 0.2.0 version migrates them to `~/.config/nwg-drawer`. 1. Placing config files in the nwg-panel config directory was a mistake, sorry. The 0.2.0 version migrates them to `~/.config/nwg-drawer`.
2. From now on you may run the program residently, which should speed it up. See "Running" below. 2. From now on you may run the program residently, which should speed it up (but also occupy some resources!). See "Running" below.
## Installation ## Installation
@@ -38,7 +40,7 @@ and `nwggrid`.
Optional (recommended): Optional (recommended):
- thunar - thunar
- alacritty - foot
You may use another file manager and terminal emulator (see command line arguments), but mentioned above have been You may use another file manager and terminal emulator (see command line arguments), but mentioned above have been
confirmed to work well with the program. Also see **Files** below. confirmed to work well with the program. Also see **Files** below.
@@ -50,9 +52,6 @@ confirmed to work well with the program. Also see **Files** below.
3. `make build` 3. `make build`
4. `sudo make install` 4. `sudo make install`
Building the gotk3 library takes quite a lot of time. If your machine is x86_64, you may skip steps 2-3, and
install the provided binary by executing step 4.
## Command line arguments ## Command line arguments
```text ```text
@@ -67,6 +66,8 @@ Usage of nwg-drawer:
File Search result COLumns (default 2) File Search result COLumns (default 2)
-fslen int -fslen int
File Search name LENgth Limit (default 80) File Search name LENgth Limit (default 80)
-g string
GTK theme name, eg. "Adwaita-dark"
-is int -is int
Icon Size (default 64) Icon Size (default 64)
-lang string -lang string
@@ -133,7 +134,7 @@ Edit `~/.config/nwg-drawer/drawer.css` to your taste.
When the search phrase is at least 3 characters long, your XDG user directories are being searched. When the search phrase is at least 3 characters long, your XDG user directories are being searched.
![screenshot-03.png](https://scrot.cloud/images/2021/05/30/screenshot-03.png) ![screenshot-02.png](https://raw.githubusercontent.com/nwg-piotr/nwg-shell/main/images/nwg-drawer/search.png)
Use the **left mouse button** to open a file with the `xdg-open` command. As configuring file associations for it is Use the **left mouse button** to open a file with the `xdg-open` command. As configuring file associations for it is
PITA, you may override them, by creating the `~/.config/nwg-panel/preferred-apps.json` file with your own definitions. PITA, you may override them, by creating the `~/.config/nwg-panel/preferred-apps.json` file with your own definitions.

Binary file not shown.

99
main.go
View File

@@ -21,7 +21,7 @@ import (
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
) )
const version = "0.2.0" const version = "0.3.0"
var ( var (
appDirs []string appDirs []string
@@ -100,9 +100,8 @@ var (
statusLabel *gtk.Label statusLabel *gtk.Label
status string status string
ignore string ignore string
showWindowTrigger bool
desktopTrigger bool desktopTrigger bool
pinnedTrigger bool pinnedItemsChanged chan interface{} = make(chan interface{}, 1)
) )
func defaultStringIfBlank(s, fallback string) string { func defaultStringIfBlank(s, fallback string) string {
@@ -120,13 +119,18 @@ var cssFileName = flag.String("s", "drawer.css", "Styling: css file name")
var targetOutput = flag.String("o", "", "name of the Output to display the drawer 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 gtkTheme = flag.String("g", "", "GTK theme name")
var iconSize = flag.Int("is", 64, "Icon Size") var iconSize = flag.Int("is", 64, "Icon Size")
var marginTop = flag.Int("mt", 0, "Margin Top")
var marginLeft = flag.Int("ml", 0, "Margin Left")
var marginRight = flag.Int("mr", 0, "Margin Right")
var marginBottom = flag.Int("mb", 0, "Margin Bottom")
var fsColumns = flag.Uint("fscol", 2, "File Search result COLumns") var fsColumns = flag.Uint("fscol", 2, "File Search result COLumns")
var columnsNumber = flag.Uint("c", 6, "number of Columns") var columnsNumber = flag.Uint("c", 6, "number of Columns")
var itemSpacing = flag.Uint("spacing", 20, "icon spacing") var itemSpacing = flag.Uint("spacing", 20, "icon spacing")
var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"") 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", defaultStringIfBlank(os.Getenv("TERM"), "alacritty"), "Terminal emulator") var term = flag.String("term", defaultStringIfBlank(os.Getenv("TERM"), "foot"), "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 noCats = flag.Bool("nocats", false, "Disable filtering by category")
var noFS = flag.Bool("nofs", false, "Disable file search") var noFS = flag.Bool("nofs", false, "Disable file search")
@@ -148,6 +152,7 @@ func main() {
// Gentle SIGTERM handler thanks to reiki4040 https://gist.github.com/reiki4040/be3705f307d3cd136e85 // Gentle SIGTERM handler thanks to reiki4040 https://gist.github.com/reiki4040/be3705f307d3cd136e85
// v0.2: we also need to support SIGUSR from now on // v0.2: we also need to support SIGUSR from now on
showWindowChannel := make(chan interface{}, 1)
signalChan := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGUSR1) signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGUSR1)
@@ -164,7 +169,7 @@ func main() {
// let's just set e helper variable here. We'll be checking it with glib.TimeoutAdd. // let's just set e helper variable here. We'll be checking it with glib.TimeoutAdd.
if !win.IsVisible() { if !win.IsVisible() {
log.Debug("SIGUSR1 received, showing the window") log.Debug("SIGUSR1 received, showing the window")
showWindowTrigger = true showWindowChannel <- struct{}{}
} else { } else {
log.Debug("SIGUSR1 received, hiding the window") log.Debug("SIGUSR1 received, hiding the window")
restoreStateAndHide() restoreStateAndHide()
@@ -251,6 +256,7 @@ func main() {
pinned, err = loadTextFile(pinnedFile) pinned, err = loadTextFile(pinnedFile)
if err != nil { if err != nil {
pinned = nil pinned = nil
savePinned()
} }
log.Info(fmt.Sprintf("Found %v pinned items", len(pinned))) log.Info(fmt.Sprintf("Found %v pinned items", len(pinned)))
@@ -297,6 +303,19 @@ func main() {
// USER INTERFACE // USER INTERFACE
gtk.Init(nil) gtk.Init(nil)
settings, _ := gtk.SettingsGetDefault()
if *gtkTheme != "" {
err = settings.SetProperty("gtk-theme-name", *gtkTheme)
if err != nil {
log.Error("Unable to set theme:", err)
} else {
log.Infof("User demanded theme: %s", *gtkTheme)
}
} else {
settings.SetProperty("gtk-application-prefer-dark-theme", true)
log.Info("Preferring dark theme variants")
}
cssProvider, _ := gtk.CssProviderNew() cssProvider, _ := gtk.CssProviderNew()
err = cssProvider.LoadFromPath(*cssFileName) err = cssProvider.LoadFromPath(*cssFileName)
@@ -342,6 +361,11 @@ func main() {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP) layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP)
} }
layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_TOP, *marginTop)
layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_LEFT, *marginLeft)
layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_RIGHT, *marginRight)
layershell.SetMargin(win, layershell.LAYER_SHELL_EDGE_BOTTOM, *marginBottom)
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE) layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
} }
@@ -349,10 +373,9 @@ func main() {
gtk.MainQuit() gtk.MainQuit()
}) })
win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool { win.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) bool {
key := &gdk.EventKey{Event: event} key := &gdk.EventKey{Event: event}
switch key.KeyVal() { if key.KeyVal() == gdk.KEY_Escape {
case gdk.KEY_Escape:
s, _ := searchEntry.GetText() s, _ := searchEntry.GetText()
if s != "" { if s != "" {
searchEntry.GrabFocus() searchEntry.GrabFocus()
@@ -364,7 +387,14 @@ func main() {
restoreStateAndHide() restoreStateAndHide()
} }
} }
return true
}
return false return false
})
win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool {
key := &gdk.EventKey{Event: event}
switch key.KeyVal() {
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:
return false return false
@@ -419,7 +449,7 @@ func main() {
resultWindow.Connect("button-release-event", func(sw *gtk.ScrolledWindow, e *gdk.Event) bool { resultWindow.Connect("button-release-event", func(sw *gtk.ScrolledWindow, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e) btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 || btnEvent.Button() == 3 { if btnEvent.Button() == 3 {
if !*resident { if !*resident {
gtk.MainQuit() gtk.MainQuit()
} else { } else {
@@ -485,20 +515,15 @@ func main() {
log.Info(fmt.Sprintf("UI created in %v ms. Thank you for your patience.", t.Sub(timeStart).Milliseconds())) log.Info(fmt.Sprintf("UI created in %v ms. Thank you for your patience.", t.Sub(timeStart).Milliseconds()))
// Check if showing the window has been requested (SIGUSR1) // Check if showing the window has been requested (SIGUSR1)
glib.TimeoutAdd(uint(1), func() bool { go func() {
if showWindowTrigger && win != nil && !win.IsVisible() { for {
win.ShowAll() select {
// focus 1st element case <-showWindowChannel:
b := appFlowBox.GetChildAtIndex(0) log.Debug("Showing window")
if b != nil { glib.TimeoutAdd(0, func() bool {
button, err := b.GetChild() if win != nil && !win.IsVisible() {
if err == nil {
button.ToWidget().GrabFocus()
}
}
}
showWindowTrigger = false
// Refresh files before displaying the root window
// some .desktop file changed // some .desktop file changed
if desktopTrigger { if desktopTrigger {
log.Debug(".desktop file changed") log.Debug(".desktop file changed")
@@ -508,15 +533,35 @@ func main() {
desktopTrigger = false desktopTrigger = false
} }
// pinned file changed // Show window and focus the search box
if pinnedTrigger { win.ShowAll()
if fileSearchResultWrapper != nil {
fileSearchResultWrapper.Hide()
}
// focus 1st element
b := appFlowBox.GetChildAtIndex(0)
if b != nil {
button, err := b.GetChild()
if err == nil {
button.ToWidget().GrabFocus()
}
}
}
return false
})
case <-pinnedItemsChanged:
glib.TimeoutAdd(0, func() bool {
log.Debug("pinned file changed") log.Debug("pinned file changed")
pinnedTrigger = false
pinned, _ = loadTextFile(pinnedFile) pinned, _ = loadTextFile(pinnedFile)
pinnedFlowBox = setUpPinnedFlowBox() pinnedFlowBox = setUpPinnedFlowBox()
}
return true return false
}) })
}
}
}()
go watchFiles() go watchFiles()

View File

@@ -15,6 +15,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"syscall"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -559,7 +560,13 @@ func launch(command string, terminal bool) {
cmd := exec.Command(elements[cmdIdx], elements[1+cmdIdx:]...) cmd := exec.Command(elements[cmdIdx], elements[1+cmdIdx:]...)
if terminal { if terminal {
args := []string{"-e", elements[cmdIdx]} var args []string
if *term != "foot" {
args = []string{"-e", elements[cmdIdx]}
} else {
args = []string{elements[cmdIdx]}
}
cmd = exec.Command(*term, args...) cmd = exec.Command(*term, args...)
} }
@@ -572,7 +579,19 @@ func launch(command string, terminal bool) {
msg := fmt.Sprintf("env vars: %s; command: '%s'; args: %s\n", envVars, elements[cmdIdx], elements[1+cmdIdx:]) msg := fmt.Sprintf("env vars: %s; command: '%s'; args: %s\n", envVars, elements[cmdIdx], elements[1+cmdIdx:])
log.Info(msg) log.Info(msg)
cmd.Start() cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
}
if cmd.Start() != nil {
log.Warn("Unable to launch terminal emulator!")
} else {
// Collect the exit code of the child process to prevent zombies
// if the drawer runs in resident mode
go func() {
_ = cmd.Wait()
}()
}
if *resident { if *resident {
restoreStateAndHide() restoreStateAndHide()
@@ -598,7 +617,15 @@ func open(filePath string, xdgOpen bool) {
} }
log.Infof("Executing: %s", cmd) log.Infof("Executing: %s", cmd)
cmd.Start() if cmd.Start() != nil {
log.Warn("Unable to execute command!")
} else {
// Collect the exit code of the child process to prevent zombies
// if the drawer runs in resident mode
go func() {
_ = cmd.Wait()
}()
}
if *resident { if *resident {
restoreStateAndHide() restoreStateAndHide()

View File

@@ -77,6 +77,9 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
btn.Connect("enter-notify-event", func() { btn.Connect("enter-notify-event", func() {
statusLabel.SetText(entry.CommentLoc) statusLabel.SetText(entry.CommentLoc)
}) })
btn.Connect("focus-in-event", func() {
statusLabel.SetText(entry.CommentLoc)
})
flowBox.Add(btn) flowBox.Add(btn)
} }
pinnedFlowBoxWrapper.PackStart(flowBox, true, false, 0) pinnedFlowBoxWrapper.PackStart(flowBox, true, false, 0)
@@ -255,6 +258,11 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
exec := entry.Exec exec := entry.Exec
terminal := entry.Terminal terminal := entry.Terminal
desc := entry.CommentLoc desc := entry.CommentLoc
if len(desc) > 120 {
r := []rune(desc)
desc = string(r[:117])
desc = fmt.Sprintf("%s…", desc)
}
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool { button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e) btnEvent := gdk.EventButtonNewFromEvent(e)
if btnEvent.Button() == 1 { if btnEvent.Button() == 1 {
@@ -272,6 +280,9 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
button.Connect("enter-notify-event", func() { button.Connect("enter-notify-event", func() {
statusLabel.SetText(desc) statusLabel.SetText(desc)
}) })
button.Connect("focus-in-event", func() {
statusLabel.SetText(desc)
})
return button return button
} }

View File

@@ -41,7 +41,9 @@ func watchFiles() {
event.Op.String() == "RENAME") { event.Op.String() == "RENAME") {
desktopTrigger = true desktopTrigger = true
} else if event.Name == pinnedFile { } else if event.Name == pinnedFile {
pinnedTrigger = true // TODO: This can be used to propagate information about the changed file to the
// GUI to avoid recreating everything
pinnedItemsChanged <- struct{}{}
} }
case err := <-watcher.Errors: case err := <-watcher.Errors: