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 { | |||
| 48 | 48 | ||
| 49 | 49 | const ( | |
| 50 | 50 | // 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 | + | }` | |
| 56 | 59 | activePaneClass = "workspace-pane-active" | |
| 60 | + | basePaneClass = "workspace-pane" | |
| 61 | + | multiPaneClass = "workspace-multi-pane" | |
| 57 | 62 | ) | |
| 58 | 63 | ||
| 59 | 64 | // registerWorkspaceShortcuts registers global workspace navigation shortcuts on the given webView. | |
| @@ -146,6 +151,8 @@ func NewWorkspaceManager(app *BrowserApp, rootPane *BrowserPane) *WorkspaceManag | |||
| 146 | 151 | app.workspace = manager | |
| 147 | 152 | manager.focusNode(root) | |
| 148 | 153 | manager.registerWorkspaceShortcuts(root.pane.webView) | |
| 154 | + | // Ensure initial CSS classes are applied | |
| 155 | + | manager.ensurePaneBaseClasses() | |
| 149 | 156 | 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 | 286 | webkit.WidgetRemoveCSSClass(wm.active.container, activePaneClass) | |
| 279 | 287 | } | |
| 280 | 288 | ||
| @@ -298,10 +306,16 @@ func (wm *WorkspaceManager) focusNode(node *paneNode) { | |||
| 298 | 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 | 312 | container := node.container | |
| 302 | 313 | viewWidget := node.pane.webView.Widget() | |
| 303 | 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 | 319 | if !webkit.WidgetIsValid(container) { | |
| 306 | 320 | log.Printf("[workspace] focus aborted: container invalid widget=%#x", container) | |
| 307 | 321 | return | |
| @@ -363,6 +377,30 @@ func (wm *WorkspaceManager) ensureStyles() { | |||
| 363 | 377 | 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 | 404 | func (wm *WorkspaceManager) focusByView(view *webkit.WebView) { | |
| 367 | 405 | if wm == nil || view == nil { | |
| 368 | 406 | return | |
| @@ -818,6 +856,9 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string) (*pane | |||
| 818 | 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 | 862 | webkit.IdleAdd(func() bool { | |
| 822 | 863 | if newContainer != 0 { | |
| 823 | 864 | webkit.WidgetShow(newContainer) | |
| @@ -958,6 +999,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error { | |||
| 958 | 999 | } | |
| 959 | 1000 | ||
| 960 | 1001 | wm.updateMainPane() | |
| 1002 | + | // Update CSS classes after pane count changes | |
| 1003 | + | wm.ensurePaneBaseClasses() | |
| 961 | 1004 | log.Printf("[workspace] root pane closed and delegated; panes remaining=%d", len(wm.app.panes)) | |
| 962 | 1005 | return nil | |
| 963 | 1006 | } | |
| @@ -1087,6 +1130,8 @@ func (wm *WorkspaceManager) closePane(node *paneNode) error { | |||
| 1087 | 1130 | } | |
| 1088 | 1131 | ||
| 1089 | 1132 | wm.updateMainPane() | |
| 1133 | + | // Update CSS classes after pane count changes | |
| 1134 | + | wm.ensurePaneBaseClasses() | |
| 1090 | 1135 | log.Printf("[workspace] pane closed; panes remaining=%d", len(wm.app.panes)) | |
| 1091 | 1136 | return nil | |
| 1092 | 1137 | } | |
| @@ -48,12 +48,17 @@ type WorkspaceManager struct { | |||||
| 48 | 48 | ||||
| 49 | const ( | 49 | const ( | ||
| 50 | // TODO : should be defined via Config + Defaults | 50 | // 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 = manager | 151 | 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 manager | 156 | 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.container | 312 | 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 | return | 321 | return | ||
| @@ -363,6 +377,30 @@ func (wm *WorkspaceManager) ensureStyles() { | |||||
| 363 | wm.cssInitialized = true | 377 | 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 | return | 406 | 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 nil | 1005 | 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 nil | 1136 | return nil | ||
| 1092 | } | 1137 | } | ||
M pkg/webkit/window_shortcuts_stub.go +1 -1
| @@ -42,4 +42,4 @@ func registerWindowShortcutCallback(cb func()) uintptr { | |||
| 42 | 42 | } | |
| 43 | 43 | ||
| 44 | 44 | 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 | + | } |