32 Commits
v0.5.0 ... main

Author SHA1 Message Date
85320215be remove search bar 2025-08-06 03:41:42 +02:00
f35903bfb7 allow for CUSTOM_DATA_DIRS variable 2025-08-05 03:58:33 +02:00
c8f8e27a65 only serach in custom director.
hide&wait for the program to exit.
2024-12-20 04:17:36 +01:00
Piotr Miller
4110554466 Merge pull request #141 from msmafra/patch-1
Update main.go
2024-12-14 00:56:03 +01:00
Marcelo dos Santos Mafra
7d1242d6ce Update main.go
Very little misspell
2024-12-13 16:37:34 -03:00
Piotr Miller
953239bb5c Merge pull request #140 from nwg-piotr/diamond
Migrate to diamondburned gtk3 bindings
2024-12-01 01:16:41 +01:00
piotr
2136a6165c update comments 2024-12-01 01:13:10 +01:00
piotr
a752be7434 update README.md 2024-12-01 01:10:23 +01:00
piotr
517054f013 update get 2024-11-29 00:34:06 +01:00
piotr
8b0d4f943b sort desktopEntries case insensitive 2024-11-29 00:21:20 +01:00
piotr
5c839eb576 userDirButton -> connect "activate" signal 2024-11-28 03:13:03 +01:00
piotr
68f42b7569 disallow userDirButton parent selection 2024-11-28 00:46:10 +01:00
piotr
725ce44058 fix gtk-theme-name detection 2024-11-27 03:20:25 +01:00
piotr
2d755f609d migrate to diamondburned GTK bindings 2024-11-27 03:05:46 +01:00
Piotr Miller
423adcd096 Merge pull request #138 from Vescrity/main
Fix env XDG_DATA_DIRS makes catagory disappear
2024-11-21 00:20:36 +01:00
vescrity
1b61a42b10 Fix env XDG_DATA_DIRS makes catagory disappear 2024-11-19 19:44:09 +08:00
piotr
c0eb0965a8 update README.md 2024-10-26 00:58:25 +02:00
piotr
c940139fd9 add default styling for #math-result label #134 2024-10-26 00:43:34 +02:00
piotr
10b1b2a1e6 Merge remote-tracking branch 'origin/main' 2024-10-26 00:38:44 +02:00
piotr
b4b7d3486a add CSS ID to math result label #134 2024-10-26 00:38:20 +02:00
Piotr Miller
61a5595d84 Merge pull request #132 from msmafra/main
Update README.md
2024-10-15 11:27:46 +02:00
Marcelo dos Santos Mafra
cb44c7215f Update README.md
nwg-drawer seems to look in its own folder ~/.config/nwg-drawer for preferred-apps.json and excluded-dirs and not nwg-panel's folder
2024-10-11 17:59:30 -03:00
piotr
e7fafebf55 bump to 0.5.2 2024-10-09 02:12:19 +02:00
piotr
702cbb1c5c update golang.org/x/sys 2024-10-09 02:07:51 +02:00
piotr
7de2854041 set gtk-layer-shell namespace 2024-10-09 02:06:30 +02:00
Piotr Miller
be6b71673d Merge pull request #131 from nwg-piotr/calc
add a simple calculator in the search box
2024-10-03 01:20:01 +02:00
piotr
9c48c585b7 add rounded corners 2024-10-03 01:10:35 +02:00
piotr
3977c4fc7c go -> 1.23 2024-10-03 01:05:14 +02:00
piotr
46a526ced8 close result win on btn release 2024-10-03 01:04:53 +02:00
piotr
12da77fbfc update README.md 2024-10-03 00:48:23 +02:00
piotr
484c6ccb29 add comments 2024-10-03 00:33:54 +02:00
piotr
7aac5a4f52 add expression calculator 2024-09-30 22:09:56 +02:00
9 changed files with 396 additions and 302 deletions

View File

@@ -1,11 +1,11 @@
get:
go get github.com/gotk3/gotk3
go get github.com/gotk3/gotk3/gdk
go get github.com/gotk3/gotk3/glib
go get github.com/dlasky/gotk3-layershell/layershell
go get github.com/diamondburned/gotk4/pkg/gdk/v3
go get github.com/diamondburned/gotk4/pkg/glib/v2
go get github.com/diamondburned/gotk4/pkg/gtk/v3
go get github.com/diamondburned/gotk4-layer-shell/pkg/gtklayershell
go get github.com/joshuarubin/go-sway
go get github.com/allan-simon/go-singleinstance
go get "github.com/sirupsen/logrus"
go get github.com/sirupsen/logrus
go get github.com/fsnotify/fsnotify
build:

View File

