Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d0c4d2e5f | ||
|
|
a7968d8510 | ||
|
|
e601316480 | ||
|
|
4818dc3358 | ||
|
|
9afd7dca20 | ||
|
|
eb1e9994e7 | ||
|
|
3e2b9395d5 | ||
|
|
061d5e1ca9 | ||
|
|
dc50203ee3 | ||
|
|
656347855b | ||
|
|
81aeb10311 | ||
|
|
04a292c2ec | ||
|
|
6912e7f320 | ||
|
|
41d48a0824 | ||
|
|
399cf7fa3a | ||
|
|
aefe2af4d5 | ||
|
|
71892c25ed | ||
|
|
2536331184 |
45
README.md
45
README.md
@@ -5,7 +5,8 @@ This application is a part of the [nwg-shell](https://github.com/nwg-piotr/nwg-s
|
|||||||
Nwg-drawer is a golang replacement to the `nwggrid` command
|
Nwg-drawer is a golang replacement to the `nwggrid` command
|
||||||
(a part of [nwg-launchers](https://github.com/nwg-piotr/nwg-launchers)). It's being developed with
|
(a part of [nwg-launchers](https://github.com/nwg-piotr/nwg-launchers)). It's being developed with
|
||||||
[sway](https://github.com/swaywm/sway) in mind, but should also work with other wlroots-based Wayland compositors.
|
[sway](https://github.com/swaywm/sway) in mind, but should also work with other wlroots-based Wayland compositors.
|
||||||
X Window System is not supported.
|
X Window System is not officially supported, but you should be able to use the drawer on some floating
|
||||||
|
window managers (tested on Openbox).
|
||||||
|
|
||||||
The `nwg-drawer` command displays the application grid. The search entry allows to look for installed applications,
|
The `nwg-drawer` command displays the application grid. The search entry allows to look for installed applications,
|
||||||
and for files in XDG user directories. The grid view may also be filtered by categories.
|
and for files in XDG user directories. The grid view may also be filtered by categories.
|
||||||
@@ -24,17 +25,18 @@ and `nwggrid`.
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
- go 1.16 (just to build)
|
- go >=1.16 (just to build)
|
||||||
- gtk3
|
- gtk3
|
||||||
- gtk-layer-shell
|
- gtk-layer-shell
|
||||||
|
- xdg-utils
|
||||||
|
|
||||||
Optional (recommended):
|
Optional (recommended):
|
||||||
|
|
||||||
- thunar
|
- thunar
|
||||||
- alacritty
|
- alacritty
|
||||||
|
|
||||||
You may use another file manager and terminal emulator (see command line arguments), but for now the program has
|
You may use another file manager and terminal emulator (see command line arguments), but mentioned above have been
|
||||||
only been tested with the two mentioned above.
|
confirmed to work well with the program. Also see **Files** below.
|
||||||
|
|
||||||
### Steps
|
### Steps
|
||||||
|
|
||||||
@@ -63,6 +65,10 @@ Usage of nwg-drawer:
|
|||||||
Icon Size (default 64)
|
Icon Size (default 64)
|
||||||
-lang string
|
-lang string
|
||||||
force lang, e.g. "en", "pl"
|
force lang, e.g. "en", "pl"
|
||||||
|
-nocats
|
||||||
|
Disable filtering by category
|
||||||
|
-nofs
|
||||||
|
Disable file search
|
||||||
-o string
|
-o string
|
||||||
name of the Output to display the drawer on (sway only)
|
name of the Output to display the drawer on (sway only)
|
||||||
-ovl
|
-ovl
|
||||||
@@ -80,6 +86,37 @@ Usage of nwg-drawer:
|
|||||||
|
|
||||||
Edit `~/.config/nwg-panel/drawer.css` to your taste.
|
Edit `~/.config/nwg-panel/drawer.css` to your taste.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Sample file content
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"\\.pdf$": "atril",
|
||||||
|
"\\.svg$": "inkscape",
|
||||||
|
"\\.(jpg|png|tiff|gif)$": "feh",
|
||||||
|
"\\.(mp3|ogg|flac|wav|wma)$": "audacious",
|
||||||
|
"\\.(avi|mp4|mkv|mov|wav)$": "mpv",
|
||||||
|
"\\.(doc|docx|xls|xlsx)$": "libreoffice"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the **right mouse button** to open the file with your file manager (see `-fm` argument). The result depends on the
|
||||||
|
file manager you use.
|
||||||
|
|
||||||
|
- thunar will open the file location
|
||||||
|
- pcmanfm will open the file with its associated program
|
||||||
|
- caja won't open anything, except for directories
|
||||||
|
|
||||||
|
I've noy yet tried other file managers.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
This program uses some great libraries:
|
This program uses some great libraries:
|
||||||
|
|||||||
BIN
bin/nwg-drawer
BIN
bin/nwg-drawer
Binary file not shown.
23
main.go
23
main.go
@@ -19,7 +19,7 @@ import (
|
|||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.1.5"
|
const version = "0.1.8"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
appDirs []string
|
appDirs []string
|
||||||
@@ -28,6 +28,7 @@ var (
|
|||||||
pinned []string
|
pinned []string
|
||||||
src glib.SourceHandle
|
src glib.SourceHandle
|
||||||
id2entry map[string]desktopEntry
|
id2entry map[string]desktopEntry
|
||||||
|
preferredApps map[string]interface{}
|
||||||
)
|
)
|
||||||
|
|
||||||
var categoryNames = [...]string{
|
var categoryNames = [...]string{
|
||||||
@@ -188,6 +189,17 @@ func main() {
|
|||||||
|
|
||||||
status = parseDesktopFiles(desktopFiles)
|
status = parseDesktopFiles(desktopFiles)
|
||||||
|
|
||||||
|
// For opening files we use xdg-open. As its configuration is PITA, we may override some associations
|
||||||
|
// in the ~/.config/nwg-panel/preferred-apps.json file.
|
||||||
|
paFile := filepath.Join(configDirectory, "preferred-apps.json")
|
||||||
|
preferredApps, err = loadPreferredApps(paFile)
|
||||||
|
if err != nil {
|
||||||
|
println(fmt.Sprintf("Custom associations file %s not found or invalid", paFile))
|
||||||
|
} else {
|
||||||
|
println(fmt.Sprintf("Found %v associations in %s", len(preferredApps), paFile))
|
||||||
|
fmt.Println(preferredApps)
|
||||||
|
}
|
||||||
|
|
||||||
// USER INTERFACE
|
// USER INTERFACE
|
||||||
gtk.Init(nil)
|
gtk.Init(nil)
|
||||||
|
|
||||||
@@ -243,15 +255,6 @@ func main() {
|
|||||||
gtk.MainQuit()
|
gtk.MainQuit()
|
||||||
})
|
})
|
||||||
|
|
||||||
win.Connect("button-release-event", func(sw *gtk.Window, e *gdk.Event) bool {
|
|
||||||
btnEvent := gdk.EventButtonNewFromEvent(e)
|
|
||||||
if btnEvent.Button() == 1 || btnEvent.Button() == 3 {
|
|
||||||
gtk.MainQuit()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool {
|
win.Connect("key-press-event", func(window *gtk.Window, event *gdk.Event) bool {
|
||||||
key := &gdk.EventKey{Event: event}
|
key := &gdk.EventKey{Event: event}
|
||||||
switch key.KeyVal() {
|
switch key.KeyVal() {
|
||||||
|
|||||||
45
tools.go
45
tools.go
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -10,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -243,6 +246,25 @@ func getAppDirs() []string {
|
|||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadPreferredApps(path string) (map[string]interface{}, error) {
|
||||||
|
jsonFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer jsonFile.Close()
|
||||||
|
|
||||||
|
byteValue, _ := ioutil.ReadAll(jsonFile)
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
json.Unmarshal([]byte(byteValue), &result)
|
||||||
|
|
||||||
|
if len(result) == 0 {
|
||||||
|
return nil, errors.New("json invalid or empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func listFiles(dir string) ([]fs.FileInfo, error) {
|
func listFiles(dir string) ([]fs.FileInfo, error) {
|
||||||
files, err := ioutil.ReadDir(dir)
|
files, err := ioutil.ReadDir(dir)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -622,14 +644,25 @@ func launch(command string, terminal bool) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func open(filePath string) {
|
func open(filePath string, xdgOpen bool) {
|
||||||
cmd := exec.Command(*fileManager, filePath)
|
var cmd *exec.Cmd
|
||||||
go cmd.Run()
|
if xdgOpen {
|
||||||
|
cmd = exec.Command("xdg-open", filePath)
|
||||||
|
// Look for possible custom file association
|
||||||
|
for key, element := range preferredApps {
|
||||||
|
r, err := regexp.Compile(key)
|
||||||
|
if err == nil && r.MatchString(filePath) {
|
||||||
|
cmd = exec.Command(fmt.Sprintf("%v", element), filePath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command(*fileManager, filePath)
|
||||||
|
}
|
||||||
|
fmt.Printf("Executing: %s", cmd)
|
||||||
|
cmd.Start()
|
||||||
|
|
||||||
glib.TimeoutAdd(uint(150), func() bool {
|
|
||||||
gtk.MainQuit()
|
gtk.MainQuit()
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns map output name -> gdk.Monitor
|
// Returns map output name -> gdk.Monitor
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ func setUpCategoriesButtonBox() *gtk.EventBox {
|
|||||||
w := b.GetAllocatedWidth()
|
w := b.GetAllocatedWidth()
|
||||||
b.SetImagePosition(gtk.POS_TOP)
|
b.SetImagePosition(gtk.POS_TOP)
|
||||||
b.SetSizeRequest(w, 0)
|
b.SetSizeRequest(w, 0)
|
||||||
|
if fileSearchResultWrapper != nil {
|
||||||
fileSearchResultWrapper.Hide()
|
fileSearchResultWrapper.Hide()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +389,8 @@ func searchUserDir(dir string) {
|
|||||||
}
|
}
|
||||||
fileSearchResultFlowBox.Hide()
|
fileSearchResultFlowBox.Hide()
|
||||||
|
|
||||||
statusLabel.SetText(fmt.Sprintf("%v results", fileSearchResultFlowBox.GetChildren().Length()))
|
statusLabel.SetText(fmt.Sprintf("%v results | LMB: xdg-open | RMB: file manager",
|
||||||
|
fileSearchResultFlowBox.GetChildren().Length()))
|
||||||
num := uint(fileSearchResultFlowBox.GetChildren().Length() / *fsColumns)
|
num := uint(fileSearchResultFlowBox.GetChildren().Length() / *fsColumns)
|
||||||
fileSearchResultFlowBox.SetMinChildrenPerLine(num + 1)
|
fileSearchResultFlowBox.SetMinChildrenPerLine(num + 1)
|
||||||
fileSearchResultFlowBox.SetMaxChildrenPerLine(num + 1)
|
fileSearchResultFlowBox.SetMaxChildrenPerLine(num + 1)
|
||||||
@@ -414,8 +417,16 @@ func setUpUserDirButton(iconName, displayName, entryName string, userDirsMap map
|
|||||||
}
|
}
|
||||||
button.SetLabel(displayName)
|
button.SetLabel(displayName)
|
||||||
|
|
||||||
button.Connect("clicked", func() {
|
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
|
||||||
launch(fmt.Sprintf("%s %s", *fileManager, userDirsMap[entryName]), false)
|
btnEvent := gdk.EventButtonNewFromEvent(e)
|
||||||
|
if btnEvent.Button() == 1 {
|
||||||
|
open(userDirsMap[entryName], true)
|
||||||
|
return true
|
||||||
|
} else if btnEvent.Button() == 3 {
|
||||||
|
open(userDirsMap[entryName], false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
box.PackStart(button, false, true, 0)
|
box.PackStart(button, false, true, 0)
|
||||||
@@ -443,8 +454,16 @@ func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box {
|
|||||||
button.SetTooltipText(tooltipText)
|
button.SetTooltipText(tooltipText)
|
||||||
}
|
}
|
||||||
|
|
||||||
button.Connect("clicked", func() {
|
button.Connect("button-release-event", func(btn *gtk.Button, e *gdk.Event) bool {
|
||||||
open(filePath)
|
btnEvent := gdk.EventButtonNewFromEvent(e)
|
||||||
|
if btnEvent.Button() == 1 {
|
||||||
|
open(filePath, true)
|
||||||
|
return true
|
||||||
|
} else if btnEvent.Button() == 3 {
|
||||||
|
open(filePath, false)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
box.PackStart(button, false, true, 0)
|
box.PackStart(button, false, true, 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user