aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/ui/plan.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 09:12:32 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 09:12:32 +0200
commit4d6386960c981678a8e8123a4db5463df7ee60bb (patch)
tree403877da13c244b68ef335f25ba1337cd72a6527 /gui/internal/ui/plan.go
parent80dde7b2cae7b9844c244e03395145370a819412 (diff)
downloadkrino-4d6386960c981678a8e8123a4db5463df7ee60bb.tar.gz
krino-4d6386960c981678a8e8123a4db5463df7ee60bb.zip
gui: keep every divider where it is put, and let the columns shrink
Diffstat (limited to 'gui/internal/ui/plan.go')
-rw-r--r--gui/internal/ui/plan.go92
1 files changed, 74 insertions, 18 deletions
diff --git a/gui/internal/ui/plan.go b/gui/internal/ui/plan.go
index 38412c2..4fa9c39 100644
--- a/gui/internal/ui/plan.go
+++ b/gui/internal/ui/plan.go
@@ -36,12 +36,13 @@ type planView struct {
selNone *gtk.Button
checked *gtk.MenuButton
- groups [6]*gtk.SizeGroup
- filter *gtk.SearchEntry
- shown []int
- list *gtk.ListBox
- headerBox *gtk.Box
- details *gtk.TextView
+ groups [6]*gtk.SizeGroup
+ listScroll *gtk.ScrolledWindow
+ filter *gtk.SearchEntry
+ shown []int
+ list *gtk.ListBox
+ headerBox *gtk.Box
+ details *gtk.TextView
previewNote *gtk.Label
picture *gtk.Picture
@@ -124,6 +125,7 @@ func newPlanView(w *Window) *planView {
listScroll.SetChild(p.list)
listScroll.SetHExpand(true)
listScroll.SetVExpand(true)
+ p.listScroll = listScroll
for i := range p.groups {
p.groups[i] = gtk.NewSizeGroup(gtk.SizeGroupHorizontal)
}
@@ -567,6 +569,12 @@ func (p *planView) fillList() {
p.selAll.SetSensitive(!p.tab.Applied)
p.selNone.SetSensitive(!p.tab.Applied)
p.sayWhatIsShown()
+ // A fresh list starts at its left edge: without this the view can open
+ // scrolled sideways, with the file names out of sight (his report,
+ // 2026-09-17).
+ if adj := p.listScroll.HAdjustment(); adj != nil {
+ adj.SetValue(0)
+ }
}
// widths is how wide each column has to be for this plan: enough for the
@@ -602,6 +610,10 @@ func (p *planView) showOutcome() bool {
return p.tab != nil && p.tab.Applied
}
+// headerFloors are how narrow each heading may become, matching the cells
+// under it.
+var headerFloors = [5]int{12, 6, 16, 10, 8}
+
// columnTitles name the columns of the plan.
var columnTitles = [5]string{"file", "action", "where it would go", "rule", "outcome"}
@@ -625,7 +637,7 @@ func (p *planView) header(w [5]int) {
if i == 4 && !p.showOutcome() {
continue
}
- l := columnMin(title, w[i], yieldChars, i == 0 || i == 2)
+ l := columnMin(title, w[i], headerFloors[i], i == 0 || i == 2)
l.AddCSSClass("heading")
l.SetTooltipText("")
p.groups[i+1].AddWidget(l)
@@ -658,16 +670,19 @@ func (p *planView) rowWidget(i int, r model.Row, w [5]int) *gtk.ListBoxRow {
// shrink first; the action and the rule keep their width, so neither is
// ever the column cut to an ellipsis.
action, colour := rowAction(r)
+ // Every column can shrink: a pane narrower than their natural widths
+ // used to push the whole row out of view to the left (his report,
+ // 2026-09-17).
cells := []gtk.Widgetter{
- column(escape(r.Rel), w[0], true),
+ columnMin(escape(r.Rel), w[0], 12, true),
colouredColumn(action, w[1], colour),
- columnMin(escape(rowWhere(r, p.dirRoot())), w[2], 18, true),
- column(escape(r.Rule), w[3], false),
+ columnMin(escape(rowWhere(r, p.dirRoot())), w[2], 16, true),
+ columnMin(escape(r.Rule), w[3], 10, false),
}
// The outcome column appears once a plan has been applied: before that
// it would be an empty column with a heading over it.
if p.showOutcome() {
- cells = append(cells, column(escape(r.Outcome), w[4], false))
+ cells = append(cells, columnMin(escape(r.Outcome), w[4], 8, false))
}
for i, cell := range cells {
p.groups[i+1].AddWidget(cell)
@@ -864,17 +879,52 @@ func (p *planView) setLayout(which string) {
p.arrangement.SetResizeEndChild(false)
p.arrangement.SetShrinkEndChild(false)
p.arrangement.SetVExpand(true)
+ prefs := p.w.prefs
if which == model.LayoutSide {
p.detailPane.SetSizeRequest(340, -1)
- p.arrangement.SetPosition(780)
+ p.arrangement.SetPosition(orDefault(prefs.ListWidth, model.DefaultListWidth))
+ p.previewHeight = orDefault(prefs.PreviewHeight, model.DefaultPreviewHeight)
} else {
p.detailPane.SetSizeRequest(-1, 240)
- p.arrangement.SetPosition(420)
+ p.arrangement.SetPosition(orDefault(prefs.ListHeight, model.DefaultListHeight))
+ p.previewHeight = orDefault(prefs.PreviewWidth, model.DefaultPreviewWidth)
}
+ p.arrangement.Connect("notify::position", p.rememberListSize)
p.root.Append(p.arrangement)
p.setPreviewHeight(p.previewHeight)
}
+// orDefault is n, or the default when nothing has been chosen yet.
+func orDefault(n, def int) int {
+ if n < 80 {
+ return def
+ }
+ return n
+}
+
+// rememberListSize keeps where the divider between the file list and the
+// explanation was left, for this layout.
+func (p *planView) rememberListSize() {
+ at := p.arrangement.Position()
+ if at < 80 {
+ return
+ }
+ prefs := p.w.prefs
+ if p.layout == model.LayoutStacked {
+ if prefs.ListHeight == at {
+ return
+ }
+ prefs.ListHeight = at
+ } else {
+ if prefs.ListWidth == at {
+ return
+ }
+ prefs.ListWidth = at
+ }
+ p.w.prefs = prefs
+ p.w.savePrefsSoon()
+}
+
// rememberPreviewHeight keeps what a drag of the divider chose, so the next
// window opens the same way.
func (p *planView) rememberPreviewHeight() {
@@ -888,11 +938,13 @@ func (p *planView) rememberPreviewHeight() {
}
p.previewHeight = height
prefs := p.w.prefs
- prefs.PreviewHeight = height
- p.w.prefs = prefs
- if err := prefs.Save(); err != nil {
- p.w.setStatus("settings: %v", err)
+ if p.layout == model.LayoutStacked {
+ prefs.PreviewWidth = height
+ } else {
+ prefs.PreviewHeight = height
}
+ p.w.prefs = prefs
+ p.w.savePrefsSoon()
}
// setPreviewHeight puts the divider where a height asks for.
@@ -926,7 +978,11 @@ func (p *planView) applyPrefs(prefs model.Prefs) {
p.previewOff = !prefs.Preview
p.startSelected = prefs.SelectAll
p.setLayout(prefs.Layout)
- p.setPreviewHeight(prefs.PreviewHeight)
+ if prefs.Layout == model.LayoutStacked {
+ p.setPreviewHeight(orDefault(prefs.PreviewWidth, model.DefaultPreviewWidth))
+ } else {
+ p.setPreviewHeight(orDefault(prefs.PreviewHeight, model.DefaultPreviewHeight))
+ }
if p.previewOff {
p.previewFor = ""
p.picture.SetVisible(false)