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}