parent
c1cfe822b0
commit
3a492a6414
|
@ -1,3 +1,6 @@
|
|||
0.2.1:
|
||||
- Minor GTK interface updates
|
||||
|
||||
0.2.0:
|
||||
- Added GTK interface
|
||||
- Added Support for link (URL shortcut) desktop entries
|
||||
|
|
|
@ -31,6 +31,7 @@ var (
|
|||
execLabel *gtk.Label
|
||||
)
|
||||
|
||||
// TODO: Load list item icon on show
|
||||
func initList(container *gtk.Box) {
|
||||
inputView = newTextView()
|
||||
setNoExpand(&inputView.Widget)
|
||||
|
@ -62,13 +63,10 @@ func initList(container *gtk.Box) {
|
|||
listBox.SetHExpand(false)
|
||||
listBox.SetFocusOnClick(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)
|
||||
}
|
||||
_, err = listBox.Connect("row-activated", func(_ *gtk.ListBox, _ *gtk.ListBoxRow) {
|
||||
err := listSelect(inputView)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -88,19 +86,23 @@ func initList(container *gtk.Box) {
|
|||
|
||||
lastEntry := len(gmenu.FilteredEntries) - 1
|
||||
|
||||
for i, entry := range gmenu.FilteredEntries {
|
||||
var entry *gmenu.ListEntry
|
||||
for i, label := range gmenu.Names {
|
||||
// Capture variables
|
||||
i := i
|
||||
entry := entry
|
||||
|
||||
row, err := gtk.ListBoxRowNew()
|
||||
if err != nil {
|
||||
log.Fatal("failed to create ListBoxRow:", err)
|
||||
}
|
||||
|
||||
row.SetFocusOnClick(false)
|
||||
if i == lastEntry {
|
||||
entry = &gmenu.ListEntry{Entry: nil, Label: ""}
|
||||
} else {
|
||||
row.SetName("#" + strconv.Itoa(i))
|
||||
|
||||
row.SetName("#" + strconv.Itoa(i))
|
||||
entry = &gmenu.ListEntry{Entry: gmenu.Entries[i], Label: label}
|
||||
}
|
||||
|
||||
container := newBox(gtk.ORIENTATION_HORIZONTAL)
|
||||
row.Add(container)
|
||||
|
@ -111,6 +113,7 @@ func initList(container *gtk.Box) {
|
|||
}
|
||||
|
||||
listBox.SetSortFunc(listSort, 0)
|
||||
listBox.SetFilterFunc(listFilter, 0)
|
||||
|
||||
_, err = buffer.Connect("changed", func(tb *gtk.TextBuffer) bool {
|
||||
gmenu.SetInput(strings.TrimSpace(textViewText(inputView)))
|
||||
|
@ -137,17 +140,20 @@ func initRow(container *gtk.Box, entry *gmenu.ListEntry, i int, lastEntry int) {
|
|||
labelContainer := newBox(gtk.ORIENTATION_VERTICAL)
|
||||
labelContainer.SetMarginStart(labelMarginStart)
|
||||
|
||||
l, err := gtk.LabelNew(fmt.Sprintf("<b>%s</b>", html.EscapeString(entry.Label)))
|
||||
labelText := "Shell command"
|
||||
if entry != nil && entry.Entry != nil {
|
||||
labelText = entry.Label
|
||||
}
|
||||
|
||||
l, err := gtk.LabelNew(fmt.Sprintf("<b>%s</b>", html.EscapeString(labelText)))
|
||||
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)
|
||||
initLabel(l)
|
||||
//glib.IdleAdd(initLabel, l)
|
||||
|
||||
if !config.HideAppIcons && !config.HideAppDetails {
|
||||
l.SetMarginTop(labelMarginTop)
|
||||
|
@ -173,11 +179,9 @@ func initRow(container *gtk.Box, entry *gmenu.ListEntry, i int, lastEntry int) {
|
|||
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)
|
||||
initLabel(l)
|
||||
//glib.IdleAdd(initLabel, l) // TODO Faster?
|
||||
|
||||
if !config.HideAppIcons {
|
||||
l.SetMarginTop(labelMarginTopComment)
|
||||
|
@ -196,6 +200,13 @@ func initRow(container *gtk.Box, entry *gmenu.ListEntry, i int, lastEntry int) {
|
|||
setNoExpand(&container.Widget)
|
||||
}
|
||||
|
||||
func initLabel(l *gtk.Label) {
|
||||
l.SetHAlign(gtk.ALIGN_START)
|
||||
l.SetLineWrap(false)
|
||||
l.SetSingleLineMode(true)
|
||||
l.SetEllipsize(pango.ELLIPSIZE_END)
|
||||
}
|
||||
|
||||
func loadIconImage(img *gtk.Image, entry *gmenu.ListEntry) {
|
||||
var (
|
||||
pbuf *gdk.Pixbuf
|
||||
|
@ -218,14 +229,21 @@ func loadIconImage(img *gtk.Image, entry *gmenu.ListEntry) {
|
|||
img.SetMarginEnd(iconMargin)
|
||||
img.SetMarginBottom(iconMargin)
|
||||
}
|
||||
|
||||
func updateList(input string) {
|
||||
listBox.InvalidateFilter()
|
||||
listBox.InvalidateSort()
|
||||
|
||||
execLabel.SetText(input)
|
||||
|
||||
row := listBox.GetRowAtIndex(0)
|
||||
if row == nil {
|
||||
return
|
||||
var row *gtk.ListBoxRow
|
||||
for i := 0; i < len(gmenu.Names); i++ {
|
||||
row = listBox.GetRowAtIndex(i)
|
||||
if row == nil {
|
||||
return
|
||||
} else if row.IsVisible() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
listBox.SelectRow(row)
|
||||
|
@ -323,11 +341,32 @@ func listSelect(_ *gtk.TextView) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func listSort(row1 *gtk.ListBoxRow, row2 *gtk.ListBoxRow, userData uintptr) int {
|
||||
func listFilter(row *gtk.ListBoxRow, _ uintptr) bool {
|
||||
match := gmenu.MatchEntry(rowID(row))
|
||||
|
||||
row.SetSelectable(match)
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func listSort(row1 *gtk.ListBoxRow, row2 *gtk.ListBoxRow, _ uintptr) int {
|
||||
r1 := rowID(row1)
|
||||
r2 := rowID(row2)
|
||||
|
||||
if r1 < 0 || r2 < 0 {
|
||||
return 0
|
||||
if r1 < 0 {
|
||||
if gmenu.MatchEntry(r2) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
} else { // r2 < 0
|
||||
if gmenu.MatchEntry(r1) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if gmenu.Sort(r1, r2) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~tslocum/gmenu/pkg/gmenu"
|
||||
"github.com/gotk3/gotk3/gdk"
|
||||
|
@ -31,6 +32,7 @@ var (
|
|||
listBox *gtk.ListBox
|
||||
inputView *gtk.TextView
|
||||
|
||||
t = time.Now()
|
||||
loaded = make(chan bool)
|
||||
)
|
||||
|
||||
|
@ -44,35 +46,9 @@ func init() {
|
|||
flag.BoolVar(&config.HideAppIcons, "no-icons", false, "hide application icons")
|
||||
}
|
||||
|
||||
func main() {
|
||||
func load() {
|
||||
flag.Parse()
|
||||
|
||||
go load()
|
||||
|
||||
application, err := gtk.ApplicationNew(appID, glib.APPLICATION_HANDLES_COMMAND_LINE)
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application:", err)
|
||||
}
|
||||
|
||||
_, 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)
|
||||
}
|
||||
|
||||
os.Exit(application.Run(os.Args))
|
||||
}
|
||||
|
||||
func load() {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -84,23 +60,72 @@ func load() {
|
|||
}
|
||||
|
||||
gmenu.LoadEntries(&config.Config)
|
||||
gmenu.FilterEntries()
|
||||
|
||||
for i, l := range gmenu.Names {
|
||||
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: l, Entry: gmenu.Entries[i]})
|
||||
}
|
||||
|
||||
gmenu.Entries = append(gmenu.Entries, nil)
|
||||
gmenu.Names = append(gmenu.Names, "")
|
||||
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: "", Entry: nil})
|
||||
|
||||
loaded <- true
|
||||
}
|
||||
|
||||
func onActivate(application *gtk.Application) {
|
||||
w, err := gtk.ApplicationWindowNew(application)
|
||||
func setupKeyBindings(w *gtk.Window) {
|
||||
_, err := w.Connect("key-press-event", handleKeybinding)
|
||||
if err != nil {
|
||||
log.Fatal("failed to connect key-press-event:", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application window:", err)
|
||||
}
|
||||
_, err = w.Connect("destroy", func() {
|
||||
func handleKeybinding(_ *gtk.Window, 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
|
||||
}
|
||||
|
||||
func main() {
|
||||
go load()
|
||||
|
||||
gtk.Init(nil)
|
||||
|
||||
w, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application window:", err)
|
||||
}
|
||||
|
||||
w.SetTitle("gmenu")
|
||||
|
||||
w.SetDecorated(false)
|
||||
|
@ -117,6 +142,13 @@ func onActivate(application *gtk.Application) {
|
|||
w.Fullscreen()
|
||||
}
|
||||
|
||||
_, err = w.Connect("destroy", func() {
|
||||
os.Exit(0)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("failed to create application window:", err)
|
||||
}
|
||||
|
||||
<-loaded
|
||||
|
||||
go gmenu.HandleInput(func(input string) {
|
||||
|
@ -126,55 +158,15 @@ func onActivate(application *gtk.Application) {
|
|||
}
|
||||
})
|
||||
|
||||
gmenu.Entries = append(gmenu.Entries, nil)
|
||||
gmenu.Names = append(gmenu.Names, "")
|
||||
gmenu.FilteredEntries = append(gmenu.FilteredEntries, &gmenu.ListEntry{Label: "Shell command", Entry: nil})
|
||||
|
||||
container := newBox(gtk.ORIENTATION_VERTICAL)
|
||||
|
||||
initList(container)
|
||||
|
||||
w.Add(container)
|
||||
|
||||
_, 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:
|
||||
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)
|
||||
}
|
||||
setupKeyBindings(w)
|
||||
|
||||
w.ShowAll()
|
||||
|
||||
gtk.Main()
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -4,7 +4,7 @@ go 1.12
|
|||
|
||||
require (
|
||||
git.sr.ht/~tslocum/desktop v0.1.1
|
||||
github.com/gotk3/gotk3 v0.0.0-20190809225113-dc58eba1cccc
|
||||
github.com/gotk3/gotk3 v0.0.0-20190827191254-95d4bac6fe1b
|
||||
github.com/jroimartin/gocui v0.4.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/lithammer/fuzzysearch v1.0.2
|
||||
|
@ -12,4 +12,5 @@ require (
|
|||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 // indirect
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,7 +1,7 @@
|
|||
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-20190809225113-dc58eba1cccc h1:QtXtC6AdJ57L/rw/YMF41a+6YmLTy92IvxsCjDp4dYE=
|
||||
github.com/gotk3/gotk3 v0.0.0-20190809225113-dc58eba1cccc/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo=
|
||||
github.com/gotk3/gotk3 v0.0.0-20190827191254-95d4bac6fe1b h1:/ExhbPkho7qhFp96P5JZq5GB2x3ApecVIy0pqPoF0DQ=
|
||||
github.com/gotk3/gotk3 v0.0.0-20190827191254-95d4bac6fe1b/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=
|
||||
|
@ -18,3 +18,5 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
|
@ -80,6 +80,19 @@ func CloseInput() {
|
|||
<-inputFlushed
|
||||
}
|
||||
|
||||
func MatchEntry(i int) bool {
|
||||
if i == -1 {
|
||||
return true
|
||||
}
|
||||
|
||||
b := strings.ToLower(input)
|
||||
if b == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return fuzzy.MatchFold(b, Names[i])
|
||||
}
|
||||
|
||||
func FilterEntries() {
|
||||
FilteredEntries = nil
|
||||
if input == "" {
|
||||
|
|
Loading…
Reference in New Issue