diff --git a/bin/nwg-drawer b/bin/nwg-drawer index 046e5a3..587eab1 100755 Binary files a/bin/nwg-drawer and b/bin/nwg-drawer differ diff --git a/drawer.css b/drawer.css index 874514e..c9d70d0 100644 --- a/drawer.css +++ b/drawer.css @@ -1,5 +1,5 @@ window { - background-color: rgba (36, 47, 79, 0.92); + background-color: rgba (36, 47, 79, 0.9); color: #eeeeee } @@ -19,4 +19,9 @@ button { button:hover { background-color: rgba (255, 255, 255, 0.1) -} \ No newline at end of file +} + +#pinned-box { + padding-bottom: 5px; + border-bottom: 1px dotted gray +} diff --git a/main.go b/main.go index b2d4648..26bb005 100644 --- a/main.go +++ b/main.go @@ -88,7 +88,8 @@ var ( fileSearchResults []string searchEntry *gtk.SearchEntry phrase string - fileSearchResultListBox *gtk.ListBox + fileSearchResultListBox *gtk.ListBox // tbd + fileSearchResultFlowBox *gtk.FlowBox buttonsWrapper *gtk.Box buttonBox *gtk.EventBox confirmationBox *gtk.Box @@ -115,6 +116,7 @@ var itemPadding = flag.Uint("padding", 2, "vertical item padding") var lang = flag.String("lang", "", "force lang, e.g. \"en\", \"pl\"") var fileManager = flag.String("fm", "thunar", "File Manager") var term = flag.String("term", "alacritty", "Terminal emulator") +var nameLimit = flag.Int("l", 90, "file name length Limit") func main() { timeStart := time.Now() @@ -238,7 +240,7 @@ func main() { gtk.MainQuit() }) - win.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) { + win.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) bool { key := &gdk.EventKey{Event: event} if key.KeyVal() == gdk.KEY_Escape { s, _ := searchEntry.GetText() @@ -248,7 +250,10 @@ func main() { } else { gtk.MainQuit() } + } else { + return false } + return false }) // Close the window on leave, but not immediately, to avoid accidental closes @@ -277,7 +282,7 @@ func main() { categoriesWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) categoriesButtonBox := setUpCategoriesButtonBox() categoriesWrapper.PackStart(categoriesButtonBox, true, false, 0) - outerVBox.PackStart(categoriesWrapper, false, false, 10) + outerVBox.PackStart(categoriesWrapper, false, false, 0) pinnedWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) outerVBox.PackStart(pinnedWrapper, false, false, 0) @@ -287,7 +292,7 @@ func main() { pinnedFlowBox = setUpPinnedFlowBox() resultWindow, _ = gtk.ScrolledWindowNew(nil, nil) - resultWindow.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + resultWindow.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) resultWindow.Connect("enter-notify-event", func() { cancelClose() }) diff --git a/uicomponents.go b/uicomponents.go index e1756d1..e934306 100644 --- a/uicomponents.go +++ b/uicomponents.go @@ -21,10 +21,11 @@ func setUpPinnedFlowBox() *gtk.FlowBox { flowBox.SetMaxChildrenPerLine(uint(len(pinned))) } - //flowBox.SetMinChildrenPerLine(9) flowBox.SetColumnSpacing(20) flowBox.SetHomogeneous(true) flowBox.SetRowSpacing(20) + flowBox.SetProperty("name", "pinned-box") + flowBox.SetSelectionMode(gtk.SELECTION_NONE) if len(pinned) > 0 { for _, desktopID := range pinned { @@ -63,6 +64,9 @@ func setUpPinnedFlowBox() *gtk.FlowBox { } return false }) + btn.Connect("activate", func() { + launch(entry.Exec, entry.Terminal) + }) flowBox.Add(btn) } @@ -73,7 +77,10 @@ func setUpPinnedFlowBox() *gtk.FlowBox { }) pinnedFlowBoxWrapper.PackStart(flowBox, true, false, 0) - flowBox.ShowAll() + //While moving focus with arrow keys we want buttons to get focus directly + flowBox.GetChildren().Foreach(func(item interface{}) { + item.(*gtk.Widget).SetCanFocus(false) + }) return flowBox } @@ -99,7 +106,6 @@ func setUpCategoriesButtonBox() *gtk.EventBox { eventBox.Add(hBox) button, _ := gtk.ButtonNewWithLabel("All") button.Connect("clicked", func(item *gtk.Button) { - searchEntry.GrabFocus() searchEntry.SetText("") appFlowBox = setUpAppsFlowBox(nil, "") for _, btn := range catButtons { @@ -118,7 +124,6 @@ func setUpCategoriesButtonBox() *gtk.EventBox { name := cat.Name b := *button button.Connect("clicked", func(item *gtk.Button) { - searchEntry.GrabFocus() searchEntry.SetText("") // !!! since gotk3 FlowBox type does not implement set_filter_func, we need to rebuild appFlowBox appFlowBox = setUpAppsFlowBox(lists[name], "") @@ -171,6 +176,8 @@ func setUpAppsFlowBox(categoryList []string, searchPhrase string) *gtk.FlowBox { flowBox.SetColumnSpacing(20) flowBox.SetRowSpacing(20) flowBox.SetHomogeneous(true) + flowBox.SetSelectionMode(gtk.SELECTION_NONE) + for _, entry := range desktopEntries { if searchPhrase == "" { if !entry.NoDisplay { @@ -197,6 +204,10 @@ func setUpAppsFlowBox(categoryList []string, searchPhrase string) *gtk.FlowBox { hWrapper, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) appSearchResultWrapper.PackStart(hWrapper, false, 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() return flowBox @@ -231,6 +242,9 @@ func flowBoxButton(entry desktopEntry) *gtk.Button { } return false }) + button.Connect("activate", func() { + launch(exec, terminal) + }) button.Connect("enter-notify-event", func() { statusLabel.SetText(desc) }) @@ -240,18 +254,19 @@ func flowBoxButton(entry desktopEntry) *gtk.Button { return button } -func setUpFileSearchResult() *gtk.ListBox { - if fileSearchResultListBox != nil { - fileSearchResultListBox.Destroy() +func setUpFileSearchResult() *gtk.FlowBox { + if fileSearchResultFlowBox != nil { + fileSearchResultFlowBox.Destroy() } - listBox, _ := gtk.ListBoxNew() - listBox.Connect("enter-notify-event", func() { + flowBox, _ := gtk.FlowBoxNew() + flowBox.SetProperty("orientation", gtk.ORIENTATION_VERTICAL) + flowBox.Connect("enter-notify-event", func() { cancelClose() }) - fileSearchResultWrapper.PackStart(listBox, false, false, 10) - listBox.ShowAll() + fileSearchResultWrapper.PackStart(flowBox, false, false, 10) + flowBox.ShowAll() // TODO: check if necessary here - return listBox + return flowBox } func walk(path string, d fs.DirEntry, e error) error { @@ -270,6 +285,7 @@ func walk(path string, d fs.DirEntry, e error) error { func setUpSearchEntry() *gtk.SearchEntry { searchEntry, _ := gtk.SearchEntryNew() + searchEntry.SetPlaceholderText("Type to search") searchEntry.Connect("enter-notify-event", func() { cancelClose() }) @@ -285,31 +301,30 @@ func setUpSearchEntry() *gtk.SearchEntry { appFlowBox = setUpAppsFlowBox(nil, phrase) if len(phrase) > 2 { - if fileSearchResultListBox != nil { - fileSearchResultListBox.Destroy() + if fileSearchResultFlowBox != nil { + fileSearchResultFlowBox.Destroy() } - fileSearchResultListBox = setUpFileSearchResult() + fileSearchResultFlowBox = setUpFileSearchResult() for key := range userDirsMap { if key != "home" { fileSearchResults = nil if len(fileSearchResults) == 0 { - fileSearchResultListBox.Show() + fileSearchResultFlowBox.Show() } - filepath.WalkDir(userDirsMap[key], walk) searchUserDir(key) } } - if fileSearchResultListBox.GetChildren().Length() == 0 { - fileSearchResultListBox.Hide() + if fileSearchResultFlowBox.GetChildren().Length() == 0 { + fileSearchResultFlowBox.Hide() } } else { - if fileSearchResultListBox != nil { - fileSearchResultListBox.Destroy() + if fileSearchResultFlowBox != nil { + fileSearchResultFlowBox.Destroy() } } } else { - if fileSearchResultListBox != nil { - fileSearchResultListBox.Destroy() + if fileSearchResultFlowBox != nil { + fileSearchResultFlowBox.Destroy() } appFlowBox = setUpAppsFlowBox(nil, "") } @@ -324,90 +339,73 @@ func setUpSearchEntry() *gtk.SearchEntry { func searchUserDir(dir string) { fileSearchResults = nil filepath.WalkDir(userDirsMap[dir], walk) - if fileSearchResults != nil && len(fileSearchResults) > 0 { - row := setUpUserDirsListRow(fmt.Sprintf("folder-%s", dir), "", dir, userDirsMap) - fileSearchResultListBox.Add(row) - fileSearchResultListBox.ShowAll() + + if fileSearchResults != nil && len(fileSearchResults) > 2 { + btn := setUpUserDirButton(fmt.Sprintf("folder-%s", dir), "", dir, userDirsMap) + fileSearchResultFlowBox.Add(btn) for _, path := range fileSearchResults { partOfPathToShow := strings.Split(path, userDirsMap[dir])[1] if partOfPathToShow != "" { - row := setUpUserFileSearchResultRow(partOfPathToShow, path) - fileSearchResultListBox.Add(row) + btn := setUpUserFileSearchResultButton(partOfPathToShow, path) + fileSearchResultFlowBox.Add(btn) } } - fileSearchResultListBox.ShowAll() - statusLabel.SetText(fmt.Sprintf("%v results", fileSearchResultListBox.GetChildren().Length())) + fileSearchResultFlowBox.Hide() + + statusLabel.SetText(fmt.Sprintf("%v results", fileSearchResultFlowBox.GetChildren().Length())) + num := uint(fileSearchResultFlowBox.GetChildren().Length() / 3) + fileSearchResultFlowBox.SetMinChildrenPerLine(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() } } -func setUpUserDirsListRow(iconName, displayName, entryName string, userDirsMap map[string]string) *gtk.ListBoxRow { +func setUpUserDirButton(iconName, displayName, entryName string, userDirsMap map[string]string) *gtk.Box { if displayName == "" { parts := strings.Split(userDirsMap[entryName], "/") displayName = parts[(len(parts) - 1)] } - row, _ := gtk.ListBoxRowNew() - //row.SetCanFocus(false) - row.SetSelectable(false) - vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) - eventBox, _ := gtk.EventBoxNew() - hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 6) - eventBox.Add(hBox) - vBox.PackStart(eventBox, false, false, *itemPadding*3) + box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) + button, _ := gtk.ButtonNew() + img, _ := gtk.ImageNewFromIconName(iconName, gtk.ICON_SIZE_MENU) + button.SetImage(img) - img, _ := gtk.ImageNewFromIconName(iconName, gtk.ICON_SIZE_DND) - hBox.PackStart(img, false, false, 0) - - if len(displayName) > 45 { - displayName = fmt.Sprintf("%s...", displayName[:42]) + if len(displayName) > *nameLimit { + displayName = fmt.Sprintf("%s...", displayName[:*nameLimit-3]) } - lbl, _ := gtk.LabelNew(displayName) - hBox.PackStart(lbl, false, false, 0) - row.Add(vBox) + button.SetLabel(displayName) - row.Connect("activate", func() { + button.Connect("clicked", func() { launch(fmt.Sprintf("%s %s", *fileManager, userDirsMap[entryName]), false) }) - eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { - btnEvent := gdk.EventButtonNewFromEvent(e) - if btnEvent.Button() == 1 { - launch(fmt.Sprintf("%s %s", *fileManager, userDirsMap[entryName]), false) - return true - } - return false - }) - - return row + box.PackStart(button, false, true, 0) + return box } -func setUpUserFileSearchResultRow(fileName, filePath string) *gtk.ListBoxRow { - row, _ := gtk.ListBoxRowNew() - row.SetSelectable(false) - vBox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) - eventBox, _ := gtk.EventBoxNew() - hBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) - eventBox.Add(hBox) - vBox.PackStart(eventBox, false, false, *itemPadding) +func setUpUserFileSearchResultButton(fileName, filePath string) *gtk.Box { + box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) + button, _ := gtk.ButtonNew() - if len(fileName) > 150 { - fileName = fmt.Sprintf("%s...", fileName[:147]) + tooltipText := "" + if len(fileName) > *nameLimit { + tooltipText = fileName + fileName = fmt.Sprintf("%s...", fileName[:*nameLimit-3]) + } + button.SetLabel(fileName) + if tooltipText != "" { + button.SetTooltipText(tooltipText) } - lbl, _ := gtk.LabelNew(fileName) - hBox.PackStart(lbl, false, false, 0) - row.Add(vBox) - row.Connect("activate", func() { + button.Connect("clicked", func() { open(filePath) }) - eventBox.Connect("button-release-event", func(row *gtk.ListBoxRow, e *gdk.Event) bool { - btnEvent := gdk.EventButtonNewFromEvent(e) - if btnEvent.Button() == 1 { - open(filePath) - return true - } - return false - }) - return row + box.PackStart(button, false, true, 0) + return box }