gtkmenu: Add icon support
This commit is contained in:
parent
5fdce165ba
commit
1bb42b05cc
12 changed files with 505 additions and 42 deletions
|
@ -2,11 +2,13 @@ arch: amd64
|
|||
environment:
|
||||
PROJECT_DIR: '~/go/src/git.sr.ht/~tslocum'
|
||||
PROJECT_NAME: 'gmenu'
|
||||
CGO_ENABLED: '0'
|
||||
GO111MODULE: 'on'
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- pkgconf
|
||||
- go
|
||||
- gtk3
|
||||
- glib
|
||||
sources:
|
||||
- https://git.sr.ht/~tslocum/gmenu
|
||||
tasks:
|
||||
|
@ -14,8 +16,11 @@ tasks:
|
|||
mkdir -p $PROJECT_DIR
|
||||
mv $PROJECT_NAME $PROJECT_DIR/$PROJECT_NAME
|
||||
- test: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gmenu
|
||||
cd $PROJECT_DIR/$PROJECT_NAME
|
||||
go test -v -cover ./...
|
||||
- build: |
|
||||
- build-gmenu: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gmenu
|
||||
go build
|
||||
- build-gtkmenu: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gtkmenu
|
||||
go build
|
||||
|
|
|
@ -2,11 +2,13 @@ arch: x86_64
|
|||
environment:
|
||||
PROJECT_DIR: '~/go/src/git.sr.ht/~tslocum'
|
||||
PROJECT_NAME: 'gmenu'
|
||||
CGO_ENABLED: '0'
|
||||
GO111MODULE: 'on'
|
||||
image: alpine/edge
|
||||
packages:
|
||||
- go
|
||||
- glib
|
||||
- glib-dev
|
||||
- gtk+3.0-dev
|
||||
sources:
|
||||
- https://git.sr.ht/~tslocum/gmenu
|
||||
tasks:
|
||||
|
@ -14,8 +16,11 @@ tasks:
|
|||
mkdir -p $PROJECT_DIR
|
||||
mv $PROJECT_NAME $PROJECT_DIR/$PROJECT_NAME
|
||||
- test: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gmenu
|
||||
cd $PROJECT_DIR/$PROJECT_NAME
|
||||
go test -v -cover ./...
|
||||
- build: |
|
||||
- build-gmenu: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gmenu
|
||||
go build
|
||||
- build-gtkmenu: |
|
||||
cd $PROJECT_DIR/$PROJECT_NAME/cmd/gtkmenu
|
||||
go build
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@ dist/
|
|||
*.sh
|
||||
gmenu
|
||||
!cmd/gmenu/
|
||||
!pkg/gmenu/
|
||||
gtkmenu
|
||||
!cmd/gtkmenu/
|
||||
vendor/
|
||||
|
|
|
@ -1,2 +1,9 @@
|
|||
0.2.0:
|
||||
- Added GTK interface
|
||||
- Added Support for link (URL shortcut) desktop entries
|
||||
|
||||
0.1.1:
|
||||
- Added fuzzy string search
|
||||
|
||||
0.1.0:
|
||||
- Initial release
|
||||
|
|
22
README.md
22
README.md
|
@ -10,13 +10,13 @@ Desktop application launcher
|
|||
### Console
|
||||
|
||||
```
|
||||
go get git.sr.ht/~tslocum/gmenu/cmd/gmenu
|
||||
GO111MODULE=on go get git.sr.ht/~tslocum/gmenu/cmd/gmenu
|
||||
```
|
||||
|
||||
### GUI
|
||||
|
||||
```
|
||||
go get git.sr.ht/~tslocum/gmenu/cmd/gtkmenu
|
||||
GO111MODULE=on go get git.sr.ht/~tslocum/gmenu/cmd/gtkmenu
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
@ -25,6 +25,8 @@ go get git.sr.ht/~tslocum/gmenu/cmd/gtkmenu
|
|||
|
||||
```
|
||||
Usage of ./gmenu:
|
||||
-browser string
|
||||
browser command
|
||||
-data-dirs string
|
||||
application data directories (default: $XDG_DATA_DIRS)
|
||||
-mouse
|
||||
|
@ -32,19 +34,31 @@ Usage of ./gmenu:
|
|||
-no-details
|
||||
hide application details
|
||||
-no-generic
|
||||
hide generic names
|
||||
hide application generic names
|
||||
-terminal string
|
||||
terminal command
|
||||
```
|
||||
|
||||
### GUI
|
||||
|
||||
```
|
||||
Usage of ./gtkmenu:
|
||||
-browser string
|
||||
browser command
|
||||
-data-dirs string
|
||||
application data directories (default: $XDG_DATA_DIRS)
|
||||
-height int
|
||||
window height (default 200)
|
||||
-no-details
|
||||
hide application details
|
||||
-no-generic
|
||||
hide generic names
|
||||
hide application generic names
|
||||
-no-icons
|
||||
hide application icons
|
||||
-resizable
|
||||
allow window to be resized
|
||||
-terminal string
|
||||
terminal command
|
||||
-width int
|
||||
window width (default 800)
|
||||
```
|
||||
|
|
|
@ -2,24 +2,38 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"html"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gotk3/gotk3/pango"
|
||||
|
||||
"git.sr.ht/~tslocum/desktop"
|
||||
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
|
||||
"github.com/gotk3/gotk3/gdk"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/tslocum/gotk3/gdk"
|
||||
"github.com/tslocum/gotk3/gtk"
|
||||
gtkfork "github.com/tslocum/gotk3/gtk"
|
||||
)
|
||||
|
||||
const (
|
||||
iconSize = 48
|
||||
iconMargin = 4
|
||||
iconMarginStart = 2
|
||||
labelMarginStart = 4
|
||||
labelMarginTop = 8
|
||||
labelMarginTopComment = 4
|
||||
)
|
||||
|
||||
var execLabel *gtk.Label
|
||||
|
||||
func initList(container *gtk.Box) {
|
||||
inputView = newTextView()
|
||||
inputView.SetHExpand(false)
|
||||
inputView.SetVExpand(false)
|
||||
setNoExpand(&inputView.Widget)
|
||||
inputView.SetProperty("accepts-tab", false)
|
||||
inputView.SetProperty("wrap-mode", gtk.WRAP_CHAR)
|
||||
inputView.SetProperty("cursor-visible", false)
|
||||
|
@ -47,7 +61,7 @@ func initList(container *gtk.Box) {
|
|||
listBox.SetSelectionMode(gtk.SELECTION_BROWSE)
|
||||
listBox.SetHExpand(false)
|
||||
|
||||
_, err = listBox.Connect("button-press-event", func(listBox *gtk.ListBox, ev *gdk.Event) {
|
||||
_, err = listBox.Connect("button-press-event", func(listBox *gtkfork.ListBox, ev *gdk.Event) {
|
||||
mouseEvent := &gdk.EventButton{ev}
|
||||
if mouseEvent.Type() == gdk.EVENT_2BUTTON_PRESS {
|
||||
err := listSelect(inputView)
|
||||
|
@ -71,20 +85,118 @@ func initList(container *gtk.Box) {
|
|||
log.Fatal("failed to create ListBoxRow:", err)
|
||||
}
|
||||
|
||||
l, err := gtk.LabelNew(entry.Label)
|
||||
row.SetName("#" + strconv.Itoa(i))
|
||||
|
||||
container := newBox(gtk.ORIENTATION_HORIZONTAL)
|
||||
|
||||
if !config.HideAppIcons {
|
||||
s, _ := container.GetScreen()
|
||||
theme, err := gtk.IconThemeGetForScreen(*s)
|
||||
if err != nil {
|
||||
log.Fatal("failed to get icon theme:", err)
|
||||
}
|
||||
|
||||
var (
|
||||
pbuf *gdk.Pixbuf
|
||||
img *gtk.Image
|
||||
)
|
||||
|
||||
if entry.Entry != nil && entry.Icon != "" {
|
||||
if path.IsAbs(entry.Icon) {
|
||||
pbuf, err = gdk.PixbufNewFromFileAtSize(entry.Icon, iconSize, iconSize)
|
||||
} else {
|
||||
pbuf, err = theme.LoadIcon(entry.Icon, iconSize, gtk.ICON_LOOKUP_USE_BUILTIN)
|
||||
}
|
||||
}
|
||||
if pbuf == nil || err != nil {
|
||||
var icon string
|
||||
if entry.Entry == nil {
|
||||
icon = "utilities-terminal"
|
||||
} else if entry.Type == desktop.Application {
|
||||
icon = "application-x-executable"
|
||||
} else {
|
||||
icon = "text-html"
|
||||
}
|
||||
pbuf, err = theme.LoadIcon(icon, iconSize, gtk.ICON_LOOKUP_USE_BUILTIN)
|
||||
}
|
||||
if err != nil {
|
||||
// Failed to load icon
|
||||
|
||||
img, err = gtk.ImageNew()
|
||||
if err == nil {
|
||||
img.SetSizeRequest(iconSize, iconSize)
|
||||
}
|
||||
} else {
|
||||
if pbuf.GetWidth() != iconSize && pbuf.GetHeight() != iconSize {
|
||||
pbuf, _ = pbuf.ScaleSimple(iconSize, iconSize, gdk.INTERP_BILINEAR)
|
||||
}
|
||||
|
||||
img, err = gtk.ImageNewFromPixbuf(pbuf)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal("failed to create Icon:", err)
|
||||
}
|
||||
|
||||
img.SetMarginStart(iconMarginStart)
|
||||
img.SetMarginTop(iconMargin)
|
||||
img.SetMarginEnd(iconMargin)
|
||||
img.SetMarginBottom(iconMargin)
|
||||
container.PackStart(img, false, false, 0)
|
||||
}
|
||||
|
||||
labelContainer := newBox(gtk.ORIENTATION_VERTICAL)
|
||||
labelContainer.SetMarginStart(labelMarginStart)
|
||||
|
||||
l, err := gtk.LabelNew(fmt.Sprintf("<b>%s</b>", html.EscapeString(entry.Label)))
|
||||
if err != nil {
|
||||
log.Fatal("failed to create Label:", err)
|
||||
}
|
||||
l.SetUseMarkup(true)
|
||||
|
||||
l.SetHAlign(gtk.ALIGN_START)
|
||||
setNoExpand(&l.Widget)
|
||||
l.SetLineWrap(false)
|
||||
l.SetSingleLineMode(true)
|
||||
l.SetEllipsize(pango.ELLIPSIZE_END)
|
||||
|
||||
row.SetName("#" + strconv.Itoa(i))
|
||||
|
||||
if i == lastEntry {
|
||||
execLabel = l
|
||||
if !config.HideAppIcons {
|
||||
l.SetMarginTop(labelMarginTop)
|
||||
}
|
||||
|
||||
row.Add(l)
|
||||
labelContainer.PackStart(l, false, false, 0)
|
||||
|
||||
if entry.Entry == nil || (entry.Entry != nil && entry.Comment != "") {
|
||||
comment := ""
|
||||
if entry.Entry != nil {
|
||||
comment = entry.Comment
|
||||
}
|
||||
l, err := gtk.LabelNew(comment)
|
||||
if err != nil {
|
||||
log.Fatal("failed to create Label:", err)
|
||||
}
|
||||
|
||||
l.SetHAlign(gtk.ALIGN_START)
|
||||
setNoExpand(&l.Widget)
|
||||
l.SetLineWrap(false)
|
||||
l.SetSingleLineMode(true)
|
||||
l.SetEllipsize(pango.ELLIPSIZE_END)
|
||||
|
||||
if !config.HideAppIcons {
|
||||
l.SetMarginTop(labelMarginTopComment)
|
||||
}
|
||||
|
||||
labelContainer.Add(l)
|
||||
|
||||
if i == lastEntry {
|
||||
execLabel = l
|
||||
}
|
||||
}
|
||||
|
||||
setNoExpand(&labelContainer.Widget)
|
||||
container.Add(labelContainer)
|
||||
|
||||
setNoExpand(&container.Widget)
|
||||
row.Add(container)
|
||||
listBox.Add(row)
|
||||
}
|
||||
|
||||
|
@ -160,6 +272,11 @@ func selectedIndex() int {
|
|||
return rowID(listBox.GetSelectedRow())
|
||||
}
|
||||
|
||||
func setNoExpand(v *gtk.Widget) {
|
||||
v.SetHExpand(false)
|
||||
v.SetVExpand(false)
|
||||
}
|
||||
|
||||
func selectedEntry() *desktop.Entry {
|
||||
i := selectedIndex()
|
||||
if len(gmenu.FilteredEntries) == 0 || i < 0 || i > len(gmenu.FilteredEntries)-1 {
|
||||
|
|
|
@ -19,6 +19,9 @@ type Config struct {
|
|||
gmenu.Config
|
||||
|
||||
Width, Height int
|
||||
Resizable bool
|
||||
|
||||
HideAppIcons bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -32,17 +35,28 @@ func init() {
|
|||
|
||||
flag.IntVar(&config.Width, "width", 800, "window width")
|
||||
flag.IntVar(&config.Height, "height", 200, "window height")
|
||||
flag.BoolVar(&config.Resizable, "resizable", false, "allow window to be resized")
|
||||
flag.BoolVar(&config.HideAppIcons, "no-icons", false, "hide application icons")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
application, err := gtk.ApplicationNew(appID, glib.APPLICATION_FLAGS_NONE)
|
||||
application, err := gtk.ApplicationNew(appID, glib.APPLICATION_HANDLES_COMMAND_LINE)
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application:", err)
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
_, err = application.Connect("activate", func() { onActivate(application) })
|
||||
_, err = application.Connect("command-line", func() {
|
||||
flag.Parse()
|
||||
|
||||
onActivate(application)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("failed to connect while creating application:", err)
|
||||
}
|
||||
_, err = application.Connect("activate", func() {
|
||||
onActivate(application)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("failed to connect while creating application:", err)
|
||||
}
|
||||
|
@ -51,17 +65,18 @@ func main() {
|
|||
}
|
||||
|
||||
func onActivate(application *gtk.Application) {
|
||||
appWindow, err := gtk.ApplicationWindowNew(application)
|
||||
w, err := gtk.ApplicationWindowNew(application)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application window:", err)
|
||||
}
|
||||
|
||||
_, err = appWindow.Connect("destroy", func() {
|
||||
_, err = w.Connect("destroy", func() {
|
||||
os.Exit(0)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application window:", err)
|
||||
}
|
||||
w.SetTitle("gmenu")
|
||||
|
||||
gmenu.LoadEntries(&config.Config)
|
||||
gmenu.FilterEntries()
|
||||
|
@ -75,28 +90,23 @@ func onActivate(application *gtk.Application) {
|
|||
|
||||
gmenu.Entries = append(gmenu.Entries, nil)
|
||||
gmenu.Names = append(gmenu.Names, "")
|
||||
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: "", Entry: nil})
|
||||
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: "Shell command", Entry: nil})
|
||||
|
||||
container := newBox(gtk.ORIENTATION_VERTICAL)
|
||||
initList(container)
|
||||
|
||||
appWindow.Add(container)
|
||||
w.Add(container)
|
||||
|
||||
appWindow.SetTitle("gmenu")
|
||||
w.SetResizable(config.Resizable)
|
||||
w.SetSizeRequest(config.Width, config.Height)
|
||||
w.SetPosition(gtk.WIN_POS_CENTER)
|
||||
|
||||
appWindow.SetResizable(false)
|
||||
appWindow.SetSizeRequest(config.Width, config.Height)
|
||||
appWindow.SetDefaultSize(config.Width, config.Height)
|
||||
appWindow.SetPosition(gtk.WIN_POS_CENTER)
|
||||
w.SetDecorated(false)
|
||||
w.SetBorderWidth(0)
|
||||
w.Stick()
|
||||
w.SetKeepAbove(true)
|
||||
|
||||
appWindow.SetDecorated(false)
|
||||
appWindow.SetBorderWidth(0)
|
||||
appWindow.Stick()
|
||||
appWindow.SetKeepAbove(true)
|
||||
|
||||
appWindow.ShowAll()
|
||||
|
||||
_, err = appWindow.Connect("key-press-event", func(win *gtk.ApplicationWindow, ev *gdk.Event) bool {
|
||||
_, err = w.Connect("key-press-event", func(win *gtk.ApplicationWindow, ev *gdk.Event) bool {
|
||||
keyEvent := &gdk.EventKey{ev}
|
||||
switch keyEvent.KeyVal() {
|
||||
case gdk.KEY_Up, gdk.KEY_Down:
|
||||
|
@ -135,4 +145,6 @@ func onActivate(application *gtk.Application) {
|
|||
if err != nil {
|
||||
log.Fatal("failed to connect key-press-event:", err)
|
||||
}
|
||||
|
||||
w.ShowAll()
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ builds:
|
|||
ldflags:
|
||||
- -s -w -X git.sr.ht/~tslocum/gmenu/pkg/gmenu.Version={{.Version}}
|
||||
goos:
|
||||
- darwin
|
||||
- freebsd
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
|
|
29
pkg/gmenu/config.go
Normal file
29
pkg/gmenu/config.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package gmenu
|
||||
|
||||
type Config struct {
|
||||
DataDirs string
|
||||
|
||||
HideGenericNames bool
|
||||
HideAppDetails bool
|
||||
|
||||
terminalCommand string
|
||||
browserCommand string
|
||||
}
|
||||
|
||||
func (c *Config) TerminalCommand() string {
|
||||
if c.terminalCommand == "" {
|
||||
c.terminalCommand = "i3-sensible-terminal"
|
||||
}
|
||||
|
||||
return c.terminalCommand
|
||||
}
|
||||
|
||||
func (c *Config) BrowserCommand() string {
|
||||
if c.browserCommand == "" {
|
||||
c.browserCommand = "xdg-open"
|
||||
}
|
||||
|
||||
return c.browserCommand
|
||||
}
|
||||
|
||||
var Version string
|
219
pkg/gmenu/gmenu.go
Normal file
219
pkg/gmenu/gmenu.go
Normal file
|
@ -0,0 +1,219 @@
|
|||
package gmenu
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.sr.ht/~tslocum/desktop"
|
||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
Entries []*desktop.Entry
|
||||
Names []string
|
||||
|
||||
FilteredEntries []*ListEntry
|
||||
|
||||
inputBuffer = make(chan string, 3)
|
||||
input string
|
||||
inputFlushed = make(chan bool)
|
||||
)
|
||||
|
||||
type ListEntry struct {
|
||||
*desktop.Entry
|
||||
|
||||
Label string
|
||||
}
|
||||
|
||||
type InputUpdateHandler func(input string)
|
||||
|
||||
func SharedInit(c *Config) {
|
||||
log.SetFlags(0)
|
||||
|
||||
flag.StringVar(&c.DataDirs, "data-dirs", "", "application data directories (default: $XDG_DATA_DIRS)")
|
||||
flag.BoolVar(&c.HideGenericNames, "no-generic", false, "hide application generic names")
|
||||
flag.BoolVar(&c.HideAppDetails, "no-details", false, "hide application details")
|
||||
flag.StringVar(&c.terminalCommand, "terminal", "", "terminal command")
|
||||
flag.StringVar(&c.browserCommand, "browser", "", "browser command")
|
||||
}
|
||||
|
||||
func HandleInput(u InputUpdateHandler) {
|
||||
var (
|
||||
in string
|
||||
ok bool
|
||||
)
|
||||
|
||||
inputLoop:
|
||||
for {
|
||||
select {
|
||||
case in, ok = <-inputBuffer:
|
||||
if !ok {
|
||||
break inputLoop
|
||||
}
|
||||
|
||||
input = in
|
||||
u(input)
|
||||
}
|
||||
}
|
||||
|
||||
inputFlushed <- true
|
||||
}
|
||||
|
||||
func LoadEntries(c *Config) {
|
||||
var err error
|
||||
Entries, Names, err = DesktopEntries(c)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func SetInput(i string) {
|
||||
inputBuffer <- i
|
||||
}
|
||||
|
||||
func CloseInput() {
|
||||
close(inputBuffer)
|
||||
<-inputFlushed
|
||||
}
|
||||
|
||||
func FilterEntries() {
|
||||
FilteredEntries = nil
|
||||
if input == "" {
|
||||
for i, l := range Names {
|
||||
FilteredEntries = append(FilteredEntries, &ListEntry{Label: l, Entry: Entries[i]})
|
||||
}
|
||||
|
||||
sort.Slice(FilteredEntries, SortEmpty)
|
||||
} else {
|
||||
b := strings.ToLower(input)
|
||||
|
||||
matches := fuzzy.RankFindFold(b, Names)
|
||||
sort.Sort(matches)
|
||||
|
||||
for _, match := range matches {
|
||||
FilteredEntries = append(FilteredEntries, &ListEntry{Label: Names[match.OriginalIndex], Entry: Entries[match.OriginalIndex]})
|
||||
}
|
||||
|
||||
sort.Slice(FilteredEntries, SortFiltered)
|
||||
}
|
||||
}
|
||||
|
||||
func DesktopEntries(c *Config) ([]*desktop.Entry, []string, error) {
|
||||
var dirs []string
|
||||
if c.DataDirs != "" {
|
||||
dirs = strings.Split(c.DataDirs, ":")
|
||||
} else {
|
||||
dirs = desktop.DataDirs()
|
||||
}
|
||||
|
||||
allEntries, err := desktop.Scan(dirs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
desktopEntries []*desktop.Entry
|
||||
desktopNames []string
|
||||
)
|
||||
|
||||
for _, entries := range allEntries {
|
||||
for _, entry := range entries {
|
||||
switch entry.Type {
|
||||
case desktop.Application:
|
||||
if entry.Exec == "" {
|
||||
continue
|
||||
}
|
||||
case desktop.Link:
|
||||
if entry.URL == "" {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
continue // Unsupported entry type
|
||||
}
|
||||
|
||||
if entry.Name != "" {
|
||||
desktopEntries = append(desktopEntries, entry)
|
||||
desktopNames = append(desktopNames, entry.Name)
|
||||
}
|
||||
if !c.HideGenericNames && entry.GenericName != "" {
|
||||
desktopEntries = append(desktopEntries, entry)
|
||||
desktopNames = append(desktopNames, entry.GenericName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return desktopEntries, desktopNames, nil
|
||||
}
|
||||
|
||||
func Sort(i, j int) bool {
|
||||
if input == "" {
|
||||
return SortEmpty(i, j)
|
||||
}
|
||||
|
||||
return SortFiltered(i, j)
|
||||
}
|
||||
|
||||
func SortEmpty(i, j int) bool {
|
||||
ilower := strings.ToLower(FilteredEntries[i].Label)
|
||||
jlower := strings.ToLower(FilteredEntries[j].Label)
|
||||
|
||||
if FilteredEntries[i].Entry == nil && FilteredEntries[j].Entry != nil {
|
||||
return true
|
||||
} else if ilower != jlower {
|
||||
return ilower < jlower
|
||||
} else {
|
||||
return i < j
|
||||
}
|
||||
}
|
||||
|
||||
func SortFiltered(i, j int) bool {
|
||||
ilower := strings.ToLower(FilteredEntries[i].Label)
|
||||
if FilteredEntries[i].Entry == nil {
|
||||
ilower = ""
|
||||
}
|
||||
|
||||
jlower := strings.ToLower(FilteredEntries[j].Label)
|
||||
if FilteredEntries[j].Entry == nil {
|
||||
jlower = ""
|
||||
}
|
||||
|
||||
ipre := strings.HasPrefix(ilower, input)
|
||||
jpre := strings.HasPrefix(jlower, input)
|
||||
|
||||
icon := strings.Contains(ilower, input)
|
||||
jcon := strings.Contains(jlower, input)
|
||||
|
||||
imatch := fuzzy.MatchFold(input, ilower)
|
||||
jmatch := fuzzy.MatchFold(input, jlower)
|
||||
|
||||
if ipre != jpre {
|
||||
return ipre && !jpre
|
||||
} else if icon != jcon {
|
||||
return icon && !jcon
|
||||
} else if imatch != jmatch {
|
||||
return imatch && !jmatch
|
||||
} else if (FilteredEntries[i].Entry == nil) != (FilteredEntries[j].Entry == nil) {
|
||||
return FilteredEntries[i].Entry == nil
|
||||
} else if ilower != jlower {
|
||||
return ilower < jlower
|
||||
} else {
|
||||
return i < j
|
||||
}
|
||||
}
|
||||
|
||||
func Run(config *Config, execute string, path string, runInTerminal bool, waitUntilFinished bool) error {
|
||||
execute = strings.TrimSpace(execute)
|
||||
|
||||
fmt.Println(execute)
|
||||
|
||||
runScript, err := desktop.RunScript(execute)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create run script")
|
||||
}
|
||||
|
||||
return run(config, runScript, path, waitUntilFinished, runInTerminal)
|
||||
}
|
44
pkg/gmenu/run_unix.go
Normal file
44
pkg/gmenu/run_unix.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package gmenu
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func run(config *Config, runScript string, path string, waitUntilFinished, runInTerminal bool) error {
|
||||
var cmd *exec.Cmd
|
||||
if runInTerminal {
|
||||
cmd = exec.Command(config.TerminalCommand(), "-e", runScript)
|
||||
} else {
|
||||
cmd = exec.Command("/usr/bin/env", "bash", "-c", runScript)
|
||||
}
|
||||
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}
|
||||
cmd.Dir = path
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start command")
|
||||
}
|
||||
|
||||
if !waitUntilFinished {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
_, isExitErr := err.(*exec.ExitError)
|
||||
if err != nil && !isExitErr {
|
||||
return errors.Wrap(err, "failed to execute command")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
8
pkg/gmenu/run_windows.go
Normal file
8
pkg/gmenu/run_windows.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
// +build windows
|
||||
|
||||
package gmenu
|
||||
|
||||
func run(config *Config, runScript string, path string, waitUntilFinished, runInTerminal bool) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue