fix(ui): improve pane and tab mode border rendering
Enhances the visual border indicators for pane mode and tab mode by applying margins to the correct container hierarchy, ensuring the window background color shows through properly. Changes: - Apply borders to root container instead of ContentArea when available - Add resolveWindow() helper for safe window reference resolution - Track border target widget for proper cleanup on mode exit - Add GetWindow() method to TabManager for workspace access - Fallback to ContentArea for compatibility with older builds This fixes cases where borders weren't rendering correctly due to incorrect container targeting in the GTK widget hierarchy.
- commit
- 2835e2605ce62ade5d8698f2678a18385e9fa38d
- author
- bnema · · 2025-11-18 06:42 UTC
- parent
- 6ab096f
- signature
- unsigned
4 files · +104 -25
M internal/app/browser/tab_manager.go +9 -0
| @@ -23,6 +23,7 @@ type TabManager struct { | |||
| 23 | 23 | rootContainer gtk.Widgetter // Main vertical box containing tab bar + content | |
| 24 | 24 | tabBar gtk.Widgetter // Horizontal tab bar container | |
| 25 | 25 | ContentArea gtk.Widgetter // Container for active workspace (exported for border styling) | |
| 26 | + | tabModeTarget gtk.Widgetter // Widget currently holding tab mode border margins | |
| 26 | 27 | ||
| 27 | 28 | // Modal state | |
| 28 | 29 | tabModeActive bool | |
| @@ -472,6 +473,14 @@ func (tm *TabManager) GetRootContainer() gtk.Widgetter { | |||
| 472 | 473 | return tm.rootContainer | |
| 473 | 474 | } | |
| 474 | 475 | ||
| 476 | + | // GetWindow returns the GTK window backing the tab manager. | |
| 477 | + | func (tm *TabManager) GetWindow() *webkit.Window { | |
| 478 | + | tm.mu.RLock() | |
| 479 | + | defer tm.mu.RUnlock() | |
| 480 | + | ||
| 481 | + | return tm.window | |
| 482 | + | } | |
| 483 | + | ||
| 475 | 484 | // Cleanup performs cleanup of all tabs and resources. | |
| 476 | 485 | func (tm *TabManager) Cleanup() { | |
| 477 | 486 | tm.mu.Lock() | |
| @@ -23,6 +23,7 @@ type TabManager struct { | |||||
| 23 | rootContainer gtk.Widgetter // Main vertical box containing tab bar + content | 23 | rootContainer gtk.Widgetter // Main vertical box containing tab bar + content | ||
| 24 | tabBar gtk.Widgetter // Horizontal tab bar container | 24 | tabBar gtk.Widgetter // Horizontal tab bar container | ||
| 25 | ContentArea gtk.Widgetter // Container for active workspace (exported for border styling) | 25 | ContentArea gtk.Widgetter // Container for active workspace (exported for border styling) | ||
| 26 | + | tabModeTarget gtk.Widgetter // Widget currently holding tab mode border margins | |||
| 26 | 27 | ||||
| 27 | // Modal state | 28 | // Modal state | ||
| 28 | tabModeActive bool | 29 | tabModeActive bool | ||
| @@ -472,6 +473,14 @@ func (tm *TabManager) GetRootContainer() gtk.Widgetter { | |||||
| 472 | return tm.rootContainer | 473 | return tm.rootContainer | ||
| 473 | } | 474 | } | ||
| 474 | 475 | ||||
| 476 | + | // GetWindow returns the GTK window backing the tab manager. | |||
| 477 | + | func (tm *TabManager) GetWindow() *webkit.Window { | |||
| 478 | + | tm.mu.RLock() | |||
| 479 | + | defer tm.mu.RUnlock() | |||
| 480 | + | ||||
| 481 | + | return tm.window | |||
| 482 | + | } | |||
| 483 | + | ||||
| 475 | // Cleanup performs cleanup of all tabs and resources. | 484 | // Cleanup performs cleanup of all tabs and resources. | ||
| 476 | func (tm *TabManager) Cleanup() { | 485 | func (tm *TabManager) Cleanup() { | ||
| 477 | tm.mu.Lock() | 486 | tm.mu.Lock() | ||
M internal/app/browser/tab_mode.go +46 -14
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/bnema/dumber/internal/logging" | |
| 8 | 8 | "github.com/bnema/dumber/pkg/webkit" | |
| 9 | + | "github.com/diamondburned/gotk4/pkg/gtk/v4" | |
| 9 | 10 | ) | |
| 10 | 11 | ||
| 11 | 12 | // EnterTabMode activates the modal tab management mode. | |
| @@ -209,51 +210,82 @@ func (tm *TabManager) handleRenameTab() { | |||
| 209 | 210 | func (tm *TabManager) applyTabModeBorder() { | |
| 210 | 211 | logging.Debug("[tab-mode] applyTabModeBorder: entry") | |
| 211 | 212 | ||
| 212 | - | if tm.window == nil || tm.ContentArea == nil { | |
| 213 | - | logging.Debug("[tab-mode] applyTabModeBorder: window or ContentArea is nil, returning") | |
| 213 | + | if tm.window == nil { | |
| 214 | + | logging.Debug("[tab-mode] applyTabModeBorder: window is nil, returning") | |
| 214 | 215 | return | |
| 215 | 216 | } | |
| 216 | 217 | ||
| 217 | - | logging.Debug("[tab-mode] applyTabModeBorder: applying margins to content area") | |
| 218 | + | // Determine which container should receive the margins: | |
| 219 | + | // - Prefer the root container (direct child of the window) so the window background shows | |
| 220 | + | // - Fallback to ContentArea for older builds | |
| 221 | + | var target gtk.Widgetter | |
| 222 | + | if tm.rootContainer != nil { | |
| 223 | + | target = tm.rootContainer | |
| 224 | + | logging.Debug("[tab-mode] applyTabModeBorder: using root container for border") | |
| 225 | + | } else if tm.ContentArea != nil { | |
| 226 | + | target = tm.ContentArea | |
| 227 | + | logging.Debug("[tab-mode] applyTabModeBorder: using content area fallback for border") | |
| 228 | + | } else { | |
| 229 | + | logging.Debug("[tab-mode] applyTabModeBorder: no container available, returning") | |
| 230 | + | return | |
| 231 | + | } | |
| 232 | + | ||
| 233 | + | // Remember the container for removal | |
| 234 | + | tm.tabModeTarget = target | |
| 218 | 235 | ||
| 219 | - | // Apply 4px margins to content area to create space for the border | |
| 220 | - | webkit.WidgetSetMargin(tm.ContentArea, 4) | |
| 236 | + | // Apply 4px margins to create space for the border | |
| 237 | + | webkit.WidgetSetMargin(target, 4) | |
| 221 | 238 | ||
| 222 | 239 | // Add CSS class to window for background color (the "border" color shows in the margin space) | |
| 223 | 240 | gtkWindow := tm.window.AsWindow() | |
| 224 | 241 | webkit.WidgetAddCSSClass(gtkWindow, "tab-mode-active") | |
| 225 | 242 | ||
| 226 | 243 | // Force resize/allocation to apply margin changes immediately | |
| 227 | - | webkit.WidgetQueueResize(tm.ContentArea) | |
| 228 | - | webkit.WidgetQueueAllocate(tm.ContentArea) | |
| 244 | + | webkit.WidgetQueueResize(target) | |
| 245 | + | webkit.WidgetQueueAllocate(target) | |
| 229 | 246 | ||
| 230 | 247 | // Queue redraw to show changes | |
| 231 | 248 | webkit.WidgetQueueDraw(gtkWindow) | |
| 232 | - | webkit.WidgetQueueDraw(tm.ContentArea) | |
| 249 | + | webkit.WidgetQueueDraw(target) | |
| 233 | 250 | ||
| 234 | 251 | logging.Debug("[tab-mode] applyTabModeBorder: border applied successfully") | |
| 235 | 252 | } | |
| 236 | 253 | ||
| 237 | 254 | // removeTabModeBorder removes the visual indicator when exiting tab mode. | |
| 238 | 255 | func (tm *TabManager) removeTabModeBorder() { | |
| 239 | - | if tm.window == nil || tm.ContentArea == nil { | |
| 256 | + | if tm.window == nil { | |
| 257 | + | return | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | // Use whichever container received margins | |
| 261 | + | target := tm.tabModeTarget | |
| 262 | + | if target == nil { | |
| 263 | + | if tm.rootContainer != nil { | |
| 264 | + | target = tm.rootContainer | |
| 265 | + | } else { | |
| 266 | + | target = tm.ContentArea | |
| 267 | + | } | |
| 268 | + | } | |
| 269 | + | if target == nil { | |
| 240 | 270 | return | |
| 241 | 271 | } | |
| 242 | 272 | ||
| 243 | - | // Remove margins from content area | |
| 244 | - | webkit.WidgetSetMargin(tm.ContentArea, 0) | |
| 273 | + | // Remove margins | |
| 274 | + | webkit.WidgetSetMargin(target, 0) | |
| 245 | 275 | ||
| 246 | 276 | // Remove CSS class from window | |
| 247 | 277 | gtkWindow := tm.window.AsWindow() | |
| 248 | 278 | webkit.WidgetRemoveCSSClass(gtkWindow, "tab-mode-active") | |
| 249 | 279 | ||
| 250 | 280 | // Force resize/allocation to apply margin changes immediately | |
| 251 | - | webkit.WidgetQueueResize(tm.ContentArea) | |
| 252 | - | webkit.WidgetQueueAllocate(tm.ContentArea) | |
| 281 | + | webkit.WidgetQueueResize(target) | |
| 282 | + | webkit.WidgetQueueAllocate(target) | |
| 253 | 283 | ||
| 254 | 284 | // Queue redraw to show changes | |
| 255 | 285 | webkit.WidgetQueueDraw(gtkWindow) | |
| 256 | - | webkit.WidgetQueueDraw(tm.ContentArea) | |
| 286 | + | webkit.WidgetQueueDraw(target) | |
| 287 | + | ||
| 288 | + | tm.tabModeTarget = nil | |
| 257 | 289 | ||
| 258 | 290 | logging.Debug("[tab-mode] Removed tab mode visual indicator") | |
| 259 | 291 | } | |
| @@ -6,6 +6,7 @@ import ( | |||||
| 6 | 6 | ||||
| 7 | "github.com/bnema/dumber/internal/logging" | 7 | "github.com/bnema/dumber/internal/logging" | ||
| 8 | "github.com/bnema/dumber/pkg/webkit" | 8 | "github.com/bnema/dumber/pkg/webkit" | ||
| 9 | + | "github.com/diamondburned/gotk4/pkg/gtk/v4" | |||
| 9 | ) | 10 | ) | ||
| 10 | 11 | ||||
| 11 | // EnterTabMode activates the modal tab management mode. | 12 | // EnterTabMode activates the modal tab management mode. | ||
| @@ -209,51 +210,82 @@ func (tm *TabManager) handleRenameTab() { | |||||
| 209 | func (tm *TabManager) applyTabModeBorder() { | 210 | func (tm *TabManager) applyTabModeBorder() { | ||
| 210 | logging.Debug("[tab-mode] applyTabModeBorder: entry") | 211 | logging.Debug("[tab-mode] applyTabModeBorder: entry") | ||
| 211 | 212 | ||||
| 212 | - | if tm.window == nil || tm.ContentArea == nil { | 213 | + | if tm.window == nil { |
| 213 | - | logging.Debug("[tab-mode] applyTabModeBorder: window or ContentArea is nil, returning") | 214 | + | logging.Debug("[tab-mode] applyTabModeBorder: window is nil, returning") |
| 214 | return | 215 | return | ||
| 215 | } | 216 | } | ||
| 216 | 217 | ||||
| 217 | - | logging.Debug("[tab-mode] applyTabModeBorder: applying margins to content area") | 218 | + | // Determine which container should receive the margins: |
| 219 | + | // - Prefer the root container (direct child of the window) so the window background shows | |||
| 220 | + | // - Fallback to ContentArea for older builds | |||
| 221 | + | var target gtk.Widgetter | |||
| 222 | + | if tm.rootContainer != nil { | |||
| 223 | + | target = tm.rootContainer | |||
| 224 | + | logging.Debug("[tab-mode] applyTabModeBorder: using root container for border") | |||
| 225 | + | } else if tm.ContentArea != nil { | |||
| 226 | + | target = tm.ContentArea | |||
| 227 | + | logging.Debug("[tab-mode] applyTabModeBorder: using content area fallback for border") | |||
| 228 | + | } else { | |||
| 229 | + | logging.Debug("[tab-mode] applyTabModeBorder: no container available, returning") | |||
| 230 | + | return | |||
| 231 | + | } | |||
| 232 | + | ||||
| 233 | + | // Remember the container for removal | |||
| 234 | + | tm.tabModeTarget = target | |||
| 218 | 235 | ||||
| 219 | - | // Apply 4px margins to content area to create space for the border | 236 | + | // Apply 4px margins to create space for the border |
| 220 | - | webkit.WidgetSetMargin(tm.ContentArea, 4) | 237 | + | webkit.WidgetSetMargin(target, 4) |
| 221 | 238 | ||||
| 222 | // Add CSS class to window for background color (the "border" color shows in the margin space) | 239 | // Add CSS class to window for background color (the "border" color shows in the margin space) | ||
| 223 | gtkWindow := tm.window.AsWindow() | 240 | gtkWindow := tm.window.AsWindow() | ||
| 224 | webkit.WidgetAddCSSClass(gtkWindow, "tab-mode-active") | 241 | webkit.WidgetAddCSSClass(gtkWindow, "tab-mode-active") | ||
| 225 | 242 | ||||
| 226 | // Force resize/allocation to apply margin changes immediately | 243 | // Force resize/allocation to apply margin changes immediately | ||
| 227 | - | webkit.WidgetQueueResize(tm.ContentArea) | 244 | + | webkit.WidgetQueueResize(target) |
| 228 | - | webkit.WidgetQueueAllocate(tm.ContentArea) | 245 | + | webkit.WidgetQueueAllocate(target) |
| 229 | 246 | ||||
| 230 | // Queue redraw to show changes | 247 | // Queue redraw to show changes | ||
| 231 | webkit.WidgetQueueDraw(gtkWindow) | 248 | webkit.WidgetQueueDraw(gtkWindow) | ||
| 232 | - | webkit.WidgetQueueDraw(tm.ContentArea) | 249 | + | webkit.WidgetQueueDraw(target) |
| 233 | 250 | ||||
| 234 | logging.Debug("[tab-mode] applyTabModeBorder: border applied successfully") | 251 | logging.Debug("[tab-mode] applyTabModeBorder: border applied successfully") | ||
| 235 | } | 252 | } | ||
| 236 | 253 | ||||
| 237 | // removeTabModeBorder removes the visual indicator when exiting tab mode. | 254 | // removeTabModeBorder removes the visual indicator when exiting tab mode. | ||
| 238 | func (tm *TabManager) removeTabModeBorder() { | 255 | func (tm *TabManager) removeTabModeBorder() { | ||
| 239 | - | if tm.window == nil || tm.ContentArea == nil { | 256 | + | if tm.window == nil { |
| 257 | + | return | |||
| 258 | + | } | |||
| 259 | + | ||||
| 260 | + | // Use whichever container received margins | |||
| 261 | + | target := tm.tabModeTarget | |||
| 262 | + | if target == nil { | |||
| 263 | + | if tm.rootContainer != nil { | |||
| 264 | + | target = tm.rootContainer | |||
| 265 | + | } else { | |||
| 266 | + | target = tm.ContentArea | |||
| 267 | + | } | |||
| 268 | + | } | |||
| 269 | + | if target == nil { | |||
| 240 | return | 270 | return | ||
| 241 | } | 271 | } | ||
| 242 | 272 | ||||
| 243 | - | // Remove margins from content area | 273 | + | // Remove margins |
| 244 | - | webkit.WidgetSetMargin(tm.ContentArea, 0) | 274 | + | webkit.WidgetSetMargin(target, 0) |
| 245 | 275 | ||||
| 246 | // Remove CSS class from window | 276 | // Remove CSS class from window | ||
| 247 | gtkWindow := tm.window.AsWindow() | 277 | gtkWindow := tm.window.AsWindow() | ||
| 248 | webkit.WidgetRemoveCSSClass(gtkWindow, "tab-mode-active") | 278 | webkit.WidgetRemoveCSSClass(gtkWindow, "tab-mode-active") | ||
| 249 | 279 | ||||
| 250 | // Force resize/allocation to apply margin changes immediately | 280 | // Force resize/allocation to apply margin changes immediately | ||
| 251 | - | webkit.WidgetQueueResize(tm.ContentArea) | 281 | + | webkit.WidgetQueueResize(target) |
| 252 | - | webkit.WidgetQueueAllocate(tm.ContentArea) | 282 | + | webkit.WidgetQueueAllocate(target) |
| 253 | 283 | ||||
| 254 | // Queue redraw to show changes | 284 | // Queue redraw to show changes | ||
| 255 | webkit.WidgetQueueDraw(gtkWindow) | 285 | webkit.WidgetQueueDraw(gtkWindow) | ||
| 256 | - | webkit.WidgetQueueDraw(tm.ContentArea) | 286 | + | webkit.WidgetQueueDraw(target) |
| 287 | + | ||||
| 288 | + | tm.tabModeTarget = nil | |||
| 257 | 289 | ||||
| 258 | logging.Debug("[tab-mode] Removed tab mode visual indicator") | 290 | logging.Debug("[tab-mode] Removed tab mode visual indicator") | ||
| 259 | } | 291 | } | ||
M internal/app/browser/workspace_manager.go +28 -0
| @@ -73,6 +73,34 @@ type WorkspaceManager struct { | |||
| 73 | 73 | pendingIdle map[uintptr][]*paneNode | |
| 74 | 74 | } | |
| 75 | 75 | ||
| 76 | + | // resolveWindow ensures the workspace has a reference to the GTK window. | |
| 77 | + | func (wm *WorkspaceManager) resolveWindow() *webkit.Window { | |
| 78 | + | if wm == nil { | |
| 79 | + | return nil | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | if wm.window != nil { | |
| 83 | + | return wm.window | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | if wm.app != nil { | |
| 87 | + | if wm.app.tabManager != nil { | |
| 88 | + | if win := wm.app.tabManager.GetWindow(); win != nil { | |
| 89 | + | wm.window = win | |
| 90 | + | return win | |
| 91 | + | } | |
| 92 | + | } | |
| 93 | + | if wm.app.webView != nil { | |
| 94 | + | if win := wm.app.webView.Window(); win != nil { | |
| 95 | + | wm.window = win | |
| 96 | + | return win | |
| 97 | + | } | |
| 98 | + | } | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | return nil | |
| 102 | + | } | |
| 103 | + | ||
| 76 | 104 | // Workspace navigation shortcuts are now handled globally by WindowShortcutHandler | |
| 77 | 105 | ||
| 78 | 106 | // NewWorkspaceManager builds a workspace manager rooted at the provided pane. | |
| @@ -73,6 +73,34 @@ type WorkspaceManager struct { | |||||
| 73 | pendingIdle map[uintptr][]*paneNode | 73 | pendingIdle map[uintptr][]*paneNode | ||
| 74 | } | 74 | } | ||
| 75 | 75 | ||||
| 76 | + | // resolveWindow ensures the workspace has a reference to the GTK window. | |||
| 77 | + | func (wm *WorkspaceManager) resolveWindow() *webkit.Window { | |||
| 78 | + | if wm == nil { | |||
| 79 | + | return nil | |||
| 80 | + | } | |||
| 81 | + | ||||
| 82 | + | if wm.window != nil { | |||
| 83 | + | return wm.window | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | if wm.app != nil { | |||
| 87 | + | if wm.app.tabManager != nil { | |||
| 88 | + | if win := wm.app.tabManager.GetWindow(); win != nil { | |||
| 89 | + | wm.window = win | |||
| 90 | + | return win | |||
| 91 | + | } | |||
| 92 | + | } | |||
| 93 | + | if wm.app.webView != nil { | |||
| 94 | + | if win := wm.app.webView.Window(); win != nil { | |||
| 95 | + | wm.window = win | |||
| 96 | + | return win | |||
| 97 | + | } | |||
| 98 | + | } | |||
| 99 | + | } | |||
| 100 | + | ||||
| 101 | + | return nil | |||
| 102 | + | } | |||
| 103 | + | ||||
| 76 | // Workspace navigation shortcuts are now handled globally by WindowShortcutHandler | 104 | // Workspace navigation shortcuts are now handled globally by WindowShortcutHandler | ||
| 77 | 105 | ||||
| 78 | // NewWorkspaceManager builds a workspace manager rooted at the provided pane. | 106 | // NewWorkspaceManager builds a workspace manager rooted at the provided pane. | ||
M internal/app/browser/workspace_pane_mode.go +21 -11
| @@ -207,18 +207,27 @@ func (wm *WorkspaceManager) dispatchPaneModeEvent(event string, detail string) { | |||
| 207 | 207 | ||
| 208 | 208 | // applyPaneModeBorder applies pane mode visual indicator using GTK margins | |
| 209 | 209 | func (wm *WorkspaceManager) applyPaneModeBorder() { | |
| 210 | - | if wm.window == nil { | |
| 210 | + | window := wm.resolveWindow() | |
| 211 | + | if window == nil { | |
| 212 | + | log.Printf("[pane-mode] Cannot apply border: missing window reference") | |
| 211 | 213 | return | |
| 212 | 214 | } | |
| 213 | 215 | ||
| 214 | 216 | // Determine which container to apply margins to: | |
| 215 | - | // - If tab manager exists, apply to its ContentArea (so window background shows through) | |
| 217 | + | // - If tab manager exists, apply to its root container (direct child of window) | |
| 218 | + | // so margins reveal the window background color | |
| 216 | 219 | // - Otherwise, apply to workspace root (direct child of window) | |
| 217 | 220 | var targetContainer gtk.Widgetter | |
| 218 | - | if wm.app != nil && wm.app.tabManager != nil && wm.app.tabManager.ContentArea != nil { | |
| 219 | - | // Tab environment: apply margins to tab manager's content area | |
| 220 | - | targetContainer = wm.app.tabManager.ContentArea | |
| 221 | - | log.Printf("[pane-mode] Using tab manager's content area for border") | |
| 221 | + | if wm.app != nil && wm.app.tabManager != nil { | |
| 222 | + | if rootContainer := wm.app.tabManager.GetRootContainer(); rootContainer != nil { | |
| 223 | + | // Tab environment: apply margins to the root container that's attached to the window | |
| 224 | + | targetContainer = rootContainer | |
| 225 | + | log.Printf("[pane-mode] Using tab manager root container for border") | |
| 226 | + | } else if wm.app.tabManager.ContentArea != nil { | |
| 227 | + | // Fallback: older builds where root container isn't available yet | |
| 228 | + | targetContainer = wm.app.tabManager.ContentArea | |
| 229 | + | log.Printf("[pane-mode] Using tab manager content area fallback for border") | |
| 230 | + | } | |
| 222 | 231 | } else if wm.root != nil && wm.root.container != nil { | |
| 223 | 232 | // Non-tab environment: apply margins to workspace root | |
| 224 | 233 | targetContainer = wm.root.container | |
| @@ -234,14 +243,14 @@ func (wm *WorkspaceManager) applyPaneModeBorder() { | |||
| 234 | 243 | webkit.WidgetSetMargin(targetContainer, 4) | |
| 235 | 244 | ||
| 236 | 245 | // Add CSS class to window for background color (the "border" color shows in the margin space) | |
| 237 | - | webkit.WidgetAddCSSClass(wm.window.AsWindow(), "pane-mode-active") | |
| 246 | + | webkit.WidgetAddCSSClass(window.AsWindow(), "pane-mode-active") | |
| 238 | 247 | ||
| 239 | 248 | // Force resize/allocation to apply margin changes immediately | |
| 240 | 249 | webkit.WidgetQueueResize(targetContainer) | |
| 241 | 250 | webkit.WidgetQueueAllocate(targetContainer) | |
| 242 | 251 | ||
| 243 | 252 | // Queue redraw to show changes | |
| 244 | - | webkit.WidgetQueueDraw(wm.window.AsWindow()) | |
| 253 | + | webkit.WidgetQueueDraw(window.AsWindow()) | |
| 245 | 254 | webkit.WidgetQueueDraw(targetContainer) | |
| 246 | 255 | ||
| 247 | 256 | log.Printf("[pane-mode] Applied border using margins (container=%p)", targetContainer) | |
| @@ -249,7 +258,8 @@ func (wm *WorkspaceManager) applyPaneModeBorder() { | |||
| 249 | 258 | ||
| 250 | 259 | // removePaneModeBorder removes the pane mode visual indicator | |
| 251 | 260 | func (wm *WorkspaceManager) removePaneModeBorder() { | |
| 252 | - | if wm.window == nil { | |
| 261 | + | window := wm.resolveWindow() | |
| 262 | + | if window == nil { | |
| 253 | 263 | return | |
| 254 | 264 | } | |
| 255 | 265 | ||
| @@ -268,14 +278,14 @@ func (wm *WorkspaceManager) removePaneModeBorder() { | |||
| 268 | 278 | webkit.WidgetSetMargin(container, 0) | |
| 269 | 279 | ||
| 270 | 280 | // Remove CSS class from window | |
| 271 | - | webkit.WidgetRemoveCSSClass(wm.window.AsWindow(), "pane-mode-active") | |
| 281 | + | webkit.WidgetRemoveCSSClass(window.AsWindow(), "pane-mode-active") | |
| 272 | 282 | ||
| 273 | 283 | // Force resize/allocation to apply margin changes immediately | |
| 274 | 284 | webkit.WidgetQueueResize(container) | |
| 275 | 285 | webkit.WidgetQueueAllocate(container) | |
| 276 | 286 | ||
| 277 | 287 | // Queue redraw to show changes | |
| 278 | - | webkit.WidgetQueueDraw(wm.window.AsWindow()) | |
| 288 | + | webkit.WidgetQueueDraw(window.AsWindow()) | |
| 279 | 289 | webkit.WidgetQueueDraw(container) | |
| 280 | 290 | ||
| 281 | 291 | // Clear the saved container reference | |
| @@ -207,18 +207,27 @@ func (wm *WorkspaceManager) dispatchPaneModeEvent(event string, detail string) { | |||||
| 207 | 207 | ||||
| 208 | // applyPaneModeBorder applies pane mode visual indicator using GTK margins | 208 | // applyPaneModeBorder applies pane mode visual indicator using GTK margins | ||
| 209 | func (wm *WorkspaceManager) applyPaneModeBorder() { | 209 | func (wm *WorkspaceManager) applyPaneModeBorder() { | ||
| 210 | - | if wm.window == nil { | 210 | + | window := wm.resolveWindow() |
| 211 | + | if window == nil { | |||
| 212 | + | log.Printf("[pane-mode] Cannot apply border: missing window reference") | |||
| 211 | return | 213 | return | ||
| 212 | } | 214 | } | ||
| 213 | 215 | ||||
| 214 | // Determine which container to apply margins to: | 216 | // Determine which container to apply margins to: | ||
| 215 | - | // - If tab manager exists, apply to its ContentArea (so window background shows through) | 217 | + | // - If tab manager exists, apply to its root container (direct child of window) |
| 218 | + | // so margins reveal the window background color | |||
| 216 | // - Otherwise, apply to workspace root (direct child of window) | 219 | // - Otherwise, apply to workspace root (direct child of window) | ||
| 217 | var targetContainer gtk.Widgetter | 220 | var targetContainer gtk.Widgetter | ||
| 218 | - | if wm.app != nil && wm.app.tabManager != nil && wm.app.tabManager.ContentArea != nil { | 221 | + | if wm.app != nil && wm.app.tabManager != nil { |
| 219 | - | // Tab environment: apply margins to tab manager's content area | 222 | + | if rootContainer := wm.app.tabManager.GetRootContainer(); rootContainer != nil { |
| 220 | - | targetContainer = wm.app.tabManager.ContentArea | 223 | + | // Tab environment: apply margins to the root container that's attached to the window |
| 221 | - | log.Printf("[pane-mode] Using tab manager's content area for border") | 224 | + | targetContainer = rootContainer |
| 225 | + | log.Printf("[pane-mode] Using tab manager root container for border") | |||
| 226 | + | } else if wm.app.tabManager.ContentArea != nil { | |||
| 227 | + | // Fallback: older builds where root container isn't available yet | |||
| 228 | + | targetContainer = wm.app.tabManager.ContentArea | |||
| 229 | + | log.Printf("[pane-mode] Using tab manager content area fallback for border") | |||
| 230 | + | } | |||
| 222 | } else if wm.root != nil && wm.root.container != nil { | 231 | } else if wm.root != nil && wm.root.container != nil { | ||
| 223 | // Non-tab environment: apply margins to workspace root | 232 | // Non-tab environment: apply margins to workspace root | ||
| 224 | targetContainer = wm.root.container | 233 | targetContainer = wm.root.container | ||
| @@ -234,14 +243,14 @@ func (wm *WorkspaceManager) applyPaneModeBorder() { | |||||
| 234 | webkit.WidgetSetMargin(targetContainer, 4) | 243 | webkit.WidgetSetMargin(targetContainer, 4) | ||
| 235 | 244 | ||||
| 236 | // Add CSS class to window for background color (the "border" color shows in the margin space) | 245 | // Add CSS class to window for background color (the "border" color shows in the margin space) | ||
| 237 | - | webkit.WidgetAddCSSClass(wm.window.AsWindow(), "pane-mode-active") | 246 | + | webkit.WidgetAddCSSClass(window.AsWindow(), "pane-mode-active") |
| 238 | 247 | ||||
| 239 | // Force resize/allocation to apply margin changes immediately | 248 | // Force resize/allocation to apply margin changes immediately | ||
| 240 | webkit.WidgetQueueResize(targetContainer) | 249 | webkit.WidgetQueueResize(targetContainer) | ||
| 241 | webkit.WidgetQueueAllocate(targetContainer) | 250 | webkit.WidgetQueueAllocate(targetContainer) | ||
| 242 | 251 | ||||
| 243 | // Queue redraw to show changes | 252 | // Queue redraw to show changes | ||
| 244 | - | webkit.WidgetQueueDraw(wm.window.AsWindow()) | 253 | + | webkit.WidgetQueueDraw(window.AsWindow()) |
| 245 | webkit.WidgetQueueDraw(targetContainer) | 254 | webkit.WidgetQueueDraw(targetContainer) | ||
| 246 | 255 | ||||
| 247 | log.Printf("[pane-mode] Applied border using margins (container=%p)", targetContainer) | 256 | log.Printf("[pane-mode] Applied border using margins (container=%p)", targetContainer) | ||
| @@ -249,7 +258,8 @@ func (wm *WorkspaceManager) applyPaneModeBorder() { | |||||
| 249 | 258 | ||||
| 250 | // removePaneModeBorder removes the pane mode visual indicator | 259 | // removePaneModeBorder removes the pane mode visual indicator | ||
| 251 | func (wm *WorkspaceManager) removePaneModeBorder() { | 260 | func (wm *WorkspaceManager) removePaneModeBorder() { | ||
| 252 | - | if wm.window == nil { | 261 | + | window := wm.resolveWindow() |
| 262 | + | if window == nil { | |||
| 253 | return | 263 | return | ||
| 254 | } | 264 | } | ||
| 255 | 265 | ||||
| @@ -268,14 +278,14 @@ func (wm *WorkspaceManager) removePaneModeBorder() { | |||||
| 268 | webkit.WidgetSetMargin(container, 0) | 278 | webkit.WidgetSetMargin(container, 0) | ||
| 269 | 279 | ||||
| 270 | // Remove CSS class from window | 280 | // Remove CSS class from window | ||
| 271 | - | webkit.WidgetRemoveCSSClass(wm.window.AsWindow(), "pane-mode-active") | 281 | + | webkit.WidgetRemoveCSSClass(window.AsWindow(), "pane-mode-active") |
| 272 | 282 | ||||
| 273 | // Force resize/allocation to apply margin changes immediately | 283 | // Force resize/allocation to apply margin changes immediately | ||
| 274 | webkit.WidgetQueueResize(container) | 284 | webkit.WidgetQueueResize(container) | ||
| 275 | webkit.WidgetQueueAllocate(container) | 285 | webkit.WidgetQueueAllocate(container) | ||
| 276 | 286 | ||||
| 277 | // Queue redraw to show changes | 287 | // Queue redraw to show changes | ||
| 278 | - | webkit.WidgetQueueDraw(wm.window.AsWindow()) | 288 | + | webkit.WidgetQueueDraw(window.AsWindow()) |
| 279 | webkit.WidgetQueueDraw(container) | 289 | webkit.WidgetQueueDraw(container) | ||
| 280 | 290 | ||||
| 281 | // Clear the saved container reference | 291 | // Clear the saved container reference | ||