Add GTK interface

This commit is contained in:
Trevor Slocum 2019-07-27 11:09:28 -07:00
parent 7190fbb773
commit 5fdce165ba
13 changed files with 563 additions and 269 deletions

View File

@ -5,25 +5,61 @@
Desktop application launcher
## Warning: Experimental
Only a terminal interface has been implemented. A GTK interface is planned.
## Installation
```go get git.sr.ht/~tslocum/gmenu/cmd/gmenu```
## Usage
### GUI
*Coming soon*
### Console
sway/i3 + alacritty:
```
go get git.sr.ht/~tslocum/gmenu/cmd/gmenu
```
### GUI
```
go get git.sr.ht/~tslocum/gmenu/cmd/gtkmenu
```
## Usage
### Console
```
Usage of ./gmenu:
-data-dirs string
application data directories (default: $XDG_DATA_DIRS)
-mouse
enable mouse support
-no-details
hide application details
-no-generic
hide generic names
```
### GUI
```
Usage of ./gtkmenu:
-data-dirs string
application data directories (default: $XDG_DATA_DIRS)
-height int
window height (default 200)
-no-generic
hide generic names
-width int
window width (default 800)
```
## Usage example - [sway](https://swaywm.org)/[i3](https://i3wm.org) + [alacritty](https://github.com/jwilm/alacritty)
### Console
```
bindsym $mod+d exec --no-startup-id alacritty --class gmenu --title gmenu --working-directory ~ -e gmenu
for_window [app_id="gmenu"] floating enable; resize set 745 105
```
### GUI
```
bindsym $mod+d exec --no-startup-id gtkmenu
```

View File

