aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gui/internal/ui/history.go26
-rw-r--r--gui/internal/ui/plan.go58
2 files changed, 71 insertions, 13 deletions
diff --git a/gui/internal/ui/history.go b/gui/internal/ui/history.go
index 7d4ee7b..bbdb27d 100644
--- a/gui/internal/ui/history.go
+++ b/gui/internal/ui/history.go
@@ -244,8 +244,9 @@ func (h *historyView) fillPlan() {
h.header.SetText(fmt.Sprintf("%d files · %d to reverse · %d refused",
c.Files, c.ToReverse, c.Refused))
}
+ w := h.widths()
for i, r := range h.tab.Rows {
- h.list.Append(h.undoRowWidget(i, r))
+ h.list.Append(h.undoRowWidget(i, r, w))
}
h.updateUndoButton()
}
@@ -258,8 +259,23 @@ func (h *historyView) clearPlan() {
h.undo.SetSensitive(false)
}
+// widths sizes the undo columns to what this plan holds, as the plan tab's
+// do, so a file or a reversal is not cut short for want of a few characters.
+func (h *historyView) widths() [3]int {
+ w := [3]int{16, 16, 6}
+ for _, r := range h.tab.Rows {
+ w[0] = max(w[0], len([]rune(r.Dir+"/"+r.File)))
+ w[1] = max(w[1], len([]rune(undoWhat(r, h.w.dirRoot(r.Dir)))))
+ w[2] = max(w[2], len([]rune(r.Outcome)))
+ }
+ for i, cap := range [3]int{40, 52, 24} {
+ w[i] = min(w[i], cap)
+ }
+ return w
+}
+
// undoRowWidget is one file of the reversal.
-func (h *historyView) undoRowWidget(i int, r model.UndoRow) *gtk.ListBoxRow {
+func (h *historyView) undoRowWidget(i int, r model.UndoRow, w [3]int) *gtk.ListBoxRow {
box := gtk.NewBox(gtk.OrientationHorizontal, 8)
box.SetMarginStart(6)
box.SetMarginEnd(6)
@@ -277,13 +293,13 @@ func (h *historyView) undoRowWidget(i int, r model.UndoRow) *gtk.ListBoxRow {
})
box.Append(check)
- box.Append(column(escape(r.Dir+"/"+r.File), 24, true))
+ box.Append(column(escape(r.Dir+"/"+r.File), w[0], true))
// The outcome comes before the reversal: after an undo it is what the
// user is looking for, and the reversal is the column that ellipsizes.
if r.Outcome != "" {
- box.Append(column(escape(r.Outcome), 12, false))
+ box.Append(column(escape(r.Outcome), w[2], false))
}
- what := column(escape(undoWhat(r, h.w.dirRoot(r.Dir))), 30, false)
+ what := columnMin(escape(undoWhat(r, h.w.dirRoot(r.Dir))), w[1], 18, true)
if r.Refused != "" {
what.AddCSSClass("error")
}
diff --git a/gui/internal/ui/plan.go b/gui/internal/ui/plan.go
index fd2f563..f6f2952 100644
--- a/gui/internal/ui/plan.go
+++ b/gui/internal/ui/plan.go
@@ -112,7 +112,7 @@ func newPlanView(w *Window) *planView {
panes.SetResizeStartChild(true)
panes.SetResizeEndChild(false)
panes.SetShrinkEndChild(false)
- panes.SetPosition(640)
+ panes.SetPosition(780)
panes.SetVExpand(true)
p.root.Append(bar)
@@ -346,15 +346,37 @@ func (p *planView) fillList() {
if p.tab == nil {
return
}
+ w := p.widths()
for i, r := range p.tab.Rows {
- p.list.Append(p.rowWidget(i, r))
+ p.list.Append(p.rowWidget(i, r, w))
}
p.apply.SetLabel(fmt.Sprintf("Apply %d selected", p.tab.SelectedCount()))
p.apply.SetSensitive(!p.tab.Applied && p.tab.SelectedCount() > 0)
}
+// widths is how wide each column has to be for this plan: enough for the
+// longest value it holds, within limits, so a rule name or an outcome is
+// shown whole rather than cut to an ellipsis. The list scrolls sideways
+// when the total does not fit (his report, 2026-09-16).
+func (p *planView) widths() [4]int {
+ w := [4]int{16, 16, 8, 6}
+ root := p.dirRoot()
+ for _, r := range p.tab.Rows {
+ w[0] = max(w[0], len([]rune(r.Rel)))
+ w[1] = max(w[1], len([]rune(rowLabel(r, root))))
+ w[2] = max(w[2], len([]rune(r.Rule)))
+ w[3] = max(w[3], len([]rune(r.Outcome)))
+ }
+ // Past these a single long value would push every other column off the
+ // window; the details pane holds the whole text either way.
+ for i, cap := range [4]int{40, 52, 34, 24} {
+ w[i] = min(w[i], cap)
+ }
+ return w
+}
+
// rowWidget is one line of the list.
-func (p *planView) rowWidget(i int, r model.Row) *gtk.ListBoxRow {
+func (p *planView) rowWidget(i int, r model.Row, w [4]int) *gtk.ListBoxRow {
box := gtk.NewBox(gtk.OrientationHorizontal, 8)
box.SetMarginStart(6)
box.SetMarginEnd(6)
@@ -373,11 +395,14 @@ func (p *planView) rowWidget(i int, r model.Row) *gtk.ListBoxRow {
})
box.Append(check)
- box.Append(column(escape(r.Rel), 24, true))
- box.Append(column(escape(rowLabel(r, p.dirRoot())), 30, false))
- box.Append(column(escape(r.Rule), 12, false))
+ // The name and what-would-happen columns share whatever space is left
+ // and shrink first; the rule keeps its width, so it is never the column
+ // cut to an ellipsis.
+ box.Append(column(escape(r.Rel), w[0], true))
+ box.Append(columnMin(escape(rowLabel(r, p.dirRoot())), w[1], 18, true))
+ box.Append(column(escape(r.Rule), w[2], false))
if r.Outcome != "" {
- box.Append(column(escape(r.Outcome), 12, false))
+ box.Append(column(escape(r.Outcome), w[3], false))
}
row := gtk.NewListBoxRow()
row.SetChild(box)
@@ -395,17 +420,34 @@ func (p *planView) dirRoot() string {
// column is one cell of a row: left-aligned, and ellipsized to chars so
// that a long name or warning cannot widen the window past the screen. The
// whole text is in the details pane beside the list.
+//
+// The expanding column - the file's name - may shrink to yieldChars when
+// the window is too narrow for every column, so it is the one that gives
+// way and the columns beside it (what would happen, and which rule decided)
+// stay readable. A fixed column keeps its width and the list scrolls.
func column(text string, chars int, expand bool) *gtk.Label {
+ return columnMin(text, chars, yieldChars, expand)
+}
+
+// columnMin is column with the width it may shrink to given explicitly.
+func columnMin(text string, chars, floor int, expand bool) *gtk.Label {
l := gtk.NewLabel(text)
l.SetXAlign(0)
l.SetEllipsize(pango.EllipsizeEnd)
- l.SetWidthChars(chars)
+ if expand {
+ l.SetWidthChars(min(chars, floor))
+ } else {
+ l.SetWidthChars(chars)
+ }
l.SetMaxWidthChars(chars)
l.SetHExpand(expand)
l.SetTooltipText(text)
return l
}
+// yieldChars is how narrow an expanding column may become.
+const yieldChars = 14
+
// showDetails writes the selected file's steps and warnings into the pane.
func (p *planView) showDetails(i int) {
if p.tab == nil || i < 0 || i >= len(p.tab.Rows) {