From 47b6776c2cb9b017ad95acb5aae8e34b8776fc7c Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 17 Sep 2026 10:05:55 +0200 Subject: gui: sort the plan by name, size, age, action or rule --- gui/internal/ui/plan.go | 88 +++++++++++++++++++++++++++++++++++---------- gui/internal/ui/settings.go | 14 ++++++++ 2 files changed, 83 insertions(+), 19 deletions(-) (limited to 'gui/internal/ui') diff --git a/gui/internal/ui/plan.go b/gui/internal/ui/plan.go index b861b14..8b6a394 100644 --- a/gui/internal/ui/plan.go +++ b/gui/internal/ui/plan.go @@ -40,29 +40,31 @@ type planView struct { groups [8]*gtk.SizeGroup listScroll *gtk.ScrolledWindow filter *gtk.SearchEntry + sort *gtk.DropDown shown []int list *gtk.ListBox headerBox *gtk.Box details *gtk.Box - previewNote *gtk.Label - picture *gtk.Picture - previewText *gtk.TextView - previewScroll *gtk.ScrolledWindow - previewTmp string - previewFor string - detailPane *gtk.Paned - arrangement *gtk.Paned - listSide *gtk.Box - detailScroll *gtk.ScrolledWindow - previewBox *gtk.Box - layout string - renderedDir string - previewHeight int - previewOff bool - startSelected bool - menu *gtk.Popover - menuRow int + previewNote *gtk.Label + picture *gtk.Picture + previewText *gtk.TextView + previewScroll *gtk.ScrolledWindow + previewTmp string + previewFor string + detailPane *gtk.Paned + arrangement *gtk.Paned + listSide *gtk.Box + detailScroll *gtk.ScrolledWindow + previewBox *gtk.Box + layout string + renderedDir string + previewHeight int + previewOff bool + sortFollowsPrefs bool + startSelected bool + menu *gtk.Popover + menuRow int tab *model.PlanTab cancelOp context.CancelFunc @@ -113,6 +115,13 @@ func newPlanView(w *Window) *planView { p.filter.SetSizeRequest(200, -1) bar.Append(p.filter) + // The order the plan is read in. It starts as the settings say and can + // be changed for this window alone (his request, 2026-09-17). + p.sort = gtk.NewDropDownFromStrings(sortItems()) + p.sort.SetTooltipText("the order the plan is listed in; Settings has the one a new window starts with") + bar.Append(gtk.NewLabel("sort")) + bar.Append(p.sort) + bar.Append(p.path) bar.Append(p.selAll) bar.Append(p.selNone) @@ -206,6 +215,11 @@ func newPlanView(w *Window) *planView { } }) p.filter.ConnectSearchChanged(func() { p.fillList() }) + p.sortFollowsPrefs = true + p.sort.Connect("notify::selected", func() { + p.sortFollowsPrefs = false + p.fillList() + }) p.selAll.ConnectClicked(func() { p.selectAll(true) }) p.selNone.ConnectClicked(func() { p.selectAll(false) }) p.list.ConnectRowSelected(func(row *gtk.ListBoxRow) { @@ -404,6 +418,10 @@ func (p *planView) confirmDeletePermanent() { func (p *planView) showPath() { if d := p.currentDir(); d != nil { p.path.SetText(escape(xdg.Abbrev(d.Root))) + // The toolbar is crowded, so the path is the first thing to be + // squeezed; the tooltip always has it in full. + p.path.SetTooltipText(escape(d.Root)) + p.dirs.SetTooltipText(escape(d.Root)) return } p.path.SetText("no directory is included; add one with: krino new NAME PATH") @@ -560,7 +578,7 @@ func (p *planView) fillList() { } w := p.widths() p.header(w) - p.shown = p.tab.Matching(p.filter.Text()) + p.shown = p.tab.Sorted(p.tab.Matching(p.filter.Text()), p.sortOrder()) for _, i := range p.shown { p.list.Append(p.rowWidget(i, p.tab.Rows[i], w)) } @@ -622,6 +640,35 @@ const ( colOutcome ) +// sortItems are the orders as the picker shows them. +func sortItems() []string { + out := make([]string, len(model.SortOrders)) + for i, o := range model.SortOrders { + out[i] = model.SortLabels[o] + } + return out +} + +// sortOrder is the order the picker names. +func (p *planView) sortOrder() string { + i := int(p.sort.Selected()) + if i < 0 || i >= len(model.SortOrders) { + return model.SortDefault + } + return model.SortOrders[i] +} + +// setSortOrder puts the picker on an order. +func (p *planView) setSortOrder(order string) { + for i, o := range model.SortOrders { + if o == order { + p.sort.SetSelected(uint(i)) + return + } + } + p.sort.SetSelected(0) +} + // showColumn reports whether a column is shown: three of them are settings, // and the outcome appears only once a plan has been applied. func (p *planView) showColumn(i int) bool { @@ -1062,6 +1109,9 @@ func (p *planView) setPreviewHeight(height int) { func (p *planView) applyPrefs(prefs model.Prefs) { p.previewOff = !prefs.Preview p.startSelected = prefs.SelectAll + if p.sortFollowsPrefs { + p.setSortOrder(prefs.Sort) + } p.setLayout(prefs.Layout) if p.tab != nil { p.fillList() diff --git a/gui/internal/ui/settings.go b/gui/internal/ui/settings.go index d1b5752..e15f60d 100644 --- a/gui/internal/ui/settings.go +++ b/gui/internal/ui/settings.go @@ -108,6 +108,15 @@ func (w *Window) showSettings() { columns.Append(showRule) columns.SetTooltipText("which of the optional columns the plan shows; file, action and where it would go are always there") + sortOrder := gtk.NewDropDownFromStrings(sortItems()) + for i, o := range model.SortOrders { + if o == prefs.Sort { + sortOrder.SetSelected(uint(i)) + } + } + sortOrder.SetTooltipText("the order a freshly scanned plan is listed in; the picker in the toolbar changes it for one window") + + box.Append(field("sort by", sortOrder)) box.Append(field("layout", layout)) box.Append(field("columns", columns)) box.Append(field("preview height", previewHeight)) @@ -120,7 +129,12 @@ func (w *Window) showSettings() { if layout.Selected() == 1 { which = model.LayoutStacked } + order := model.SortDefault + if i := int(sortOrder.Selected()); i >= 0 && i < len(model.SortOrders) { + order = model.SortOrders[i] + } p := model.Prefs{ + Sort: order, ShowSize: showSize.Active(), ShowAge: showAge.Active(), ShowRule: showRule.Active(), -- cgit v1.3