@ -4,34 +4,35 @@ import (
"github.com/jroimartin/gocui"
)
func initGUI() (*gocui.Gui, error) {
g, err := gocui.NewGui(gocui.OutputNormal)
func initGUI() error {
var err error
gui, err = gocui.NewGui(gocui.OutputNormal)
if err != nil {
return nil, err
return err
}
g.InputEsc = true
g.Cursor = true
g.Mouse = !disableMouseSupport
gui.InputEsc = true
gui.Cursor = true
gui.Mouse = config.EnableMouse
g.SetManagerFunc(layout)
gui.SetManagerFunc(layout)
if err := keybindings(g); err != nil {
return nil, err
if err := keybindings(); err != nil {
return err
}
go updateEntryInfo()
return g, nil
return nil
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
func layout(_ *gocui.Gui) error {
maxX, maxY := gui.Size()
listWidth := maxX
if !hideAppDetails {
if !config.HideAppDetails {
listWidth = maxX / 2
if v, err := g.SetView("ex", maxX/2, -1, maxX, 1); err != nil {
if v, err := gui.SetView("ex", maxX/2, -1, maxX, 1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@ -41,7 +42,7 @@ func layout(g *gocui.Gui) error {
v.Frame = false
v.Wrap = false
}
if v, err := g.SetView("comment", maxX/2, 1, maxX, maxY); err != nil {
if v, err := gui.SetView("comment", maxX/2, 1, maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@ -52,7 +53,7 @@ func layout(g *gocui.Gui) error {
v.Wrap = true
}
}
if v, err := g.SetView("list", -1, 0, listWidth, maxY); err != nil {
if v, err := gui.SetView("list", -1, 0, listWidth, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@ -64,10 +65,8 @@ func layout(g *gocui.Gui) error {
v.Highlight = true
v.SelBgColor = gocui.ColorGreen
v.SelFgColor = gocui.ColorBlack
updateEntries("")
}
if v, err := g.SetView("main", -1, -1, listWidth, 1); err != nil {
if v, err := gui.SetView("main", -1, -1, listWidth, 1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@ -80,7 +79,7 @@ func layout(g *gocui.Gui) error {
v.Wrap = true
v.Editor = gocui.EditorFunc(searchEditor)
if _, err := g.SetCurrentView("main"); err != nil {
if _, err := gui.SetCurrentView("main"); err != nil {
return err
}
}
@ -97,12 +96,12 @@ func closeGUI() {
gui.Close()
gui.Update(func(g *gocui.Gui) error {
gui.Update(func(_ *gocui.Gui) error {
return gocui.ErrQuit
})
}
func quit(g *gocui.Gui, v *gocui.View) error {
func quit(_ *gocui.Gui, _ *gocui.View) error {
closeGUI()
return gocui.ErrQuit
}

View File

@ -2,7 +2,9 @@ package main
import (
"log"
"strings"
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
"github.com/jroimartin/gocui"
)
@ -31,30 +33,61 @@ func searchEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
v.MoveCursor(1, 0, false)
}
updateEntries(v.Buffer())
gmenu.SetInput(strings.TrimSpace(v.Buffer()))
}
func listPrev(g *gocui.Gui, v *gocui.View) error {
func listPrevPage(_ *gocui.Gui, _ *gocui.View) error {
_, size := list.Size()
_, o := list.Origin()
if o == 0 {
return list.SetCursor(0, 0)
}
o -= size - 2
if o < 0 {
o = 0
}
return list.SetOrigin(0, o)
}
func listNextPage(_ *gocui.Gui, _ *gocui.View) error {
numEntries := len(gmenu.FilteredEntries)
_, size := list.Size()
_, o := list.Origin()
if o >= numEntries-size {
return list.SetCursor(0, size-1)
}
o += size - 2
if o > numEntries-size {
o = numEntries - size
}
return list.SetOrigin(0, o)
}
func listPrevEntry(_ *gocui.Gui, _ *gocui.View) error {
list.MoveCursor(0, -1, false)
updateEntryInfo()
return nil
}
func listNext(g *gocui.Gui, v *gocui.View) error {
func listNextEntry(_ *gocui.Gui, _ *gocui.View) error {
list.MoveCursor(0, 1, false)
updateEntryInfo()
return nil
}
func listClickFromMouse(g *gocui.Gui, v *gocui.View) error {
func listClickFromMouse(_ *gocui.Gui, _ *gocui.View) error {
clickedList = true
return nil
}
func listSelectFromMouse(g *gocui.Gui, v *gocui.View) error {
func listSelectFromMouse(_ *gocui.Gui, _ *gocui.View) error {
if !clickedList {
return nil
}
@ -62,46 +95,54 @@ func listSelectFromMouse(g *gocui.Gui, v *gocui.View) error {
return listSelect()
}
func listDeselectFromMouse(g *gocui.Gui, v *gocui.View) error {
func listDeselectFromMouse(_ *gocui.Gui, _ *gocui.View) error {
clickedList = false
return nil
}
func listSelectFromKey(g *gocui.Gui, v *gocui.View) error {
func listSelectFromKey(_ *gocui.Gui, _ *gocui.View) error {
return listSelect()
}
func keybindings(g *gocui.Gui) error {
if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, listPrev); err != nil {
func keybindings() error {
if err := gui.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, listPrevPage); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, listNext); err != nil {
if err := gui.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, listNextPage); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, listSelectFromKey); err != nil {
if err := gui.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, listPrevEntry); err != nil {
return err
}
if err := g.SetKeybinding("list", gocui.MouseLeft, gocui.ModNone, listClickFromMouse); err != nil {
if err := gui.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, listNextEntry); err != nil {
return err
}
if err := g.SetKeybinding("list", gocui.MouseRelease, gocui.ModNone, listSelectFromMouse); err != nil {
if err := gui.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, listSelectFromKey); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, listDeselectFromMouse); err != nil {
if err := gui.SetKeybinding("list", gocui.MouseLeft, gocui.ModNone, listClickFromMouse); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
if err := gui.SetKeybinding("list", gocui.MouseRelease, gocui.ModNone, listSelectFromMouse); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyEsc, gocui.ModNone, quit); err != nil {
if err := gui.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, listDeselectFromMouse); err != nil {
return err
}
if err := gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := gui.SetKeybinding("", gocui.KeyEsc, gocui.ModNone, quit); err != nil {
return err
}

View File

@ -3,95 +3,40 @@ package main
import (
"flag"
"fmt"
"log"
"sort"
"strings"
"time"
"git.sr.ht/~tslocum/desktop"
"git.sr.ht/~tslocum/gmenu/pkg/config"
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
"github.com/jroimartin/gocui"
"github.com/kballard/go-shellquote"
"github.com/lithammer/fuzzysearch/fuzzy"
"github.com/pkg/errors"
)
type listEntry struct {
*desktop.Entry
func updateEntries(input string) {
gui.Update(func(_ *gocui.Gui) error {
gmenu.FilterEntries()
originalIndex int
label string
}
list.Clear()
list.SetOrigin(0, 0)
list.SetCursor(0, 0)
defer updateEntryInfo()
func updateEntries(buf string) {
buf = strings.TrimSpace(buf)
emptyBuf := buf == ""
filteredEntries = nil
if emptyBuf {
for i, l := range desktopNames {
filteredEntries = append(filteredEntries, &listEntry{originalIndex: i, label: l, Entry: desktopEntries[i]})
}
sort.Slice(filteredEntries, func(i, j int) bool {
ilower := strings.ToLower(filteredEntries[i].label)
jlower := strings.ToLower(filteredEntries[j].label)
if ilower != jlower {
return ilower < jlower
} else {
return i < j
var printedEntry bool
for _, entry := range gmenu.FilteredEntries {
if printedEntry {
fmt.Fprint(list, "\n")
}
})
} else {
b := strings.ToLower(buf)
matches := fuzzy.RankFindFold(b, desktopNames)
sort.Sort(matches)
for i, match := range matches {
filteredEntries = append(filteredEntries, &listEntry{originalIndex: i, label: desktopNames[match.OriginalIndex], Entry: desktopEntries[match.OriginalIndex]})
fmt.Fprint(list, entry.Label)
printedEntry = true
}
sort.Slice(filteredEntries, func(i, j int) bool {
ilower := strings.ToLower(filteredEntries[i].label)
jlower := strings.ToLower(filteredEntries[j].label)
ipre := strings.HasPrefix(ilower, b)
jpre := strings.HasPrefix(jlower, b)
icon := strings.Contains(ilower, b)
jcon := strings.Contains(jlower, b)
if ipre != jpre {
return ipre && !jpre
} else if icon != jcon {
return icon && !jcon
} else if ilower != jlower {
return ilower < jlower
} else {
return i < j
}
})
}
list.Clear()
list.SetOrigin(0, 0)
list.SetCursor(0, 0)
defer updateEntryInfo()
var printedEntry bool
for _, entry := range filteredEntries {
if printedEntry {
fmt.Fprint(list, "\n")
if printedEntry && input != "" {
fmt.Fprint(list, "\n"+input)
}
fmt.Fprint(list, entry.label)
printedEntry = true
}
if printedEntry && !emptyBuf {
fmt.Fprint(list, "\n"+buf)
}
return nil
})
}
func selectedIndex() int {
@ -111,15 +56,15 @@ func selectedEntry() *desktop.Entry {
}
i := selectedIndex()
if len(filteredEntries) == 0 || i < 0 || i > len(filteredEntries)-1 {
if len(gmenu.FilteredEntries) == 0 || i < 0 || i > len(gmenu.FilteredEntries)-1 {
return nil
}
return filteredEntries[i].Entry
return gmenu.FilteredEntries[i].Entry
}
func updateEntryInfo() {
if hideAppDetails {
if config.HideAppDetails {
return
}
@ -137,7 +82,7 @@ func updateEntryInfo() {
if entry.Type == desktop.Application {
exLine = entry.Exec
} else { // Type == desktop.Link
exLine = config.BrowserCommand + " " + entry.URL
exLine = config.BrowserCommand() + " " + entry.URL
}
comLine = entry.Comment
@ -172,23 +117,20 @@ func listSelect() error {
runInTerminal = entry.Terminal
execute = entry.ExpandExec(shellquote.Join(flag.Args()...))
} else { // Type == desktop.Link
execute = shellquote.Join(config.BrowserCommand, entry.URL)
execute = shellquote.Join(config.BrowserCommand(), entry.URL)
}
execute = strings.TrimSpace(execute)
closeGUI()
log.Println(execute)
runScript, err := desktop.RunScript(execute)
if err != nil {
return errors.Wrap(err, "failed to create run script")
}
path := ""
if entry != nil {
path = entry.Path
}
return run(runScript, path, waitUntilFinished, runInTerminal)
err := gmenu.Run(&config.Config, execute, path, runInTerminal, waitUntilFinished)
if err == nil {
err = gocui.ErrQuit
}
return err
}

View File

@ -1,43 +1,36 @@
// gmenu - Desktop application launcher
// https://git.sr.ht/~tslocum/gmenu
package main
import (
"flag"
"log"
"os"
"strings"
"git.sr.ht/~tslocum/desktop"
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
"github.com/jroimartin/gocui"
"github.com/mattn/go-isatty"
)
var (
dataDirs string
disableMouseSupport bool
hideAppDetails bool
hideGenericNames bool
type Config struct {
gmenu.Config
desktopEntries []*desktop.Entry
desktopNames []string
filteredEntries []*listEntry
EnableMouse bool
}
var (
config = &Config{}
gui *gocui.Gui
input, comment, ex, list *gocui.View
clickedList bool
closedGUI bool
done = make(chan bool)
closedGUI bool
done = make(chan bool)
)
func init() {
log.SetFlags(0)
gmenu.SharedInit(&config.Config)
flag.StringVar(&dataDirs, "data-dirs", "", "application data directories (default: $XDG_DATA_DIRS)")
flag.BoolVar(&disableMouseSupport, "no-mouse", false, "disable mouse support")
flag.BoolVar(&hideAppDetails, "no-details", false, "hide application details")
flag.BoolVar(&hideGenericNames, "no-generic", false, "hide generic names")
flag.BoolVar(&config.EnableMouse, "mouse", false, "enable mouse support")
}
func main() {
@ -48,48 +41,15 @@ func main() {
log.Fatal("failed to start gmenu: non-interactive terminals are not supported")
}
var dirs []string
if dataDirs != "" {
dirs = strings.Split(dataDirs, ":")
} else {
dirs = desktop.DataDirs()
}
gmenu.LoadEntries(&config.Config)
allEntries, err := desktop.Scan(dirs)
err := initGUI()
if err != nil {
log.Fatal(err)
}
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 !hideGenericNames && entry.GenericName != "" {
desktopEntries = append(desktopEntries, entry)
desktopNames = append(desktopNames, entry.GenericName)
}
}
}
gui, err = initGUI()
if err != nil {
log.Fatal(err)
}
go gmenu.HandleInput(updateEntries)
gmenu.SetInput("")
go func() {
if err := gui.MainLoop(); err != nil && err != gocui.ErrQuit {

View File

@ -1,46 +0,0 @@
// +build linux
package main
import (
"os"
"os/exec"
"syscall"
"git.sr.ht/~tslocum/gmenu/pkg/config"
"github.com/jroimartin/gocui"
"github.com/pkg/errors"
)
func run(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 gocui.ErrQuit
}
err = cmd.Wait()
_, isExitErr := err.(*exec.ExitError)
if err != nil && !isExitErr {
return errors.Wrap(err, "failed to execute command")
}
return gocui.ErrQuit
}

View File

@ -1,8 +0,0 @@
// +build windows
package main
func run(runScript string, waitUntilFinished, runInTerminal bool) error {
// TODO
return nil
}

235
cmd/gtkmenu/gui_list.go Normal file
View File

@ -0,0 +1,235 @@
package main
import (
"flag"
"log"
"os"
"strconv"
"strings"
"git.sr.ht/~tslocum/desktop"
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
"github.com/kballard/go-shellquote"
"github.com/tslocum/gotk3/gdk"
"github.com/tslocum/gotk3/gtk"
)
var execLabel *gtk.Label
func initList(container *gtk.Box) {
inputView = newTextView()
inputView.SetHExpand(false)
inputView.SetVExpand(false)
inputView.SetProperty("accepts-tab", false)
inputView.SetProperty("wrap-mode", gtk.WRAP_CHAR)
inputView.SetProperty("cursor-visible", false)
inputView.SetProperty("left-margin", 2)
inputView.SetProperty("right-margin", 2)
buffer, err := inputView.GetBuffer()
if err != nil {
log.Fatal("failed to create ListBox:", err)
}
container.Add(inputView)
listScroll, err := gtk.ScrolledWindowNew(nil, nil)
if err != nil {
log.Fatal("failed to create ListBox:", err)
}
listScroll.SetHExpand(true)
listScroll.SetVExpand(false)
listBox, err = gtk.ListBoxNew()
if err != nil {
log.Fatal("failed to create ListBox:", err)
}
listBox.SetSelectionMode(gtk.SELECTION_BROWSE)
listBox.SetHExpand(false)
_, err = listBox.Connect("button-press-event", func(listBox *gtk.ListBox, ev *gdk.Event) {
mouseEvent := &gdk.EventButton{ev}
if mouseEvent.Type() == gdk.EVENT_2BUTTON_PRESS {
err := listSelect(inputView)
if err != nil {
log.Fatal(err)
}
}
})
if err != nil {
log.Fatal("failed to create ListBox:", err)
}
listScroll.Add(listBox)
container.PackEnd(listScroll, true, true, 0)
lastEntry := len(gmenu.FilteredEntries) - 1
for i, entry := range gmenu.FilteredEntries {
row, err := gtk.ListBoxRowNew()
if err != nil {
log.Fatal("failed to create ListBoxRow:", err)
}
l, err := gtk.LabelNew(entry.Label)
if err != nil {
log.Fatal("failed to create Label:", err)
}
l.SetHAlign(gtk.ALIGN_START)
row.SetName("#" + strconv.Itoa(i))
if i == lastEntry {
execLabel = l
}
row.Add(l)
listBox.Add(row)
}
listBox.SetSortFunc(listSort, 0)
_, err = buffer.Connect("changed", func(tb *gtk.TextBuffer) bool {
gmenu.SetInput(strings.TrimSpace(textViewText(inputView)))
return false
})
if err != nil {
log.Fatal("failed to create ListBox:", err)
}
gmenu.SetInput("")
}
func updateList(input string) {
listBox.InvalidateSort()
execLabel.SetText(input)
row := listBox.GetRowAtIndex(0)
if row == nil {
return
}
listBox.SelectRow(row)
inputView.GrabFocus()
}
func newBox(orient gtk.Orientation) *gtk.Box {
box, err := gtk.BoxNew(orient, 0)
if err != nil {
log.Fatal("Unable to create box:", err)
}
return box
}
func newTextView() *gtk.TextView {
tv, err := gtk.TextViewNew()
if err != nil {
log.Fatal("Unable to create TextView:", err)
}
return tv
}
func textViewBuffer(tv *gtk.TextView) *gtk.TextBuffer {
buffer, err := tv.GetBuffer()
if err != nil {
log.Fatal("Unable to get TextView buffer:", err)
}
return buffer
}
func textViewText(tv *gtk.TextView) string {
buffer := textViewBuffer(tv)
start, end := buffer.GetBounds()
text, err := buffer.GetText(start, end, true)
if err != nil {
log.Fatal("Unable to get TextView text:", err)
}
return text
}
func selectedIndex() int {
if listBox == nil {
return -1
}
return rowID(listBox.GetSelectedRow())
}
func selectedEntry() *desktop.Entry {
i := selectedIndex()
if len(gmenu.FilteredEntries) == 0 || i < 0 || i > len(gmenu.FilteredEntries)-1 {
return nil
}
return gmenu.FilteredEntries[i].Entry
}
func listSelect(_ *gtk.TextView) error {
gmenu.CloseInput()
var (
execute string
runInTerminal bool
waitUntilFinished bool
)
entry := selectedEntry()
if entry == nil {
waitUntilFinished = true
execute = textViewText(inputView)
} else if entry.Type == desktop.Application {
runInTerminal = entry.Terminal
execute = entry.ExpandExec(shellquote.Join(flag.Args()...))
} else { // Type == desktop.Link
execute = shellquote.Join(config.BrowserCommand(), entry.URL)
}
path := ""
if entry != nil {
path = entry.Path
}
err := gmenu.Run(&config.Config, execute, path, runInTerminal, waitUntilFinished)
if err != nil {
return err
}
os.Exit(0)
return nil
}
func listSort(row1 *gtk.ListBoxRow, row2 *gtk.ListBoxRow, userData uintptr) int {
r1 := rowID(row1)
r2 := rowID(row2)
if r1 < 0 || r2 < 0 {
return 0
}
if gmenu.Sort(r1, r2) {
return -1
}
return 1
}
func rowID(row *gtk.ListBoxRow) int {
if row == nil {
return -1
}
name, err := row.GetName()
if err != nil || len(name) < 2 || name[0] != '#' {
return -1
}
id, err := strconv.Atoi(name[1:])
if err != nil {
return -1
}
return id
}

View File

@ -1,3 +1,138 @@
package gtkmenu
package main
func main() {}
import (
"flag"
"log"
"os"
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
"github.com/gotk3/gotk3/glib"
"github.com/tslocum/gotk3/gdk"
"github.com/tslocum/gotk3/gtk"
)
const (
appID = "space.rocketnine.gmenu"
)
type Config struct {
gmenu.Config
Width, Height int
}
var (
config = &Config{}
listBox *gtk.ListBox
inputView *gtk.TextView
)
func init() {
gmenu.SharedInit(&config.Config)
flag.IntVar(&config.Width, "width", 800, "window width")
flag.IntVar(&config.Height, "height", 200, "window height")
}
func main() {
flag.Parse()
application, err := gtk.ApplicationNew(appID, glib.APPLICATION_FLAGS_NONE)
if err != nil {
log.Fatal("failed to create application:", err)
}
_, err = application.Connect("activate", func() { onActivate(application) })
if err != nil {
log.Fatal("failed to connect while creating application:", err)
}
os.Exit(application.Run(os.Args))
}
func onActivate(application *gtk.Application) {
appWindow, err := gtk.ApplicationWindowNew(application)
if err != nil {
log.Fatal("failed to create application window:", err)
}
_, err = appWindow.Connect("destroy", func() {
os.Exit(0)
})
if err != nil {
log.Fatal("failed to create application window:", err)
}
gmenu.LoadEntries(&config.Config)
gmenu.FilterEntries()
go gmenu.HandleInput(func(input string) {
_, err := glib.IdleAdd(updateList, input)
if err != nil {
log.Fatal(err)
}
})
gmenu.Entries = append(gmenu.Entries, nil)
gmenu.Names = append(gmenu.Names, "")
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: "", Entry: nil})
container := newBox(gtk.ORIENTATION_VERTICAL)
initList(container)
appWindow.Add(container)
appWindow.SetTitle("gmenu")
appWindow.SetResizable(false)
appWindow.SetSizeRequest(config.Width, config.Height)
appWindow.SetDefaultSize(config.Width, config.Height)
appWindow.SetPosition(gtk.WIN_POS_CENTER)
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 {
keyEvent := &gdk.EventKey{ev}
switch keyEvent.KeyVal() {
case gdk.KEY_Up, gdk.KEY_Down:
offset := -1
if keyEvent.KeyVal() == gdk.KEY_Down {
offset = 1
}
index := 0
row := listBox.GetSelectedRow()
if row != nil {
index = row.GetIndex()
}
row = listBox.GetRowAtIndex(index + offset)
if row != nil {
listBox.SelectRow(row)
row.GrabFocus()
inputView.GrabFocus()
}
return true
case gdk.KEY_Return:
err = listSelect(inputView)
if err != nil {
log.Fatal(err)
}
return true
case gdk.KEY_Escape:
os.Exit(0)
}
return false
})
if err != nil {
log.Fatal("failed to connect key-press-event:", err)
}
}

2
go.mod
View File

@ -4,6 +4,7 @@ go 1.12
require (
git.sr.ht/~tslocum/desktop v0.1.1
github.com/gotk3/gotk3 v0.0.0-20190620081259-6dcdf9e5c51e
github.com/jroimartin/gocui v0.4.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/lithammer/fuzzysearch v1.0.2
@ -11,5 +12,6 @@ require (
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/nsf/termbox-go v0.0.0-20190624072549-eeb6cd0a1762 // indirect
github.com/pkg/errors v0.8.1
github.com/tslocum/gotk3 v0.0.0-20190727120037-6cb68ea19890
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
)

7
go.sum
View File

@ -1,7 +1,7 @@
git.sr.ht/~tslocum/desktop v0.1.0 h1:cfOcbsBA/XOkemwRjqZ9N5oeYGnGlnC6Vq976aLrrS8=
git.sr.ht/~tslocum/desktop v0.1.0/go.mod h1:cUn0Q8ALjkAq40qSei795yN3CfO5pkeYKo2gmzaZ2SI=
git.sr.ht/~tslocum/desktop v0.1.1 h1:hS1DgT1Ur0DR42Z4vr+Zsasjjd8M9PVwIEmeAd1xLS4=
git.sr.ht/~tslocum/desktop v0.1.1/go.mod h1:cUn0Q8ALjkAq40qSei795yN3CfO5pkeYKo2gmzaZ2SI=
github.com/gotk3/gotk3 v0.0.0-20190620081259-6dcdf9e5c51e h1:KFy3swDjmbaSAE6b1iExIgsYt0OkfoLP3HjLm4ifSR8=
github.com/gotk3/gotk3 v0.0.0-20190620081259-6dcdf9e5c51e/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@ -16,6 +16,9 @@ github.com/nsf/termbox-go v0.0.0-20190624072549-eeb6cd0a1762 h1:44Lv0bNi88GweB54
github.com/nsf/termbox-go v0.0.0-20190624072549-eeb6cd0a1762/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/tslocum/gotk3 v0.0.0-20190727120037-6cb68ea19890 h1:E4ShJr9mkfrOgv2gJfLhuc8s1uFfWsTG24h2JDIjrVs=
github.com/tslocum/gotk3 v0.0.0-20190727120037-6cb68ea19890/go.mod h1:lKW1BMxjgJ9vFqOZ2pNvC6p6ln6en3KqvV4KRt5FLB4=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -4,29 +4,31 @@ before:
hooks:
- go mod download
builds:
- binary: gmenu
-
id: gmenu
binary: gmenu
main: ./cmd/gmenu
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X git.sr.ht/~tslocum/gmenu/pkg/config.Version={{.Version}}
- -s -w -X git.sr.ht/~tslocum/gmenu/pkg/gmenu.Version={{.Version}}
goos:
- linux
- windows
goarch:
- 386
- amd64
- binary: gtkmenu
-
id: gtkmenu
binary: gtkmenu
main: ./cmd/gtkmenu
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X git.sr.ht/~tslocum/gmenu/pkg/config.Version={{.Version}}
- -s -w -X git.sr.ht/~tslocum/gmenu/pkg/gmenu.Version={{.Version}}
goos:
- linux
- windows
# - windows
goarch:
- 386
# - 386
- amd64
archive:
replacements:

View File

@ -1,7 +0,0 @@
package config
var (
Version string
TerminalCommand = "i3-sensible-terminal"
BrowserCommand = "xdg-open"
)