main 5041353

tab_button.go RawHistoryFolder

1// Package component provides reusable GTK UI components.
2package component
3
4import (
5	"github.com/bnema/dumber/internal/domain/entity"
6	"github.com/bnema/puregotk/v4/gtk"
7	"github.com/bnema/puregotk/v4/pango"
8)
9
10// TabButton represents a single tab button in the tab bar.
11type TabButton struct {
12	button   *gtk.Button
13	label    *gtk.Label
14	tabID    entity.TabID
15	isActive bool
16
17	// Callback for click events
18	onClick func(tabID entity.TabID)
19}
20
21// NewTabButton creates a new tab button for the given tab.
22func NewTabButton(tab *entity.Tab) *TabButton {
23	tb := &TabButton{
24		tabID: tab.ID,
25	}
26
27	// Create the button
28	tb.button = gtk.NewButton()
29	if tb.button == nil {
30		return nil
31	}
32
33	// Prevent focus stealing from WebView
34	tb.button.SetFocusOnClick(false)
35	tb.button.SetCanFocus(false)
36
37	// Add CSS class
38	tb.button.AddCssClass("tab-button")
39
40	// Create the label
41	title := tab.Title()
42	tb.label = gtk.NewLabel(&title)
43	if tb.label == nil {
44		tb.button.Unref()
45		return nil
46	}
47
48	// Configure label for text overflow
49	const maxTabTitleChars = 20
50	tb.label.SetEllipsize(pango.EllipsizeMiddleValue)
51	tb.label.SetMaxWidthChars(maxTabTitleChars)
52	tb.label.AddCssClass("tab-title")
53
54	// Set label as button child
55	tb.button.SetChild(&tb.label.Widget)
56
57	return tb
58}
59
60// Widget returns the underlying GTK widget for embedding.
61func (tb *TabButton) Widget() *gtk.Widget {
62	return &tb.button.Widget
63}
64
65// Button returns the underlying GTK button.
66func (tb *TabButton) Button() *gtk.Button {
67	return tb.button
68}
69
70// TabID returns the ID of the tab this button represents.
71func (tb *TabButton) TabID() entity.TabID {
72	return tb.tabID
73}
74
75// SetTitle updates the button's label text.
76func (tb *TabButton) SetTitle(title string) {
77	if tb.label != nil {
78		tb.label.SetText(title)
79	}
80}
81
82// SetActive updates the active state styling.
83func (tb *TabButton) SetActive(active bool) {
84	if tb.isActive == active {
85		return
86	}
87	tb.isActive = active
88	if tb.button == nil {
89		return
90	}
91
92	if active {
93		tb.button.AddCssClass("tab-button-active")
94	} else {
95		tb.button.RemoveCssClass("tab-button-active")
96	}
97}
98
99// IsActive returns whether this tab is currently active.
100func (tb *TabButton) IsActive() bool {
101	return tb.isActive
102}
103
104// SetOnClick sets the callback for click events.
105func (tb *TabButton) SetOnClick(fn func(tabID entity.TabID)) {
106	tb.onClick = fn
107
108	if fn != nil {
109		tabID := tb.tabID // Capture for closure
110		clickCb := func(_ gtk.Button) {
111			if tb.onClick != nil {
112				tb.onClick(tabID)
113			}
114		}
115		tb.button.ConnectClicked(&clickCb)
116	}
117}
118
119// Destroy cleans up the button resources.
120func (tb *TabButton) Destroy() {
121	if tb.label != nil {
122		tb.label.Unref()
123		tb.label = nil
124	}
125	if tb.button != nil {
126		tb.button.Unref()
127		tb.button = nil
128	}
129}

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