detached cb15db6

refactor: Enhance gtk border on focus

commit
cb15db6c68da51b52ee17f4e6e937a94f4c499dc
author
bamen · · 2025-09-19 04:54 UTC
committer
bamen · · 2025-09-19 04:56 UTC
parent
8de061e
signature
unsigned
2 files · +53 -8
M internal/app/browser/workspace_manager.go +52 -7
@@ -48,12 +48,17 @@ type WorkspaceManager struct {
4848
4949 const (
5050 // TODO : should be defined via Config + Defaults
51- activePaneCSS = `.workspace-pane-active {
52- border: 2px solid @theme_selected_bg_color;
53- border-radius: 0px;
54- transition: border-color 120ms ease-in-out;
55-}`
51+ activePaneCSS = `.workspace-pane {
52+ box-shadow: inset 0 0 0 0px transparent;
53+ transition: box-shadow 150ms ease-in-out;
54+ }
55+
56+ .workspace-pane-active {
57+ box-shadow: inset 0 0 0 2px @theme_selected_bg_color;
58+ }`
5659 activePaneClass = "workspace-pane-active"
60+ basePaneClass = "workspace-pane"
61+ multiPaneClass = "workspace-multi-pane"
5762 )
5863
5964 // registerWorkspaceShortcuts registers global workspace navigation shortcuts on the given webView.
@@ -146,6 +151,8 @@ func NewWorkspaceManager(app *BrowserApp, rootPane *BrowserPane) *WorkspaceManag
146151 app.workspace = manager
147152 manager.focusNode(root)
148153 manager.registerWorkspaceShortcuts(root.pane.webView)
154+ // Ensure initial CSS classes are applied
155+ manager.ensurePaneBaseClasses()
149156 return manager
150157 }
151158
@@ -274,7 +281,8 @@ func (wm *WorkspaceManager) focusNode(node *paneNode) {
274281 }
275282 }
276283
277- if wm.active != nil && wm.active.container != 0 {
284+ // Remove active class from previous pane only if we have multiple panes
285+ if wm.active != nil && wm.active.container != 0 && wm.hasMultiplePanes() {
278286 webkit.WidgetRemoveCSSClass(wm.active.container, activePaneClass)
279287 }
280288
@@ -298,10 +306,16 @@ func (wm *WorkspaceManager) focusNode(node *paneNode) {
298306 wm.app.browserService.AttachWebView(node.pane.webView)
299307 }
300308
309+ // Ensure all panes have proper base classes
310+ wm.ensurePaneBaseClasses()
311+
301312 container := node.container
302313 viewWidget := node.pane.webView.Widget()
303314 if container != 0 && container != previousContainer {
304- webkit.WidgetAddCSSClass(container, activePaneClass)
315+ // Only add active class if we have multiple panes (no border for single pane)
316+ if wm.hasMultiplePanes() {
317+ webkit.WidgetAddCSSClass(container, activePaneClass)
318+ }
305319 if !webkit.WidgetIsValid(container) {
306320 log.Printf("[workspace] focus aborted: container invalid widget=%#x", container)
307321 return
@@ -363,6 +377,30 @@ func (wm *WorkspaceManager) ensureStyles() {
363377 wm.cssInitialized = true
364378 }
365379
380+// hasMultiplePanes returns true if there are multiple panes in the workspace
381+func (wm *WorkspaceManager) hasMultiplePanes() bool {
382+ return wm != nil && wm.app != nil && len(wm.app.panes) > 1
383+}
384+
385+// ensurePaneBaseClasses ensures all panes have the proper base CSS classes
386+func (wm *WorkspaceManager) ensurePaneBaseClasses() {
387+ if wm == nil {
388+ return
389+ }
390+
391+ leaves := wm.collectLeaves()
392+ for _, leaf := range leaves {
393+ if leaf != nil && leaf.container != 0 {
394+ webkit.WidgetAddCSSClass(leaf.container, basePaneClass)
395+ if wm.hasMultiplePanes() {
396+ webkit.WidgetAddCSSClass(leaf.container, multiPaneClass)
397+ } else {
398+ webkit.WidgetRemoveCSSClass(leaf.container, multiPaneClass)
399+ }
400+ }
401+ }
402+}
403+
366404 func (wm *WorkspaceManager) focusByView(view *webkit.WebView) {
367405 if wm == nil || view == nil {
368406 return
@@ -818,6 +856,9 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string) (*pane
818856 newPane.zoomController.ApplyInitialZoom()
819857 }
820858
859+ // Update CSS classes for all panes now that we have multiple panes
860+ wm.ensurePaneBaseClasses()
861+
821862 webkit.IdleAdd(func() bool {
822863 if newContainer != 0 {
823864 webkit.WidgetShow(newContainer)
@@ -958,6 +999,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error {
958999 }
9591000
9601001 wm.updateMainPane()
1002+ // Update CSS classes after pane count changes
1003+ wm.ensurePaneBaseClasses()
9611004 log.Printf("[workspace] root pane closed and delegated; panes remaining=%d", len(wm.app.panes))
9621005 return nil
9631006 }
@@ -1087,6 +1130,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error {
10871130 }
10881131
10891132 wm.updateMainPane()
1133+ // Update CSS classes after pane count changes
1134+ wm.ensurePaneBaseClasses()
10901135 log.Printf("[workspace] pane closed; panes remaining=%d", len(wm.app.panes))
10911136 return nil
10921137 }
@@ -48,12 +48,17 @@ type WorkspaceManager struct {
48 48
49 const (49 const (
50 // TODO : should be defined via Config + Defaults50 // TODO : should be defined via Config + Defaults
51- activePaneCSS = `.workspace-pane-active {51+ activePaneCSS = `.workspace-pane {
52- border: 2px solid @theme_selected_bg_color;52+ box-shadow: inset 0 0 0 0px transparent;
53- border-radius: 0px;53+ transition: box-shadow 150ms ease-in-out;
54- transition: border-color 120ms ease-in-out;54+ }
55-}`55+
56+ .workspace-pane-active {
57+ box-shadow: inset 0 0 0 2px @theme_selected_bg_color;
58+ }`
56 activePaneClass = "workspace-pane-active"59 activePaneClass = "workspace-pane-active"
60+ basePaneClass = "workspace-pane"
61+ multiPaneClass = "workspace-multi-pane"
57 )62 )
58 63
59 // registerWorkspaceShortcuts registers global workspace navigation shortcuts on the given webView.64 // registerWorkspaceShortcuts registers global workspace navigation shortcuts on the given webView.
@@ -146,6 +151,8 @@ func NewWorkspaceManager(app *BrowserApp, rootPane *BrowserPane) *WorkspaceManag
146 app.workspace = manager151 app.workspace = manager
147 manager.focusNode(root)152 manager.focusNode(root)
148 manager.registerWorkspaceShortcuts(root.pane.webView)153 manager.registerWorkspaceShortcuts(root.pane.webView)
154+ // Ensure initial CSS classes are applied
155+ manager.ensurePaneBaseClasses()
149 return manager156 return manager
150 }157 }
151 158
@@ -274,7 +281,8 @@ func (wm *WorkspaceManager) focusNode(node *paneNode) {
274 }281 }
275 }282 }
276 283
277- if wm.active != nil && wm.active.container != 0 {284+ // Remove active class from previous pane only if we have multiple panes
285+ if wm.active != nil && wm.active.container != 0 && wm.hasMultiplePanes() {
278 webkit.WidgetRemoveCSSClass(wm.active.container, activePaneClass)286 webkit.WidgetRemoveCSSClass(wm.active.container, activePaneClass)
279 }287 }
280 288
@@ -298,10 +306,16 @@ func (wm *WorkspaceManager) focusNode(node *paneNode) {
298 wm.app.browserService.AttachWebView(node.pane.webView)306 wm.app.browserService.AttachWebView(node.pane.webView)
299 }307 }
300 308
309+ // Ensure all panes have proper base classes
310+ wm.ensurePaneBaseClasses()
311+
301 container := node.container312 container := node.container
302 viewWidget := node.pane.webView.Widget()313 viewWidget := node.pane.webView.Widget()
303 if container != 0 && container != previousContainer {314 if container != 0 && container != previousContainer {
304- webkit.WidgetAddCSSClass(container, activePaneClass)315+ // Only add active class if we have multiple panes (no border for single pane)
316+ if wm.hasMultiplePanes() {
317+ webkit.WidgetAddCSSClass(container, activePaneClass)
318+ }
305 if !webkit.WidgetIsValid(container) {319 if !webkit.WidgetIsValid(container) {
306 log.Printf("[workspace] focus aborted: container invalid widget=%#x", container)320 log.Printf("[workspace] focus aborted: container invalid widget=%#x", container)
307 return321 return
@@ -363,6 +377,30 @@ func (wm *WorkspaceManager) ensureStyles() {
363 wm.cssInitialized = true377 wm.cssInitialized = true
364 }378 }
365 379
380+// hasMultiplePanes returns true if there are multiple panes in the workspace
381+func (wm *WorkspaceManager) hasMultiplePanes() bool {
382+ return wm != nil && wm.app != nil && len(wm.app.panes) > 1
383+}
384+
385+// ensurePaneBaseClasses ensures all panes have the proper base CSS classes
386+func (wm *WorkspaceManager) ensurePaneBaseClasses() {
387+ if wm == nil {
388+ return
389+ }
390+
391+ leaves := wm.collectLeaves()
392+ for _, leaf := range leaves {
393+ if leaf != nil && leaf.container != 0 {
394+ webkit.WidgetAddCSSClass(leaf.container, basePaneClass)
395+ if wm.hasMultiplePanes() {
396+ webkit.WidgetAddCSSClass(leaf.container, multiPaneClass)
397+ } else {
398+ webkit.WidgetRemoveCSSClass(leaf.container, multiPaneClass)
399+ }
400+ }
401+ }
402+}
403+
366 func (wm *WorkspaceManager) focusByView(view *webkit.WebView) {404 func (wm *WorkspaceManager) focusByView(view *webkit.WebView) {
367 if wm == nil || view == nil {405 if wm == nil || view == nil {
368 return406 return
@@ -818,6 +856,9 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string) (*pane
818 newPane.zoomController.ApplyInitialZoom()856 newPane.zoomController.ApplyInitialZoom()
819 }857 }
820 858
859+ // Update CSS classes for all panes now that we have multiple panes
860+ wm.ensurePaneBaseClasses()
861+
821 webkit.IdleAdd(func() bool {862 webkit.IdleAdd(func() bool {
822 if newContainer != 0 {863 if newContainer != 0 {
823 webkit.WidgetShow(newContainer)864 webkit.WidgetShow(newContainer)
@@ -958,6 +999,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error {
958 }999 }
959 1000
960 wm.updateMainPane()1001 wm.updateMainPane()
1002+ // Update CSS classes after pane count changes
1003+ wm.ensurePaneBaseClasses()
961 log.Printf("[workspace] root pane closed and delegated; panes remaining=%d", len(wm.app.panes))1004 log.Printf("[workspace] root pane closed and delegated; panes remaining=%d", len(wm.app.panes))
962 return nil1005 return nil
963 }1006 }
@@ -1087,6 +1130,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error {
1087 }1130 }
1088 1131
1089 wm.updateMainPane()1132 wm.updateMainPane()
1133+ // Update CSS classes after pane count changes
1134+ wm.ensurePaneBaseClasses()
1090 log.Printf("[workspace] pane closed; panes remaining=%d", len(wm.app.panes))1135 log.Printf("[workspace] pane closed; panes remaining=%d", len(wm.app.panes))
1091 return nil1136 return nil
1092 }1137 }
M pkg/webkit/window_shortcuts_stub.go +1 -1
@@ -42,4 +42,4 @@ func registerWindowShortcutCallback(cb func()) uintptr {
4242 }
4343
4444 func unregisterWindowShortcutCallback(id uintptr) {
45-}⏎̸
45+}
@@ -42,4 +42,4 @@ func registerWindowShortcutCallback(cb func()) uintptr {
42 }42 }
43 43
44 func unregisterWindowShortcutCallback(id uintptr) {44 func unregisterWindowShortcutCallback(id uintptr) {
45-}⏎̸45+}

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