Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3b3635bd8 | ||
|
|
bb84a2fc46 | ||
|
|
93587a47db | ||
|
|
afe99b42f0 | ||
|
|
e052a86b12 | ||
|
|
45cb286296 | ||
|
|
d050598086 | ||
|
|
42ddd815f4 | ||
|
|
8b1dfb4421 | ||
|
|
d572c1125d | ||
|
|
6b055bb48a | ||
|
|
165b925da2 | ||
|
|
058f8684aa | ||
|
|
f4b51b0706 | ||
|
|
b781f58097 | ||
|
|
68137935a9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@
|
||||
|
||||
# Binaries built with make
|
||||
bin
|
||||
nwg-drawer
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
10
README.md
10
README.md
@@ -15,9 +15,9 @@ 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)
|
||||
and `nwggrid`.
|
||||
|
||||

|
||||

|
||||
|
||||
[more screenshots](https://scrot.cloud/album/nwg-drawer.Bogd) | [see on YouTube](https://youtu.be/iIgxJQhCQf0)
|
||||
[see on YouTube](https://youtu.be/iIgxJQhCQf0)
|
||||
|
||||
[](https://repology.org/project/nwg-drawer/versions)
|
||||
|
||||
@@ -66,6 +66,8 @@ Usage of nwg-drawer:
|
||||
File Search result COLumns (default 2)
|
||||
-fslen int
|
||||
File Search name LENgth Limit (default 80)
|
||||
-g string
|
||||
GTK theme name, eg. "Adwaita-dark"
|
||||
-is int
|
||||
Icon Size (default 64)
|
||||
-lang string
|
||||
@@ -132,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.
|
||||
|
||||

|
||||

|
||||
|
||||
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.
|
||||
@@ -179,4 +181,4 @@ Copyright (c) 2015-2018 gotk3 contributors
|
||||
- [go-sway](https://github.com/joshuarubin/go-sway) Copyright (c) 2019 Joshua Rubin
|
||||
- [go-singleinstance](github.com/allan-simon/go-singleinstance) Copyright (c) 2015 Allan Simon
|
||||
- [logrus](https://github.com/sirupsen/logrus) Copyright (c) 2014 Simon Eskildsen
|
||||
- [fsnotify](https://github.com/fsnotify/fsnotify) Copyright (c) 2012-2019 fsnotify Authors
|
||||
- [fsnotify](https://github.com/fsnotify/fsnotify) Copyright (c) 2012-2019 fsnotify Authors
|
||||
|
||||
109
main.go
109
main.go
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
)
|
||||
|
||||
const version = "0.2.7"
|
||||
const version = "0.3.0"
|
||||
|
||||
var (
|
||||
appDirs []string
|
||||
@@ -100,9 +100,8 @@ var (
|
||||
statusLabel *gtk.Label
|
||||
status string
|
||||
ignore string
|
||||
showWindowTrigger bool
|
||||
desktopTrigger bool
|
||||
pinnedTrigger bool
|
||||
pinnedItemsChanged chan interface{} = make(chan interface{}, 1)
|
||||
)
|
||||
|
||||
func defaultStringIfBlank(s, fallback string) string {
|
||||
@@ -120,7 +119,12 @@ 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 displayVersion = flag.Bool("v", false, "display Version information")
|
||||
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 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 columnsNumber = flag.Uint("c", 6, "number of Columns")
|
||||
var itemSpacing = flag.Uint("spacing", 20, "icon spacing")
|
||||
@@ -148,6 +152,7 @@ func main() {
|
||||
|
||||
// Gentle SIGTERM handler thanks to reiki4040 https://gist.github.com/reiki4040/be3705f307d3cd136e85
|
||||
// v0.2: we also need to support SIGUSR from now on
|
||||
showWindowChannel := make(chan interface{}, 1)
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
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.
|
||||
if !win.IsVisible() {
|
||||
log.Debug("SIGUSR1 received, showing the window")
|
||||
showWindowTrigger = true
|
||||
showWindowChannel <- struct{}{}
|
||||
} else {
|
||||
log.Debug("SIGUSR1 received, hiding the window")
|
||||
restoreStateAndHide()
|
||||
@@ -298,6 +303,19 @@ func main() {
|
||||
// USER INTERFACE
|
||||
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()
|
||||
|
||||
err = cssProvider.LoadFromPath(*cssFileName)
|
||||
@@ -343,6 +361,11 @@ func main() {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -492,41 +515,53 @@ func main() {
|
||||
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)
|
||||
glib.TimeoutAdd(uint(1), func() bool {
|
||||
if showWindowTrigger && win != nil && !win.IsVisible() {
|
||||
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()
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-showWindowChannel:
|
||||
log.Debug("Showing window")
|
||||
glib.TimeoutAdd(0, func() bool {
|
||||
if win != nil && !win.IsVisible() {
|
||||
|
||||
// Refresh files before displaying the root window
|
||||
// some .desktop file changed
|
||||
if desktopTrigger {
|
||||
log.Debug(".desktop file changed")
|
||||
desktopFiles = listDesktopFiles()
|
||||
status = parseDesktopFiles(desktopFiles)
|
||||
appFlowBox = setUpAppsFlowBox(nil, "")
|
||||
desktopTrigger = false
|
||||
}
|
||||
|
||||
// Show window and focus the search box
|
||||
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")
|
||||
pinned, _ = loadTextFile(pinnedFile)
|
||||
pinnedFlowBox = setUpPinnedFlowBox()
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
showWindowTrigger = false
|
||||
|
||||
// some .desktop file changed
|
||||
if desktopTrigger {
|
||||
log.Debug(".desktop file changed")
|
||||
desktopFiles = listDesktopFiles()
|
||||
status = parseDesktopFiles(desktopFiles)
|
||||
appFlowBox = setUpAppsFlowBox(nil, "")
|
||||
desktopTrigger = false
|
||||
}
|
||||
|
||||
// pinned file changed
|
||||
if pinnedTrigger {
|
||||
log.Debug("pinned file changed")
|
||||
pinnedTrigger = false
|
||||
pinned, _ = loadTextFile(pinnedFile)
|
||||
pinnedFlowBox = setUpPinnedFlowBox()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}()
|
||||
|
||||
go watchFiles()
|
||||
|
||||
|
||||
22
tools.go
22
tools.go
@@ -579,11 +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:])
|
||||
log.Info(msg)
|
||||
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setsid: true,
|
||||
}
|
||||
|
||||
cmd.Start()
|
||||
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 {
|
||||
restoreStateAndHide()
|
||||
@@ -609,7 +617,15 @@ func open(filePath string, xdgOpen bool) {
|
||||
}
|
||||
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 {
|
||||
restoreStateAndHide()
|
||||
|
||||
@@ -258,6 +258,11 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
|
||||
exec := entry.Exec
|
||||
terminal := entry.Terminal
|
||||
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 {
|
||||
btnEvent := gdk.EventButtonNewFromEvent(e)
|
||||
if btnEvent.Button() == 1 {
|
||||
|
||||
@@ -41,7 +41,9 @@ func watchFiles() {
|
||||
event.Op.String() == "RENAME") {
|
||||
desktopTrigger = true
|
||||
} 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:
|
||||
|
||||
Reference in New Issue
Block a user