25 Commits
v0.5.1 ... 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
9 changed files with 332 additions and 316 deletions

View File

@@ -1,11 +1,11 @@
get: get:
go get github.com/gotk3/gotk3 go get github.com/diamondburned/gotk4/pkg/gdk/v3
go get github.com/gotk3/gotk3/gdk go get github.com/diamondburned/gotk4/pkg/glib/v2
go get github.com/gotk3/gotk3/glib go get github.com/diamondburned/gotk4/pkg/gtk/v3
go get github.com/dlasky/gotk3-layershell/layershell go get github.com/diamondburned/gotk4-layer-shell/pkg/gtklayershell
go get github.com/joshuarubin/go-sway go get github.com/joshuarubin/go-sway
go get github.com/allan-simon/go-singleinstance 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 go get github.com/fsnotify/fsnotify
build: 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. 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 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 ### Sample `preferred-apps.json` file content
@@ -199,7 +199,7 @@ I've noy yet tried other file managers.
### File search exclusions ### File search exclusions
You may want to exclude some paths inside your XDG user directories from searching. If so, define exclusions in the 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 ```text
# exclude all paths containing 'node_modules' # exclude all paths containing 'node_modules'
@@ -212,13 +212,21 @@ If the search box is not empty, and you press Enter, the search box content will
If the result is not an error, it will be displayed in a small window, and copied to the clipboard with wl-copy. 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. 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 ## Credits
This program uses some great libraries: This program uses some great libraries:
- [gotk3](https://github.com/gotk3/gotk3) Copyright (c) 2013-2014 Conformal Systems LLC, - [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)
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!
- [go-sway](https://github.com/joshuarubin/go-sway) Copyright (c) 2019 Joshua Rubin - [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 - [go-singleinstance](github.com/allan-simon/go-singleinstance) Copyright (c) 2015 Allan Simon
- [logrus](https://github.com/sirupsen/logrus) Copyright (c) 2014 Simon Eskildsen - [logrus](https://github.com/sirupsen/logrus) Copyright (c) 2014 Simon Eskildsen

View File

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

14
go.mod
View File

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

27
go.sum
View File

@@ -1,17 +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 h1:28uU3TtuvQ6KRndxg9TrC868jBWmSKgh0GTXkACCXmA=
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37/go.mod h1:6AXRstqK+32jeFmw89QGL2748+dj34Av4xc/I9oo9BY= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/diamondburned/gotk4-layer-shell/pkg v0.0.0-20240109211357-6efa9f6dc438 h1:Ymnl4B+Fn4srLxXbRV2RY1iHT2SH3oAkOfxeEeMI3Fg=
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/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 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= 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 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56 h1:eR+xxC8qqKuPMTucZqaklBxLIT7/4L7dzhlwKMrDbj8= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gotk3/gotk3 v0.6.5-0.20240618185848-ff349ae13f56/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/joshuarubin/go-sway v1.2.0 h1:t3eqW504//uj9PDwFf0+IVfkD+WoOGaDX5gYIe0BHyM= 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/go-sway v1.2.0/go.mod h1:qcDd6f25vJ0++wICwA1BainIcRC67p2Mb4lsrZ0k3/k=
github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54= github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54=
@@ -29,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.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 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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.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 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

217
main.go
View File

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

View File

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

View File

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