detached 524e65e

feat(stacked-panes): add click-to-activate on title bars

- Add WidgetAttachClickHandler helper function for GTK4 gesture clicks
- Wire up click handlers to stacked pane title bars
- Clicking on a collapsed title bar now activates that pane
- Uses existing handleTitleBarClick logic for pane switching
commit
524e65ebfd8c277c99eeb1b92efa8658689c9431
author
bnema · · 2025-11-16 22:35 UTC
committer
bnema · · 2025-11-16 19:55 UTC
parent
6d403db
signature
unsigned
2 files · +34 -10
M internal/app/browser/stacked_panes.go +16 -10
@@ -541,10 +541,9 @@ func (spm *StackedPaneManager) createTitleBar(pane *paneNode) gtk.Widgetter {
541541 spm.titleBarToPane[titleBarID] = pane
542542
543543 // Attach click handler
544- // TODO: Implement click handler with gtk.GestureClick
545- // webkit.WidgetAttachClickHandler(titleBar, func() {
546- // spm.handleTitleBarClick(titleBarID)
547- // })
544+ webkit.WidgetAttachClickHandler(titleBar, func() {
545+ spm.handleTitleBarClick(titleBarID)
546+ })
548547 }
549548
550549 return titleBar
@@ -613,10 +612,9 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string)
613612 spm.titleBarToPane[titleBarID] = node
614613
615614 // Attach click handler
616- // TODO: Implement with gtk.GestureClick
617- // webkit.WidgetAttachClickHandler(newTitleBar, func() {
618- // spm.handleTitleBarClick(titleBarID)
619- // })
615+ webkit.WidgetAttachClickHandler(newTitleBar, func() {
616+ spm.handleTitleBarClick(titleBarID)
617+ })
620618
621619 // Replace the old title bar in the stack
622620 if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != nil {
@@ -682,9 +680,17 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str
682680
683681 if texture != nil && titleBox != nil {
684682
685- // Create favicon image
683+ // Create favicon image with UI scaling
686684 faviconImg := gtk.NewImageFromPaintable(texture)
687- faviconImg.SetPixelSize(16)
685+ // Scale favicon size based on UI scale (base: 16px)
686+ faviconSize := 16
687+ if spm.wm != nil && spm.wm.app != nil && spm.wm.app.config != nil {
688+ uiScale := spm.wm.app.config.Workspace.Styling.UIScale
689+ if uiScale > 0 {
690+ faviconSize = int(16 * uiScale)
691+ }
692+ }
693+ faviconImg.SetPixelSize(faviconSize)
688694 webkit.WidgetAddCSSClass(faviconImg, "stacked-pane-favicon")
689695
690696 // IDEMPOTENT: Remove placeholder only if it still has titleBox as parent
@@ -541,10 +541,9 @@ func (spm *StackedPaneManager) createTitleBar(pane *paneNode) gtk.Widgetter {
541 spm.titleBarToPane[titleBarID] = pane541 spm.titleBarToPane[titleBarID] = pane
542 542
543 // Attach click handler543 // Attach click handler
544- // TODO: Implement click handler with gtk.GestureClick544+ webkit.WidgetAttachClickHandler(titleBar, func() {
545- // webkit.WidgetAttachClickHandler(titleBar, func() {545+ spm.handleTitleBarClick(titleBarID)
546- // spm.handleTitleBarClick(titleBarID)546+ })
547- // })
548 }547 }
549 548
550 return titleBar549 return titleBar
@@ -613,10 +612,9 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string)
613 spm.titleBarToPane[titleBarID] = node612 spm.titleBarToPane[titleBarID] = node
614 613
615 // Attach click handler614 // Attach click handler
616- // TODO: Implement with gtk.GestureClick615+ webkit.WidgetAttachClickHandler(newTitleBar, func() {
617- // webkit.WidgetAttachClickHandler(newTitleBar, func() {616+ spm.handleTitleBarClick(titleBarID)
618- // spm.handleTitleBarClick(titleBarID)617+ })
619- // })
620 618
621 // Replace the old title bar in the stack619 // Replace the old title bar in the stack
622 if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != nil {620 if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != nil {
@@ -682,9 +680,17 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str
682 680
683 if texture != nil && titleBox != nil {681 if texture != nil && titleBox != nil {
684 682
685- // Create favicon image683+ // Create favicon image with UI scaling
686 faviconImg := gtk.NewImageFromPaintable(texture)684 faviconImg := gtk.NewImageFromPaintable(texture)
687- faviconImg.SetPixelSize(16)685+ // Scale favicon size based on UI scale (base: 16px)
686+ faviconSize := 16
687+ if spm.wm != nil && spm.wm.app != nil && spm.wm.app.config != nil {
688+ uiScale := spm.wm.app.config.Workspace.Styling.UIScale
689+ if uiScale > 0 {
690+ faviconSize = int(16 * uiScale)
691+ }
692+ }
693+ faviconImg.SetPixelSize(faviconSize)
688 webkit.WidgetAddCSSClass(faviconImg, "stacked-pane-favicon")694 webkit.WidgetAddCSSClass(faviconImg, "stacked-pane-favicon")
689 695
690 // IDEMPOTENT: Remove placeholder only if it still has titleBox as parent696 // IDEMPOTENT: Remove placeholder only if it still has titleBox as parent
M pkg/webkit/helpers.go +18 -0
@@ -250,6 +250,24 @@ func WidgetAddController(w gtk.Widgetter, controller gtk.EventControllerer) {
250250 }
251251 }
252252
253+// WidgetAttachClickHandler attaches a left-click handler to a widget
254+func WidgetAttachClickHandler(w gtk.Widgetter, callback func()) *gtk.GestureClick {
255+ widget := getWidget(w)
256+ if widget == nil {
257+ return nil
258+ }
259+
260+ gestureClick := gtk.NewGestureClick()
261+ gestureClick.SetButton(1) // Left click only
262+
263+ gestureClick.ConnectPressed(func(nPress int, x, y float64) {
264+ callback()
265+ })
266+
267+ widget.AddController(gestureClick)
268+ return gestureClick
269+}
270+
253271 // WidgetAllocation returns the widget's allocation (alias for WidgetGetAllocation)
254272 func WidgetAllocation(w gtk.Widgetter) (x, y, width, height int) {
255273 return WidgetGetAllocation(w)
@@ -250,6 +250,24 @@ func WidgetAddController(w gtk.Widgetter, controller gtk.EventControllerer) {
250 }250 }
251 }251 }
252 252
253+// WidgetAttachClickHandler attaches a left-click handler to a widget
254+func WidgetAttachClickHandler(w gtk.Widgetter, callback func()) *gtk.GestureClick {
255+ widget := getWidget(w)
256+ if widget == nil {
257+ return nil
258+ }
259+
260+ gestureClick := gtk.NewGestureClick()
261+ gestureClick.SetButton(1) // Left click only
262+
263+ gestureClick.ConnectPressed(func(nPress int, x, y float64) {
264+ callback()
265+ })
266+
267+ widget.AddController(gestureClick)
268+ return gestureClick
269+}
270+
253 // WidgetAllocation returns the widget's allocation (alias for WidgetGetAllocation)271 // WidgetAllocation returns the widget's allocation (alias for WidgetGetAllocation)
254 func WidgetAllocation(w gtk.Widgetter) (x, y, width, height int) {272 func WidgetAllocation(w gtk.Widgetter) (x, y, width, height int) {
255 return WidgetGetAllocation(w)273 return WidgetGetAllocation(w)

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