v0.26.2 3cd9d2e

modal_sizing.go RawHistoryFolder

1package component
2
3import (
4	"github.com/bnema/dumber/internal/ui/layout"
5	"github.com/jwijenbergh/puregotk/v4/gtk"
6)
7
8// ModalSizeConfig holds configuration for modal sizing calculations.
9type ModalSizeConfig struct {
10	WidthPct       float64 // Percentage of parent width (e.g., 0.6)
11	MaxWidth       int     // Maximum width in pixels
12	TopMarginPct   float64 // Top margin as percentage of parent height
13	FallbackWidth  int     // Fallback when parent not allocated
14	FallbackHeight int     // Fallback height
15}
16
17// RowHeightDefaults holds default (unscaled) row heights for list-based modals.
18// These are base pixel values that should be multiplied by UI scale.
19type RowHeightDefaults struct {
20	Standard int // Standard list row (e.g., session row, history row)
21	Compact  int // Compact row (e.g., tree row, sub-item)
22	Divider  int // Divider/separator row
23}
24
25// ListDisplayDefaults holds display limits for list-based modals.
26type ListDisplayDefaults struct {
27	MaxVisibleRows int // Maximum rows visible before scrolling
28	MaxResults     int // Maximum results to fetch/display
29}
30
31// Package-level defaults for row heights.
32var DefaultRowHeights = RowHeightDefaults{
33	Standard: 50,
34	Compact:  28,
35	Divider:  30,
36}
37
38// OmniboxSizeDefaults provides default sizing for omnibox modal.
39var OmniboxSizeDefaults = ModalSizeConfig{
40	WidthPct:       0.8,
41	MaxWidth:       800,
42	TopMarginPct:   0.2,
43	FallbackWidth:  800,
44	FallbackHeight: 600,
45}
46
47// OmniboxListDefaults provides display limits for omnibox modal.
48var OmniboxListDefaults = ListDisplayDefaults{
49	MaxVisibleRows: 10,
50	MaxResults:     10,
51}
52
53// SessionManagerSizeDefaults provides default sizing for session manager modal.
54var SessionManagerSizeDefaults = ModalSizeConfig{
55	WidthPct:       0.6,
56	MaxWidth:       600,
57	TopMarginPct:   0.15,
58	FallbackWidth:  600,
59	FallbackHeight: 600,
60}
61
62// SessionManagerListDefaults provides display limits for session manager modal.
63var SessionManagerListDefaults = ListDisplayDefaults{
64	MaxVisibleRows: 6,
65	MaxResults:     50,
66}
67
68// TabPickerSizeDefaults provides default sizing for tab picker modal.
69var TabPickerSizeDefaults = ModalSizeConfig{
70	WidthPct:       0.6,
71	MaxWidth:       600,
72	TopMarginPct:   0.15,
73	FallbackWidth:  600,
74	FallbackHeight: 600,
75}
76
77// TabPickerListDefaults provides display limits for tab picker modal.
78var TabPickerListDefaults = ListDisplayDefaults{
79	MaxVisibleRows: 8,
80	MaxResults:     20,
81}
82
83// CalculateModalDimensions computes width and top margin based on parent overlay.
84// Returns calculated width and top margin in pixels.
85func CalculateModalDimensions(parent layout.OverlayWidget, cfg ModalSizeConfig) (width, marginTop int) {
86	var parentWidth, parentHeight int
87
88	if parent != nil {
89		parentWidth = parent.GetAllocatedWidth()
90		parentHeight = parent.GetAllocatedHeight()
91	}
92
93	// Use fallback if parent not allocated or too small to be useful
94	// A width < 100 is too small for any meaningful modal
95	if parentWidth < 100 {
96		parentWidth = cfg.FallbackWidth
97	}
98	if parentHeight < 100 {
99		parentHeight = cfg.FallbackHeight
100	}
101
102	width = int(float64(parentWidth) * cfg.WidthPct)
103	if width > cfg.MaxWidth {
104		width = cfg.MaxWidth
105	}
106
107	marginTop = int(float64(parentHeight) * cfg.TopMarginPct)
108	return width, marginTop
109}
110
111// SetScrolledWindowHeight safely sets min/max content height on a ScrolledWindow.
112// Resets min to -1 first to avoid GTK assertion (min <= max) when shrinking.
113func SetScrolledWindowHeight(sw *gtk.ScrolledWindow, height int) {
114	if sw == nil {
115		return
116	}
117	sw.SetMinContentHeight(-1)
118	sw.SetMaxContentHeight(height)
119	sw.SetMinContentHeight(height)
120}
121
122// MeasureWidgetHeight returns the natural height of a widget for a given width.
123// Returns 0 if widget is nil or measurement fails.
124func MeasureWidgetHeight(widget *gtk.Widget, forWidth int) int {
125	if widget == nil {
126		return 0
127	}
128	var minH, natH int
129	widget.Measure(gtk.OrientationVerticalValue, forWidth, &minH, &natH, nil, nil)
130	return natH
131}
132
133// ScaleValue scales a base pixel value by UI scale factor.
134// Returns the base value if scale is <= 0.
135func ScaleValue(base int, uiScale float64) int {
136	if uiScale <= 0 {
137		uiScale = 1.0
138	}
139	return int(float64(base) * uiScale)
140}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close