@@ -172,7 +172,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.
PITA, you may override them, by creating the `~/.config/nwg-drawer/preferred-apps.json` file with your own definitions.
### Sample `preferred-apps.json` file content
@@ -199,21 +199,36 @@ I've noy yet tried other file managers.
### File search exclusions
You may want to exclude some paths inside your XDG user directories from searching. If so, define exclusions in the
`~/.config/nwg-panel/excluded-dirs` file, e.g. like this:
`~/.config/nwg-drawer/excluded-dirs` file, e.g. like this:
```text
# exclude all paths containing 'node_modules'
node_modules
```
### Calculations in the search box
If the search box is not empty, and you press Enter, the search box content will be evaluated as an arithmetic operation.
If the result is not an error, it will be displayed in a small window, and copied to the clipboard with wl-copy.
Press any key to close the window.
You may change the result label styling e.g. like this:
```css
/* math operation result label */
#math-label {
font-weight: bold;
font-size: 16px
}
```
## Credits
This program uses some great libraries:
- [gotk3](https://github.com/gotk3/gotk3) Copyright (c) 2013-2014 Conformal Systems LLC,
Copyright (c) 2015-2018 gotk3 contributors
- [gotk3-layershell](https://github.com/dlasky/gotk3-layershell) by [@dlasky](https://github.com/dlasky/gotk3-layershell/commits?author=dlasky) - many thanks for writing this software, and for patience with my requests!
- [gotk4](https://github.com/diamondburned/gotk4) by [diamondburned](https://github.com/diamondburned) released under [GNU Affero General Public License v3.0](https://github.com/diamondburned/gotk4/blob/4/LICENSE.md)
- [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
- [expr](https://github.com/expr-lang/expr) Copyright (c) 2018 Anton Medvedev

View File

@@ -1,5 +1,5 @@
window {
background-color: rgba (36, 47, 79, 0.95);
background-color: rgba(36, 47, 79, 0.95);
color: #eeeeee
}
@@ -32,3 +32,9 @@ button:hover {
border: 1px dotted gray;
border-radius: 15px
}
/* math operation result label */
#math-label {
font-weight: bold;
font-size: 16px
}

15
go.mod
View File

@@ -1,19 +1,22 @@
module github.com/nwg-piotr/nwg-drawer
go 1.22
go 1.23
require (
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774
github.com/fsnotify/fsnotify v1.7.0
github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56
github.com/diamondburned/gotk4-layer-shell/pkg v0.0.0-20240109211357-6efa9f6dc438
github.com/diamondburned/gotk4/pkg v0.3.1
github.com/expr-lang/expr v1.16.9
github.com/fsnotify/fsnotify v1.8.0
github.com/joshuarubin/go-sway v1.2.0
github.com/sirupsen/logrus v1.9.3
)
require (
github.com/KarpelesLab/weak v0.1.1 // indirect
github.com/joshuarubin/lifecycle v1.1.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
)

29
go.sum
View File

@@ -1,15 +1,20 @@
github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g=
github.com/KarpelesLab/weak v0.1.1/go.mod h1:pzXsWs5f2bf+fpgHayTlBE1qJpO3MpJKo5sRaLu1XNw=
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37 h1:28uU3TtuvQ6KRndxg9TrC868jBWmSKgh0GTXkACCXmA=
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.mod h1:6AXRstqK+32jeFmw89QGL2748+dj34Av4xc/I9oo9BY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774 h1:o87OVL4olQBlVwN3+NSVQpS6gj9FWUYtxOfHXWZigUE=
github.com/dlasky/gotk3-layershell v0.0.0-20240515133811-5c5115f0d774/go.mod h1:JHLx2Wz4mAPVwn4PFhC69ydwyHP4A3wQvlg7HKVVc1U=
github.com/diamondburned/gotk4-layer-shell/pkg v0.0.0-20240109211357-6efa9f6dc438 h1:Ymnl4B+Fn4srLxXbRV2RY1iHT2SH3oAkOfxeEeMI3Fg=
github.com/diamondburned/gotk4-layer-shell/pkg v0.0.0-20240109211357-6efa9f6dc438/go.mod h1:AjrxxF6teeNWgaEg0zIUwoqFtXlVTHlEGZvrOn7RXaQ=
github.com/diamondburned/gotk4/pkg v0.3.1 h1:uhkXSUPUsCyz3yujdvl7DSN8jiLS2BgNTQE95hk6ygg=
github.com/diamondburned/gotk4/pkg v0.3.1/go.mod h1:DqeOW+MxSZFg9OO+esk4JgQk0TiUJJUBfMltKhG+ub4=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56 h1:eR+xxC8qqKuPMTucZqaklBxLIT7/4L7dzhlwKMrDbj8=
github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/joshuarubin/go-sway v1.2.0 h1:t3eqW504//uj9PDwFf0+IVfkD+WoOGaDX5gYIe0BHyM=
github.com/joshuarubin/go-sway v1.2.0/go.mod h1:qcDd6f25vJ0++wICwA1BainIcRC67p2Mb4lsrZ0k3/k=
github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54=
@@ -27,16 +32,18 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

224
main.go
View File

@@ -12,16 +12,18 @@ import (
"syscall"
"time"
log "github.com/sirupsen/logrus"
"github.com/diamondburned/gotk4-layer-shell/pkg/gtklayershell"
"github.com/expr-lang/expr"
"github.com/allan-simon/go-singleinstance"
"github.com/dlasky/gotk3-layershell/layershell"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
log "github.com/sirupsen/logrus"
"github.com/diamondburned/gotk4/pkg/gdk/v3"
"github.com/diamondburned/gotk4/pkg/glib/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v3"
)
const version = "0.5.0"
const version = "0.6.0"
var (
appDirs []string
@@ -114,7 +116,7 @@ var (
win *gtk.Window
resultWindow *gtk.ScrolledWindow
fileSearchResults []string
searchEntry *gtk.SearchEntry
//searchEntry *gtk.SearchEntry
phrase string
fileSearchResultFlowBox *gtk.FlowBox
userDirsMap map[string]string
@@ -386,90 +388,79 @@ func main() {
}
// USER INTERFACE
gtk.Init(nil)
gtk.Init()
settings, _ := gtk.SettingsGetDefault()
settings := gtk.SettingsGetDefault()
if *gtkTheme != "" {
err = settings.SetProperty("gtk-theme-name", *gtkTheme)
if err != nil {
log.Error("Unable to set theme:", err)
} else {
settings.SetObjectProperty("gtk-theme-name", *gtkTheme)
log.Infof("User demanded theme: %s", *gtkTheme)
}
} else {
err := settings.SetProperty("gtk-application-prefer-dark-theme", true)
if err != nil {
log.Error("Error setting 'gtk-application-prefer-dark-theme' property")
return
}
settings.SetObjectProperty("gtk-application-prefer-dark-theme", true)
log.Info("Preferring dark theme variants")
}
if *gtkIconTheme != "" {
err = settings.SetProperty("gtk-icon-theme-name", *gtkIconTheme)
if err != nil {
log.Error("Unable to set icon theme:", err)
} else {
settings.SetObjectProperty("gtk-icon-theme-name", *gtkIconTheme)
log.Infof("User demanded icon theme: %s", *gtkIconTheme)
}
}
cssProvider, _ := gtk.CssProviderNew()
cssProvider := gtk.NewCSSProvider()
err = cssProvider.LoadFromPath(*cssFileName)
if err != nil {
log.Errorf("ERROR: %s css file not found or erroneous. Using GTK styling.", *cssFileName)
} else {
log.Info(fmt.Sprintf("Using style from %s", *cssFileName))
screen, _ := gdk.ScreenGetDefault()
gtk.AddProviderForScreen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
screen := gdk.ScreenGetDefault()
gtk.StyleContextAddProviderForScreen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
}
win, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
win = gtk.NewWindow(gtk.WindowToplevel)
if err != nil {
log.Fatal("Unable to create window:", err)
}
if wayland() {
layershell.InitForWindow(win)
gtklayershell.InitForWindow(win)
gtklayershell.SetNamespace(win, "nwg-drawer")
var output2mon map[string]*gdk.Monitor
if *targetOutput != "" {
// We want to assign layershell to a monitor, but we only know the output name!
output2mon, err = mapOutputs()
log.Debugf("output2mon: %s", output2mon)
log.Debugf("output2mon: %v", output2mon)
if err == nil {
monitor := output2mon[*targetOutput]
layershell.SetMonitor(win, monitor)
mon := output2mon[*targetOutput]
gtklayershell.SetMonitor(win, mon)
} else {
log.Errorf("%s", 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)
gtklayershell.SetAnchor(win, gtklayershell.LayerShellEdgeBottom, true)
gtklayershell.SetAnchor(win, gtklayershell.LayerShellEdgeTop, true)
gtklayershell.SetAnchor(win, gtklayershell.LayerShellEdgeLeft, true)
gtklayershell.SetAnchor(win, gtklayershell.LayerShellEdgeRight, true)
if *overlay {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_OVERLAY)
layershell.SetExclusiveZone(win, -1)
gtklayershell.SetLayer(win, gtklayershell.LayerShellLayerOverlay)
gtklayershell.SetExclusiveZone(win, -1)
} else {
layershell.SetLayer(win, layershell.LAYER_SHELL_LAYER_TOP)
gtklayershell.SetLayer(win, gtklayershell.LayerShellLayerTop)
}
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)
gtklayershell.SetMargin(win, gtklayershell.LayerShellEdgeTop, *marginTop)
gtklayershell.SetMargin(win, gtklayershell.LayerShellEdgeLeft, *marginLeft)
gtklayershell.SetMargin(win, gtklayershell.LayerShellEdgeRight, *marginRight)
gtklayershell.SetMargin(win, gtklayershell.LayerShellEdgeBottom, *marginBottom)
if *keyboard {
log.Info("Setting GTK layer shell keyboard mode to: on-demand")
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND)
gtklayershell.SetKeyboardMode(win, gtklayershell.LayerShellKeyboardModeOnDemand)
} else {
log.Info("Setting GTK layer shell keyboard mode to default: exclusive")
layershell.SetKeyboardMode(win, layershell.LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE)
gtklayershell.SetKeyboardMode(win, gtklayershell.LayerShellKeyboardModeExclusive)
}
}
@@ -479,12 +470,14 @@ func main() {
})
win.Connect("key-release-event", func(_ *gtk.Window, event *gdk.Event) bool {
key := &gdk.EventKey{Event: event}
if key.KeyVal() == gdk.KEY_Escape {
s, _ := searchEntry.GetText()
//key := &gdk.EventKey{Event: event}
key := event.AsKey()
if key.Keyval() == gdk.KEY_Escape {
//s := searchEntry.Text()
s := ""
if s != "" {
searchEntry.GrabFocus()
searchEntry.SetText("")
//searchEntry.GrabFocus()
//searchEntry.SetText("")
} else {
if !*resident {
gtk.MainQuit()
@@ -493,27 +486,43 @@ func main() {
}
}
return true
} else if key.KeyVal() == gdk.KEY_Tab {
} else if key.Keyval() == gdk.KEY_Tab {
if firstPowerBtn != nil {
firstPowerBtn.ToWidget().GrabFocus()
firstPowerBtn.GrabFocus()
}
} else if key.Keyval() == gdk.KEY_Return {
//s := searchEntry.Text()
s := ""
if s != "" {
// Check if the search box content is an arithmetic expression. If so, display the result
// and copy to the clipboard with wl-copy.
result, e := expr.Eval(s, nil)
if e == nil {
log.Debugf("Setting up mathemathical operation result window. Operation: %s, result: %v", s, result)
setUpOperationResultWindow(s, fmt.Sprintf("%v", result))
}
}
return false
return true
}
return true
})
win.Connect("key-press-event", func(_ *gtk.Window, event *gdk.Event) bool {
key := &gdk.EventKey{Event: event}
switch key.KeyVal() {
//key := &gdk.EventKey{Event: event}
key := event.AsKey()
switch key.Keyval() {
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:
return false
default:
if !searchEntry.IsFocus() {
searchEntry.GrabFocusWithoutSelecting()
//if !searchEntry.IsFocus() {
// searchEntry.GrabFocusWithoutSelecting()
//}
}
return false
}
})
/*
@@ -528,48 +537,49 @@ func main() {
}
// Set up UI
outerVBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
outerVBox := gtk.NewBox(gtk.OrientationVertical, 0)
win.Add(outerVBox)
searchBoxWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
outerVBox.PackStart(searchBoxWrapper, false, false, 10)
//searchBoxWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
//outerVBox.PackStart(searchBoxWrapper, false, false, 10)
searchEntry = setUpSearchEntry()
searchEntry.SetMaxWidthChars(30)
searchBoxWrapper.PackStart(searchEntry, true, false, 0)
//searchEntry = setUpSearchEntry()
//searchEntry.SetMaxWidthChars(30)
//searchBoxWrapper.PackStart(searchEntry, true, false, 0)
if !*noCats {
categoriesWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
categoriesWrapper = gtk.NewBox(gtk.OrientationHorizontal, 0)
categoriesButtonBox := setUpCategoriesButtonBox()
categoriesWrapper.PackStart(categoriesButtonBox, true, false, 0)
outerVBox.PackStart(categoriesWrapper, false, false, 0)
}
pinnedWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
pinnedWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
outerVBox.PackStart(pinnedWrapper, false, false, 0)
pinnedFlowBoxWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
pinnedFlowBoxWrapper = gtk.NewBox(gtk.OrientationHorizontal, 0)
outerVBox.PackStart(pinnedFlowBoxWrapper, false, false, 0)
pinnedFlowBox = setUpPinnedFlowBox()
resultWindow, _ = gtk.ScrolledWindowNew(nil, nil)
resultWindow.SetEvents(int(gdk.ALL_EVENTS_MASK))
resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
resultWindow = gtk.NewScrolledWindow(nil, nil)
resultWindow.SetEvents(int(gdk.AllEventsMask))
resultWindow.SetPolicy(gtk.PolicyAutomatic, gtk.PolicyAutomatic)
// On touch screen we don't want the button-release-event to launch the app if the user just wanted to scroll the
// window. Let's forbid doing so if the content has been scrolled. We will reset the value on button-press-event.
// Resolves https://github.com/nwg-piotr/nwg-drawer/issues/110
vAdj := resultWindow.GetVAdjustment()
vAdj := resultWindow.VAdjustment()
vAdj.Connect("value-changed", func() {
beenScrolled = true
})
hAdj := resultWindow.GetHAdjustment()
hAdj := resultWindow.HAdjustment()
hAdj.Connect("value-changed", func() {
beenScrolled = true
})
resultWindow.Connect("button-release-event", func(_ *gtk.ScrolledWindow, event *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(event)
//btnEvent := gdk.EventButtonNewFromEvent(event)
btnEvent := event.AsButton()
if btnEvent.Button() == 3 {
if !*resident {
gtk.MainQuit()
@@ -582,35 +592,35 @@ func main() {
})
outerVBox.PackStart(resultWindow, true, true, 10)
resultsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
resultsWrapper := gtk.NewBox(gtk.OrientationVertical, 0)
resultWindow.Add(resultsWrapper)
appSearchResultWrapper, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
appSearchResultWrapper = gtk.NewBox(gtk.OrientationVertical, 0)
resultsWrapper.PackStart(appSearchResultWrapper, false, false, 0)
appFlowBox = setUpAppsFlowBox(nil, "")
// Focus 1st pinned item if any, otherwise focus 1st found app icon
var button gtk.IWidget
if pinnedFlowBox.GetChildren().Length() > 0 {
button, err = pinnedFlowBox.GetChildAtIndex(0).GetChild()
var button gtk.Widget
if len(pinnedFlowBox.Children()) > 0 {
button = pinnedFlowBox.ChildAtIndex(0).Widget
} else {
button, err = appFlowBox.GetChildAtIndex(0).GetChild()
button = appFlowBox.ChildAtIndex(0).Widget
}
if err == nil {
button.ToWidget().GrabFocus()
button.GrabFocus()
}
userDirsMap = mapXdgUserDirs()
log.Debugf("User dirs map: %s", userDirsMap)
placeholder, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
placeholder := gtk.NewBox(gtk.OrientationVertical, 0)
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 := gtk.NewBox(gtk.OrientationHorizontal, 0)
fileSearchResultWrapper = gtk.NewBox(gtk.OrientationHorizontal, 0)
fileSearchResultWrapper.SetObjectProperty("name", "files-box")
wrapper.PackStart(fileSearchResultWrapper, true, false, 0)
resultsWrapper.PackEnd(wrapper, false, false, 10)
}
@@ -618,13 +628,13 @@ func main() {
// Power Button Bar
if dataDirectory != "" {
if *pbExit != "" || *pbLock != "" || *pbPoweroff != "" || *pbReboot != "" || *pbSleep != "" {
powerBarWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
powerBarWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
outerVBox.PackStart(powerBarWrapper, false, false, 0)
powerButtonsWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
powerButtonsWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
powerBarWrapper.PackStart(powerButtonsWrapper, true, false, 12)
if *pbPoweroff != "" {
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/poweroff.svg"), *pbPoweroff)
} else {
@@ -634,7 +644,7 @@ func main() {
firstPowerBtn = btn
}
if *pbSleep != "" {
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/sleep.svg"), *pbSleep)
} else {
@@ -644,7 +654,7 @@ func main() {
firstPowerBtn = btn
}
if *pbReboot != "" {
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/reboot.svg"), *pbReboot)
} else {
@@ -654,7 +664,7 @@ func main() {
firstPowerBtn = btn
}
if *pbExit != "" {
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/exit.svg"), *pbExit)
} else {
@@ -664,7 +674,7 @@ func main() {
firstPowerBtn = btn
}
if *pbLock != "" {
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
if !*pbUseIconTheme {
btn = powerButton(filepath.Join(dataDirectory, "img/lock.svg"), *pbLock)
} else {
@@ -678,21 +688,21 @@ func main() {
log.Warn("Couldn't find data dir, power bar icons unavailable")
}
statusLineWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
statusLineWrapper.SetProperty("name", "status-line-wrapper")
statusLineWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
statusLineWrapper.SetObjectProperty("name", "status-line-wrapper")
outerVBox.PackStart(statusLineWrapper, false, false, 10)
statusLabel, _ = gtk.LabelNew(status)
statusLabel.SetProperty("name", "status-label")
statusLabel = gtk.NewLabel(status)
statusLabel.SetObjectProperty("name", "status-label")
statusLineWrapper.PackStart(statusLabel, true, false, 0)
win.ShowAll()
if !*noFS {
fileSearchResultWrapper.SetSizeRequest(appFlowBox.GetAllocatedWidth(), 1)
fileSearchResultWrapper.SetSizeRequest(appFlowBox.AllocatedWidth(), 1)
fileSearchResultWrapper.Hide()
}
if !*noCats {
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.GetAllocatedHeight()*2)
categoriesWrapper.SetSizeRequest(1, categoriesWrapper.AllocatedHeight()*2)
}
if *resident {
win.Hide()
@@ -726,14 +736,14 @@ func main() {
fileSearchResultWrapper.Hide()
}
// focus 1st element
var button gtk.IWidget
if pinnedFlowBox.GetChildren().Length() > 0 {
button, err = pinnedFlowBox.GetChildAtIndex(0).GetChild()
var button gtk.Widget
if len(pinnedFlowBox.Children()) > 0 {
button = pinnedFlowBox.ChildAtIndex(0).Widget
} else {
button, err = appFlowBox.GetChildAtIndex(0).GetChild()
button = appFlowBox.ChildAtIndex(0).Widget
}
if err == nil {
button.ToWidget().GrabFocus()
button.GrabFocus()
}
}
@@ -764,20 +774,20 @@ func restoreStateAndHide() {
}
// clear search
searchEntry.SetText("")
//searchEntry.SetText("")
// clear category filter (in gotk3 it means: rebuild, as we have no filtering here)
// One day or another we'll add SetFilterFunction here; it was impossible on the gotk3 library
appFlowBox = setUpAppsFlowBox(nil, "")
for _, btn := range catButtons {
btn.SetImagePosition(gtk.POS_LEFT)
btn.SetImagePosition(gtk.PosLeft)
btn.SetSizeRequest(0, 0)
}
// scroll to the top
if resultWindow != nil {
resultWindow.GetVAdjustment().SetValue(0)
resultWindow.VAdjustment().SetValue(0)
}
t := time.Now()
log.Debugf(fmt.Sprintf("UI hidden and restored in the backgroud in %v ms", t.Sub(timeStart1).Milliseconds()))
log.Debugf(fmt.Sprintf("UI hidden and restored in the background in %v ms", t.Sub(timeStart1).Milliseconds()))
}

11
run_forever.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# Infinite loop to run the command
while true; do
echo "Starting nwg-drawer..."
bin/nwg-drawer -nocats -nofs -ovl -d
done &
# Return immediately after starting the loop
echo "The loop is running in the background."

104
tools.go
View File

@@ -5,8 +5,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/joshuarubin/go-sway"
log "github.com/sirupsen/logrus"
"io"
"io/fs"
"net"
@@ -20,22 +18,23 @@ import (
"syscall"
"time"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"github.com/joshuarubin/go-sway"
log "github.com/sirupsen/logrus"
"github.com/diamondburned/gotk4/pkg/gdk/v3"
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v3"
)
func wayland() bool {
return os.Getenv("WAYLAND_DISPLAY") != "" || os.Getenv("XDG_SESSION_TYPE") == "wayland"
}
func createPixbuf(icon string, size int) (*gdk.Pixbuf, error) {
iconTheme, err := gtk.IconThemeGetDefault()
if err != nil {
log.Fatal("Couldn't get default theme: ", err)
}
func createPixbuf(icon string, size int) (*gdkpixbuf.Pixbuf, error) {
iconTheme := gtk.IconThemeGetDefault()
if strings.Contains(icon, "/") {
pixbuf, err := gdk.PixbufNewFromFileAtSize(icon, size, size)
pixbuf, err := gdkpixbuf.NewPixbufFromFileAtSize(icon, size, size)
if err != nil {
log.Errorf("%s", err)
return nil, err
@@ -47,18 +46,18 @@ func createPixbuf(icon string, size int) (*gdk.Pixbuf, error) {
icon = strings.Split(icon, ".")[0]
}
pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.ICON_LOOKUP_FORCE_SIZE)
pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.IconLookupForceSize)
if err != nil {
if strings.HasPrefix(icon, "/") {
pixbuf, err := gdk.PixbufNewFromFileAtSize(icon, size, size)
if err != nil {
return nil, err
pixbuf, e := gdkpixbuf.NewPixbufFromFileAtSize(icon, size, size)
if e != nil {
return nil, e
}
return pixbuf, nil
}
pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.ICON_LOOKUP_FORCE_SIZE)
pixbuf, err := iconTheme.LoadIcon(icon, size, gtk.IconLookupForceSize)
if err != nil {
return nil, err
}
@@ -240,7 +239,8 @@ func dataDir() string {
}
for _, d := range strings.Split(xdgDataDirs, ":") {
p := filepath.Join(d, "nwg-drawer")
if pathExists(p) {
q := filepath.Join(p, "desktop-directories")
if pathExists(q) {
log.Infof("Data dir: %v", p)
return p
}
@@ -251,35 +251,36 @@ func dataDir() string {
func getAppDirs() []string {
var dirs []string
log.Info("App dirs::")
home := os.Getenv("HOME")
xdgDataHome := os.Getenv("XDG_DATA_HOME")
xdgDataDirs := os.Getenv("XDG_DATA_DIRS")
if xdgDataDirs == "" {
xdgDataDirs = "/usr/local/share/:/usr/share/"
}
if xdgDataHome != "" {
dirs = append(dirs, filepath.Join(xdgDataHome, "applications"))
} else if home != "" {
dirs = append(dirs, filepath.Join(home, ".local/share/applications"))
}
for _, d := range strings.Split(xdgDataDirs, ":") {
dirs = append(dirs, filepath.Join(d, "applications"))
}
flatpakDirs := []string{filepath.Join(home, ".local/share/flatpak/exports/share/applications"),
"/var/lib/flatpak/exports/share/applications"}
//xdgDataHome := os.Getenv("XDG_DATA_HOME")
//xdgDataDirs := os.Getenv("XDG_DATA_DIRS")
for _, d := range flatpakDirs {
if pathExists(d) && !isIn(dirs, d) {
dirs = append(dirs, d)
custom := os.Getenv("CUSTOM_DATA_DIRS")
if home != "" {
if custom == "" {
dirs = append(dirs, filepath.Join(home, "/my_applications"))
} else {
dirs = append(dirs, filepath.Join(home, custom))
}
}
log.Infof("App dirs: %v", dirs)
var confirmedDirs []string
for _, d := range dirs {
if pathExists(d) {
confirmedDirs = append(confirmedDirs, d)
}
}
log.Infof("App dirs: %v", dirs)
return confirmedDirs
}
@@ -424,7 +425,7 @@ func parseDesktopFiles(desktopFiles []string) string {
assignToLists(entry.DesktopID, entry.Category)
}
sort.Slice(desktopEntries, func(i, j int) bool {
return desktopEntries[i].NameLoc < desktopEntries[j].NameLoc
return strings.ToLower(desktopEntries[i].NameLoc) < strings.ToLower(desktopEntries[j].NameLoc)
})
summary := fmt.Sprintf("%v entries (+%v hidden)", len(desktopEntries)-hidden, hidden)
log.Infof("Skipped %v duplicates; %v .desktop entries hidden by \"NoDisplay=true\"", skipped, hidden)
@@ -571,7 +572,7 @@ func savePinned() {
}
}
func launch(command string, terminal bool) {
func launch(command string, terminal bool, terminate bool) {
// trim % and everything afterwards
if strings.Contains(command, "%") {
cutAt := strings.Index(command, "%")
@@ -581,14 +582,12 @@ func launch(command string, terminal bool) {
}
themeToPrepend := ""
// add "GTK_THEME=<default_gtk_theme>" environment variable
//add "GTK_THEME=<default_gtk_theme>" environment variable
if *forceTheme {
settings, _ := gtk.SettingsGetDefault()
th, err := settings.GetProperty("gtk-theme-name")
if err == nil {
settings := gtk.SettingsGetDefault()
th := settings.ObjectProperty("gtk-theme-name")
themeToPrepend = th.(string)
}
}
if themeToPrepend != "" {
command = fmt.Sprintf("GTK_THEME=%q %s", themeToPrepend, command)
@@ -615,7 +614,7 @@ func launch(command string, terminal bool) {
cmd = exec.Command("riverctl", "spawn", strings.Join(elements, " "))
}
msg := fmt.Sprintf("command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:])
msg := fmt.Sprintf("Executing command: %q; args: %q\n", cmd.Args[0], cmd.Args[1:])
log.Info(msg)
cmd.SysProcAttr = &syscall.SysProcAttr{
@@ -628,14 +627,18 @@ func launch(command string, terminal bool) {
// Collect the exit code of the child process to prevent zombies
// if the drawer runs in resident mode
go func() {
restoreStateAndHide()
_ = cmd.Wait()
gtk.MainQuit()
}()
}
if terminate {
if *resident {
restoreStateAndHide()
} else {
gtk.MainQuit()
}
}
}
@@ -681,14 +684,11 @@ func mapOutputs() (map[string]*gdk.Monitor, error) {
err := listHyprlandMonitors()
if err == nil {
display, err := gdk.DisplayGetDefault()
if err != nil {
return nil, err
}
display := gdk.DisplayGetDefault()
num := display.GetNMonitors()
num := display.NMonitors()
for i := 0; i < num; i++ {
mon, _ := display.GetMonitor(i)
mon := display.Monitor(i)
output := hyprlandMonitors[i]
result[output.Name] = mon
}
@@ -710,14 +710,14 @@ func mapOutputs() (map[string]*gdk.Monitor, error) {
return nil, err
}
display, err := gdk.DisplayGetDefault()
display := gdk.DisplayGetDefault()
if err != nil {
return nil, err
}
num := display.GetNMonitors()
num := display.NMonitors()
for i := 0; i < num; i++ {
mon, _ := display.GetMonitor(i)
mon := display.Monitor(i)
output := outputs[i]
result[output.Name] = mon
}

View File

@@ -6,17 +6,20 @@ import (
"path/filepath"
"strings"
"github.com/diamondburned/gotk4-layer-shell/pkg/gtklayershell"
log "github.com/sirupsen/logrus"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"github.com/diamondburned/gotk4/pkg/gdk/v3"
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v3"
)
func setUpPinnedFlowBox() *gtk.FlowBox {
if pinnedFlowBox != nil {
pinnedFlowBox.Destroy()
}
flowBox, _ := gtk.FlowBoxNew()
flowBox := gtk.NewFlowBox()
if uint(len(pinned)) >= *columnsNumber {
flowBox.SetMaxChildrenPerLine(*columnsNumber)
} else if len(pinned) > 0 {
@@ -26,8 +29,8 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
flowBox.SetColumnSpacing(*itemSpacing)
flowBox.SetRowSpacing(*itemSpacing)
flowBox.SetHomogeneous(true)
flowBox.SetProperty("name", "pinned-box")
flowBox.SetSelectionMode(gtk.SELECTION_NONE)
flowBox.SetObjectProperty("name", "pinned-box")
flowBox.SetSelectionMode(gtk.SelectionNone)
if len(pinned) > 0 {
for _, desktopID := range pinned {
@@ -37,19 +40,19 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
continue
}
btn, _ := gtk.ButtonNew()
btn := gtk.NewButton()
var img *gtk.Image
if entry.Icon != "" {
pixbuf, _ := createPixbuf(entry.Icon, *iconSize)
img, _ = gtk.ImageNewFromPixbuf(pixbuf)
img = gtk.NewImageFromPixbuf(pixbuf)
} else {
img, _ = gtk.ImageNewFromIconName("image-missing", gtk.ICON_SIZE_INVALID)
img = gtk.NewImageFromIconName("image-missing", int(gtk.IconSizeInvalid))
}
btn.SetImage(img)
btn.SetAlwaysShowImage(true)
btn.SetImagePosition(gtk.POS_TOP)
btn.SetImagePosition(gtk.PosTop)
name := ""
if entry.NameLoc != "" {
@@ -59,14 +62,14 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
}
if len(name) > 20 {
r := substring(name, 0, 17)
name = fmt.Sprintf("%s…", string(r))
name = fmt.Sprintf("%s…", r)
}
btn.SetLabel(name)
btn.Connect("button-release-event", func(row *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
btn.Connect("button-release-event", func(row *gtk.Button, event *gdk.Event) bool {
btnEvent := event.AsButton()
if btnEvent.Button() == 1 {
launch(entry.Exec, entry.Terminal)
launch(entry.Exec, entry.Terminal, true)
return true
} else if btnEvent.Button() == 3 {
unpinItem(entry.DesktopID)
@@ -75,7 +78,7 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
return false
})
btn.Connect("activate", func() {
launch(entry.Exec, entry.Terminal)
launch(entry.Exec, entry.Terminal, true)
})
btn.Connect("enter-notify-event", func() {
statusLabel.SetText(entry.CommentLoc)
@@ -84,13 +87,9 @@ func setUpPinnedFlowBox() *gtk.FlowBox {
statusLabel.SetText(entry.CommentLoc)
})
flowBox.Add(btn)
btn.Parent().(*gtk.FlowBoxChild).SetCanFocus(false)
}
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()
@@ -110,17 +109,17 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
"other": listOther,
}
eventBox, _ := gtk.EventBoxNew()
eventBox := gtk.NewEventBox()
hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
hBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
eventBox.Add(hBox)
button, _ := gtk.ButtonNewWithLabel("All")
button.SetProperty("name", "category-button")
button := gtk.NewButtonWithLabel("All")
button.SetObjectProperty("name", "category-button")
button.Connect("clicked", func(item *gtk.Button) {
searchEntry.SetText("")
//searchEntry.SetText("")
appFlowBox = setUpAppsFlowBox(nil, "")
for _, btn := range catButtons {
btn.SetImagePosition(gtk.POS_LEFT)
btn.SetImagePosition(gtk.PosLeft)
btn.SetSizeRequest(0, 0)
}
})
@@ -128,8 +127,8 @@ 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")
button = gtk.NewButtonFromIconName(cat.Icon, int(gtk.IconSizeMenu))
button.SetObjectProperty("name", "category-button")
catButtons = append(catButtons, button)
button.SetLabel(cat.DisplayName)
button.SetAlwaysShowImage(true)
@@ -137,14 +136,14 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
name := cat.Name
b := *button
button.Connect("clicked", func(item *gtk.Button) {
searchEntry.SetText("")
// !!! since gotk3 FlowBox type does not implement set_filter_func, we need to rebuild appFlowBox
//searchEntry.SetText("")
// One day or another we'll add SetFilterFunction here; it was impossible on the gotk3 library
appFlowBox = setUpAppsFlowBox(lists[name], "")
for _, btn := range catButtons {
btn.SetImagePosition(gtk.POS_LEFT)
btn.SetImagePosition(gtk.PosLeft)
}
w := b.GetAllocatedWidth()
b.SetImagePosition(gtk.POS_TOP)
w := b.AllocatedWidth()
b.SetImagePosition(gtk.PosTop)
b.SetSizeRequest(w, 0)
if fileSearchResultWrapper != nil {
fileSearchResultWrapper.Hide()
@@ -186,13 +185,13 @@ func setUpAppsFlowBox(categoryList []string, searchPhrase string) *gtk.FlowBox {
if appFlowBox != nil {
appFlowBox.Destroy()
}
flowBox, _ := gtk.FlowBoxNew()
flowBox := gtk.NewFlowBox()
flowBox.SetMinChildrenPerLine(*columnsNumber)
flowBox.SetMaxChildrenPerLine(*columnsNumber)
flowBox.SetColumnSpacing(*itemSpacing)
flowBox.SetRowSpacing(*itemSpacing)
flowBox.SetHomogeneous(true)
flowBox.SetSelectionMode(gtk.SELECTION_NONE)
flowBox.SetSelectionMode(gtk.SelectionNone)
for _, entry := range desktopEntries {
if searchPhrase == "" {
@@ -205,6 +204,7 @@ func setUpAppsFlowBox(categoryList []string, searchPhrase string) *gtk.FlowBox {
} else {
button := flowBoxButton(entry)
flowBox.Add(button)
button.Parent().(*gtk.FlowBoxChild).SetCanFocus(false)
}
}
} else {
@@ -214,26 +214,24 @@ func setUpAppsFlowBox(categoryList []string, searchPhrase string) *gtk.FlowBox {
strings.Contains(strings.ToLower(entry.Exec), strings.ToLower(searchPhrase))) {
button := flowBoxButton(entry)
flowBox.Add(button)
button.Parent().(*gtk.FlowBoxChild).SetCanFocus(false)
}
}
}
hWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
hWrapper := gtk.NewBox(gtk.OrientationHorizontal, 0)
appSearchResultWrapper.PackStart(hWrapper, false, false, 0)
hWrapper.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)
})
resultWindow.ShowAll()
return flowBox
}
func flowBoxButton(entry desktopEntry) *gtk.Button {
button, _ := gtk.ButtonNew()
button := gtk.NewButton()
button.SetAlwaysShowImage(true)
var pixbuf *gdk.Pixbuf
var pixbuf *gdkpixbuf.Pixbuf
var img *gtk.Image
var err error
if entry.Icon != "" {
@@ -245,14 +243,14 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
if err != nil {
pixbuf, _ = createPixbuf("unknown", *iconSize)
}
img, _ = gtk.ImageNewFromPixbuf(pixbuf)
img = gtk.NewImageFromPixbuf(pixbuf)
button.SetImage(img)
button.SetImagePosition(gtk.POS_TOP)
button.SetImagePosition(gtk.PosTop)
name := entry.NameLoc
if len(name) > 20 {
r := substring(name, 0, 17)
name = fmt.Sprintf("%s…", string(r))
name = fmt.Sprintf("%s…", r)
}
button.SetLabel(name)
@@ -262,7 +260,7 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
desc := entry.CommentLoc
if len(desc) > 120 {
r := substring(desc, 0, 117)
desc = fmt.Sprintf("%s…", string(r))
desc = fmt.Sprintf("%s…", r)
}
button.Connect("button-press-event", func() {
@@ -270,11 +268,11 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
beenScrolled = false
})
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
button.Connect("button-release-event", func(btn *gtk.Button, event *gdk.Event) bool {
btnEvent := event.AsButton()
if btnEvent.Button() == 1 {
if !beenScrolled {
launch(exec, terminal)
launch(exec, terminal, true)
return true
}
} else if btnEvent.Button() == 3 {
@@ -284,7 +282,7 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
return false
})
button.Connect("activate", func() {
launch(exec, terminal)
launch(exec, terminal, true)
})
button.Connect("enter-notify-event", func() {
statusLabel.SetText(desc)
@@ -299,36 +297,36 @@ func flowBoxButton(entry desktopEntry) *gtk.Button {
}
func powerButton(iconPathOrName, command string) *gtk.Button {
button, _ := gtk.ButtonNew()
button := gtk.NewButton()
button.SetAlwaysShowImage(true)
var pixbuf *gdk.Pixbuf
var pixbuf *gdkpixbuf.Pixbuf
var img *gtk.Image
var err error
if !*pbUseIconTheme {
pixbuf, err = gdk.PixbufNewFromFileAtSize(iconPathOrName, *pbSize, *pbSize)
pixbuf, err = gdkpixbuf.NewPixbufFromFileAtSize(iconPathOrName, *pbSize, *pbSize)
if err != nil {
pixbuf, _ = createPixbuf("unknown", *pbSize)
log.Warnf("Couldn't find icon %s", iconPathOrName)
}
img, _ = gtk.ImageNewFromPixbuf(pixbuf)
img = gtk.NewImageFromPixbuf(pixbuf)
} else {
img, _ = gtk.ImageNewFromIconName(iconPathOrName, gtk.ICON_SIZE_DIALOG)
img = gtk.NewImageFromIconName(iconPathOrName, int(gtk.IconSizeDialog))
}
button.SetImage(img)
button.SetImagePosition(gtk.POS_TOP)
button.SetImagePosition(gtk.PosTop)
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
button.Connect("button-release-event", func(btn *gtk.Button, event *gdk.Event) bool {
btnEvent := event.AsButton()
if btnEvent.Button() == 1 {
launch(command, false)
launch(command, false, true)
return true
}
return false
})
button.Connect("activate", func() {
launch(command, false)
launch(command, false, true)
})
button.Connect("enter-notify-event", func() {
statusLabel.SetText(command)
@@ -346,8 +344,8 @@ func setUpFileSearchResultContainer() *gtk.FlowBox {
if fileSearchResultFlowBox != nil {
fileSearchResultFlowBox.Destroy()
}
flowBox, _ := gtk.FlowBoxNew()
flowBox.SetProperty("orientation", gtk.ORIENTATION_VERTICAL)
flowBox := gtk.NewFlowBox()
flowBox.SetObjectProperty("orientation", gtk.OrientationVertical)
fileSearchResultWrapper.PackStart(flowBox, false, false, 10)
return flowBox
@@ -360,7 +358,7 @@ func walk(path string, d fs.DirEntry, e error) error {
// don't search leading part of the path, as e.g. '/home/user/Pictures'
toSearch := strings.Split(path, ignore)[1]
// Remaing part of the path (w/o file name) must be checked against being present in excluded dirs
// Remaining part of the path (w/o file name) must be checked against being present in excluded dirs
doSearch := true
parts := strings.Split(toSearch, "/")
remainingPart := ""
@@ -384,18 +382,15 @@ func walk(path string, d fs.DirEntry, e error) error {
}
func setUpSearchEntry() *gtk.SearchEntry {
searchEntry, _ := gtk.SearchEntryNew()
searchEntry.SetPlaceholderText("Type to search")
/*searchEntry.Connect("enter-notify-event", func() {
cancelClose()
})*/
searchEntry.Connect("search-changed", func() {
sEntry := gtk.NewSearchEntry()
sEntry.SetPlaceholderText("Type to search")
sEntry.Connect("search-changed", func() {
for _, btn := range catButtons {
btn.SetImagePosition(gtk.POS_LEFT)
btn.SetImagePosition(gtk.PosLeft)
btn.SetSizeRequest(0, 0)
}
phrase, _ = searchEntry.GetText()
phrase = sEntry.Text()
if len(phrase) > 0 {
// search apps
@@ -415,7 +410,7 @@ func setUpSearchEntry() *gtk.SearchEntry {
searchUserDir(key)
}
}
if fileSearchResultFlowBox.GetChildren().Length() == 0 {
if len(fileSearchResultFlowBox.Children()) == 0 {
fileSearchResultWrapper.Hide()
statusLabel.SetText("0 results")
}
@@ -429,25 +424,22 @@ func setUpSearchEntry() *gtk.SearchEntry {
}
}
// focus 1st search result #17
var w *gtk.Widget
var w *gtk.Button
if appFlowBox != nil {
b := appFlowBox.GetChildAtIndex(0)
b := appFlowBox.ChildAtIndex(0)
if b != nil {
button, err := b.GetChild()
if err == nil {
button.ToWidget().GrabFocus()
w = button.ToWidget()
}
button := b.Child().(*gtk.Button)
button.SetCanFocus(true)
button.GrabFocus()
w = button
}
}
if w == nil && fileSearchResultFlowBox != nil {
f := fileSearchResultFlowBox.GetChildAtIndex(0)
f := fileSearchResultFlowBox.ChildAtIndex(0)
if f != nil {
button, err := f.GetChild()
if err == nil {
button.ToWidget().SetCanFocus(true)
button.ToWidget().GrabFocus()
}
button := f.Child().(*gtk.Box)
button.SetCanFocus(true)
button.GrabFocus()
}
}
} else {
@@ -464,7 +456,7 @@ func setUpSearchEntry() *gtk.SearchEntry {
}
})
return searchEntry
return sEntry
}
func isExcluded(dir string) bool {
@@ -484,14 +476,16 @@ func searchUserDir(dir string) {
if len(fileSearchResults) > 0 {
btn := setUpUserDirButton(fmt.Sprintf("folder-%s", dir), "", dir, userDirsMap)
fileSearchResultFlowBox.Add(btn)
btn.Parent().(*gtk.FlowBoxChild).SetCanFocus(false)
for _, path := range fileSearchResults {
log.Debugf("Path: %s", path)
partOfPathToShow := strings.Split(path, userDirsMap[dir])[1]
if partOfPathToShow != "" {
if !(strings.HasPrefix(path, "#is_dir#") && isExcluded(path)) {
btn := setUpUserFileSearchResultButton(partOfPathToShow, path)
fileSearchResultFlowBox.Add(btn)
button := setUpUserFileSearchResultButton(partOfPathToShow, path)
fileSearchResultFlowBox.Add(button)
button.Parent().(*gtk.FlowBoxChild).SetCanFocus(false)
}
}
@@ -499,14 +493,11 @@ func searchUserDir(dir string) {
fileSearchResultFlowBox.Hide()
statusLabel.SetText(fmt.Sprintf("%v results | LMB: xdg-open | RMB: file manager",
fileSearchResultFlowBox.GetChildren().Length()))
num := uint(fileSearchResultFlowBox.GetChildren().Length() / *fsColumns)
len(fileSearchResultFlowBox.Children())))
num := uint(len(fileSearchResultFlowBox.Children())) / *fsColumns
fileSearchResultFlowBox.SetMinChildrenPerLine(num + 1)
fileSearchResultFlowBox.SetMaxChildrenPerLine(num + 1)
//While moving focus with arrow keys we want buttons to get focus directly
fileSearchResultFlowBox.GetChildren().Foreach(func(item interface{}) {
item.(*gtk.Widget).SetCanFocus(false)
})
fileSearchResultFlowBox.ShowAll()
}
}
@@ -516,10 +507,10 @@ func setUpUserDirButton(iconName, displayName, entryName string, userDirsMap map
parts := strings.Split(userDirsMap[entryName], "/")
displayName = parts[(len(parts) - 1)]
}
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
button, _ := gtk.ButtonNew()
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
button := gtk.NewButton()
button.SetAlwaysShowImage(true)
img, _ := gtk.ImageNewFromIconName(iconName, gtk.ICON_SIZE_MENU)
img := gtk.NewImageFromIconName(iconName, int(gtk.IconSizeMenu))
button.SetImage(img)
if len(displayName) > *nameLimit {
@@ -527,8 +518,8 @@ func setUpUserDirButton(iconName, displayName, entryName string, userDirsMap map
}
button.SetLabel(displayName)
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
button.Connect("button-release-event", func(btn *gtk.Button, event *gdk.Event) bool {
btnEvent := event.AsButton()
if btnEvent.Button() == 1 {
open(userDirsMap[entryName], true)
return true
@@ -539,18 +530,22 @@ func setUpUserDirButton(iconName, displayName, entryName string, userDirsMap map
return false
})
button.Connect("activate", func() {
open(userDirsMap[entryName], true)
})
box.PackStart(button, false, true, 0)
return box
}
func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box {
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
button, _ := gtk.ButtonNew()
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
button := gtk.NewButton()
// in the walk function we've marked directories with the '#is_dir#' prefix
if strings.HasPrefix(filePath, "#is_dir#") {
filePath = filePath[8:]
img, _ := gtk.ImageNewFromIconName("folder", gtk.ICON_SIZE_MENU)
img := gtk.NewImageFromIconName("folder", int(gtk.IconSizeMenu))
button.SetAlwaysShowImage(true)
button.SetImage(img)
}
@@ -565,8 +560,8 @@ func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box {
button.SetTooltipText(tooltipText)
}
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
btnEvent := gdk.EventButtonNewFromEvent(e)
button.Connect("button-release-event", func(btn *gtk.Button, event *gdk.Event) bool {
btnEvent := event.AsButton()
if btnEvent.Button() == 1 {
open(filePath, true)
return true
@@ -580,7 +575,54 @@ func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box {
button.Connect("activate", func() {
open(filePath, true)
})
box.PackStart(button, false, true, 0)
return box
}
func setUpOperationResultWindow(operation string, result string) {
window := gtk.NewWindow(gtk.WindowToplevel)
window.SetModal(true)
if wayland() {
gtklayershell.InitForWindow(window)
gtklayershell.SetLayer(window, gtklayershell.LayerShellLayerOverlay)
gtklayershell.SetKeyboardMode(window, gtklayershell.LayerShellKeyboardModeExclusive)
}
// any key to close the window
window.Connect("key-release-event", func(_ *gtk.Window, event *gdk.Event) bool {
window.Destroy()
return true
})
// any button to close the window
window.Connect("button-release-event", func(_ *gtk.Window, event *gdk.Event) bool {
window.Destroy()
return true
})
outerVBox := gtk.NewBox(gtk.OrientationVertical, 6)
window.Add(outerVBox)
vBox := gtk.NewBox(gtk.OrientationHorizontal, 5)
outerVBox.PackStart(vBox, true, true, 6)
lbl := gtk.NewLabel(fmt.Sprintf("%s = %s", operation, result))
lbl.SetObjectProperty("name", "math-label")
vBox.PackStart(lbl, true, true, 12)
mRefProvider := gtk.NewCSSProvider()
css := "window { background-color: rgba (0, 0, 0, 255); color: #fff; border: solid 1px grey; border-radius: 5px}"
err := mRefProvider.LoadFromData(css)
if err != nil {
log.Warn(err)
}
ctx := window.StyleContext()
ctx.AddProvider(mRefProvider, gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
window.ShowAll()
if wayland() {
cmd := fmt.Sprintf("wl-copy %v", result)
launch(cmd, false, false)
}
}