refactor(browser): migrate stacked_panes.go to gotk4 API
Complete migration of stacked pane widget operations to gotk4: Widget Type Changes: - titleBarToPane: map[uintptr] → map[uint64] - createTitleBar/createTitleBarWithTitle: return gtk.Widgetter - Widget variables: uintptr → gtk.Widgetter throughout Widget Operations Updated: - gtk.NewBox() instead of webkit.NewBox() - box.Append/Prepend/Remove() instead of webkit.Box*() - widget.SetVisible/Show/Hide() instead of webkit.Widget*() - widget.Parent/Unparent() instead of webkit.WidgetGet/Unparent() - Type assertions for Box/Paned operations Null Checks: - All widget != 0 → widget != nil - All widget == 0 → widget == nil Click Handlers: - Commented out WidgetAttachClickHandler (TODO: implement with gtk.GestureClick) - Updated handleTitleBarClick to use uint64 ID Functions Updated: - prepareNewStackedPane - addPaneToExistingStack - convertToStackedContainer - detachFromParent/reattachToParent - UpdateStackVisibility - createTitleBar/createTitleBarWithTitle - updateTitleBarLabel - CloseStackedPane - handleTitleBarClick Remaining: Click handlers need gtk.GestureClick implementation
- commit
- 2716a7313a2e9eb462ee674fc5a350dd07351af6
- author
- bamen · · 2025-10-07 19:26 UTC
- committer
- bamen · · 2025-10-07 19:20 UTC
- parent
- 7ce5a26
- signature
- unsigned
1 file · +175 -164
M internal/app/browser/stacked_panes.go +175 -164
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/dumber/internal/cache" | |
| 12 | 12 | "github.com/bnema/dumber/pkg/webkit" | |
| 13 | + | "github.com/diamondburned/gotk4/pkg/gtk/v4" | |
| 13 | 14 | ) | |
| 14 | 15 | ||
| 15 | 16 | const ( | |
| @@ -20,7 +21,7 @@ const ( | |||
| 20 | 21 | // StackedPaneManager handles all stacked pane operations | |
| 21 | 22 | type StackedPaneManager struct { | |
| 22 | 23 | wm *WorkspaceManager | |
| 23 | - | titleBarToPane map[uintptr]*paneNode | |
| 24 | + | titleBarToPane map[uint64]*paneNode | |
| 24 | 25 | nextTitleBarID uint64 | |
| 25 | 26 | } | |
| 26 | 27 | ||
| @@ -28,7 +29,7 @@ type StackedPaneManager struct { | |||
| 28 | 29 | func NewStackedPaneManager(wm *WorkspaceManager) *StackedPaneManager { | |
| 29 | 30 | return &StackedPaneManager{ | |
| 30 | 31 | wm: wm, | |
| 31 | - | titleBarToPane: make(map[uintptr]*paneNode), | |
| 32 | + | titleBarToPane: make(map[uint64]*paneNode), | |
| 32 | 33 | } | |
| 33 | 34 | } | |
| 34 | 35 | ||
| @@ -84,12 +85,12 @@ func (spm *StackedPaneManager) prepareNewStackedPane() (*paneNode, error) { | |||
| 84 | 85 | } | |
| 85 | 86 | ||
| 86 | 87 | newContainer := newPane.webView.RootWidget() | |
| 87 | - | if newContainer == 0 { | |
| 88 | + | if newContainer == nil { | |
| 88 | 89 | return nil, errors.New("new pane missing container") | |
| 89 | 90 | } | |
| 90 | - | webkit.WidgetSetHExpand(newContainer, true) | |
| 91 | - | webkit.WidgetSetVExpand(newContainer, true) | |
| 92 | - | webkit.WidgetRealizeInContainer(newContainer) | |
| 91 | + | newContainer.SetHExpand(true) | |
| 92 | + | newContainer.SetVExpand(true) | |
| 93 | + | newContainer.Realize() | |
| 93 | 94 | ||
| 94 | 95 | // Create the new leaf node | |
| 95 | 96 | newLeaf := &paneNode{ | |
| @@ -104,8 +105,10 @@ func (spm *StackedPaneManager) prepareNewStackedPane() (*paneNode, error) { | |||
| 104 | 105 | spm.wm.setTitleBar(newLeaf, newTitleBar) | |
| 105 | 106 | ||
| 106 | 107 | // Keep new container hidden initially - will be shown after transition | |
| 107 | - | webkit.WidgetSetVisible(newContainer, false) | |
| 108 | - | webkit.WidgetSetVisible(newTitleBar, false) | |
| 108 | + | newContainer.SetVisible(false) | |
| 109 | + | if newTitleBar != nil { | |
| 110 | + | newTitleBar.SetVisible(false) | |
| 111 | + | } | |
| 109 | 112 | ||
| 110 | 113 | return newLeaf, nil | |
| 111 | 114 | } | |
| @@ -141,9 +144,9 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||
| 141 | 144 | stackNode.stackedPanes[insertIndex] = newLeaf // Insert new pane | |
| 142 | 145 | ||
| 143 | 146 | // Unparent the new container before adding it to the stack (only if it has a parent) | |
| 144 | - | if newLeaf.container != 0 && webkit.WidgetIsValid(newLeaf.container) { | |
| 145 | - | if webkit.WidgetGetParent(newLeaf.container) != 0 { | |
| 146 | - | webkit.WidgetUnparent(newLeaf.container) | |
| 147 | + | if newLeaf.container != nil { | |
| 148 | + | if newLeaf.container.Parent() != nil { | |
| 149 | + | newLeaf.container.Unparent() | |
| 147 | 150 | } | |
| 148 | 151 | } | |
| 149 | 152 | ||
| @@ -151,32 +154,32 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||
| 151 | 154 | // Each pane has 2 widgets (titleBar, container), so position = insertIndex * 2 | |
| 152 | 155 | ||
| 153 | 156 | // Find the widget to insert after (based on insertIndex) | |
| 154 | - | var insertAfterWidget uintptr = 0 | |
| 157 | + | var insertAfterWidget gtk.Widgetter | |
| 155 | 158 | if insertIndex > 0 { | |
| 156 | 159 | // Insert after the previous pane's container | |
| 157 | 160 | prevPane := stackNode.stackedPanes[insertIndex-1] | |
| 158 | - | if prevPane.container != 0 { | |
| 161 | + | if prevPane.container != nil { | |
| 159 | 162 | insertAfterWidget = prevPane.container | |
| 160 | 163 | } | |
| 161 | 164 | } | |
| 162 | 165 | ||
| 163 | - | if insertAfterWidget != 0 { | |
| 166 | + | if insertAfterWidget != nil { | |
| 164 | 167 | // Insert titleBar after the previous pane's container | |
| 165 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | |
| 166 | - | webkit.BoxInsertChildAfter(stackNode.stackWrapper, newLeaf.titleBar, insertAfterWidget) | |
| 168 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { | |
| 169 | + | box.InsertChildAfter(newLeaf.titleBar, insertAfterWidget) | |
| 167 | 170 | } | |
| 168 | 171 | // Insert container after the newly inserted titleBar | |
| 169 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 && newLeaf.titleBar != 0 { | |
| 170 | - | webkit.BoxInsertChildAfter(stackNode.stackWrapper, newLeaf.container, newLeaf.titleBar) | |
| 172 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil && newLeaf.titleBar != nil { | |
| 173 | + | box.InsertChildAfter(newLeaf.container, newLeaf.titleBar) | |
| 171 | 174 | } | |
| 172 | - | log.Printf("[workspace] inserted widgets at position %d (after widget %#x)", insertIndex, insertAfterWidget) | |
| 175 | + | log.Printf("[workspace] inserted widgets at position %d (after widget %p)", insertIndex, insertAfterWidget) | |
| 173 | 176 | } else { | |
| 174 | 177 | // Insert at the beginning (insertIndex = 0) | |
| 175 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 { | |
| 176 | - | webkit.BoxPrepend(stackNode.stackWrapper, newLeaf.container) | |
| 178 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { | |
| 179 | + | box.Prepend(newLeaf.container) | |
| 177 | 180 | } | |
| 178 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | |
| 179 | - | webkit.BoxPrepend(stackNode.stackWrapper, newLeaf.titleBar) | |
| 181 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { | |
| 182 | + | box.Prepend(newLeaf.titleBar) | |
| 180 | 183 | } | |
| 181 | 184 | log.Printf("[workspace] prepended widgets at position 0") | |
| 182 | 185 | } | |
| @@ -189,27 +192,27 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||
| 189 | 192 | log.Printf("[workspace] converting pane to stacked: %p", target) | |
| 190 | 193 | ||
| 191 | 194 | // Create the wrapper container - this is what will be used by splitNode | |
| 192 | - | stackWrapperContainer := webkit.NewBox(webkit.OrientationVertical, 0) | |
| 193 | - | if stackWrapperContainer == 0 { | |
| 195 | + | stackWrapperContainer := gtk.NewBox(gtk.OrientationVertical, 0) | |
| 196 | + | if stackWrapperContainer == nil { | |
| 194 | 197 | return nil, 0, errors.New("failed to create stack wrapper container") | |
| 195 | 198 | } | |
| 196 | - | webkit.WidgetSetHExpand(stackWrapperContainer, true) | |
| 197 | - | webkit.WidgetSetVExpand(stackWrapperContainer, true) | |
| 198 | - | webkit.WidgetRealizeInContainer(stackWrapperContainer) // Ensures size request is set like regular panes | |
| 199 | + | stackWrapperContainer.SetHExpand(true) | |
| 200 | + | stackWrapperContainer.SetVExpand(true) | |
| 201 | + | stackWrapperContainer.Realize() // Ensures size request is set like regular panes | |
| 199 | 202 | ||
| 200 | 203 | // Add CSS class to the stack container for proper styling | |
| 201 | - | webkit.WidgetAddCSSClass(stackWrapperContainer, stackContainerClass) | |
| 204 | + | stackWrapperContainer.AddCSSClass(stackContainerClass) | |
| 202 | 205 | ||
| 203 | 206 | // Create the internal box for the actual stacked widgets (titles + webviews) | |
| 204 | - | stackInternalBox := webkit.NewBox(webkit.OrientationVertical, 0) | |
| 205 | - | if stackInternalBox == 0 { | |
| 207 | + | stackInternalBox := gtk.NewBox(gtk.OrientationVertical, 0) | |
| 208 | + | if stackInternalBox == nil { | |
| 206 | 209 | return nil, 0, errors.New("failed to create stack internal box") | |
| 207 | 210 | } | |
| 208 | - | webkit.WidgetSetHExpand(stackInternalBox, true) | |
| 209 | - | webkit.WidgetSetVExpand(stackInternalBox, true) | |
| 211 | + | stackInternalBox.SetHExpand(true) | |
| 212 | + | stackInternalBox.SetVExpand(true) | |
| 210 | 213 | ||
| 211 | 214 | // The internal box goes inside the wrapper | |
| 212 | - | webkit.BoxAppend(stackWrapperContainer, stackInternalBox) | |
| 215 | + | stackWrapperContainer.Append(stackInternalBox) | |
| 213 | 216 | ||
| 214 | 217 | // Get the existing container and parent info | |
| 215 | 218 | existingContainer := target.container | |
| @@ -219,15 +222,19 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||
| 219 | 222 | titleBar := spm.createTitleBar(target) | |
| 220 | 223 | ||
| 221 | 224 | // Keep existing container visible during transition to prevent rendering glitch | |
| 222 | - | webkit.WidgetSetVisible(titleBar, false) | |
| 225 | + | if titleBar != nil { | |
| 226 | + | titleBar.SetVisible(false) | |
| 227 | + | } | |
| 223 | 228 | ||
| 224 | 229 | // Detach existing container from its current parent first | |
| 225 | 230 | spm.detachFromParent(target, parent) | |
| 226 | 231 | ||
| 227 | 232 | // Build the complete stack structure with hidden widgets | |
| 228 | - | webkit.BoxAppend(stackInternalBox, titleBar) | |
| 229 | - | if existingContainer != 0 { | |
| 230 | - | webkit.BoxAppend(stackInternalBox, existingContainer) | |
| 233 | + | if titleBar != nil { | |
| 234 | + | stackInternalBox.Append(titleBar) | |
| 235 | + | } | |
| 236 | + | if existingContainer != nil { | |
| 237 | + | stackInternalBox.Append(existingContainer) | |
| 231 | 238 | } | |
| 232 | 239 | ||
| 233 | 240 | // Immediately reattach the stack wrapper to minimize visibility gap | |
| @@ -275,24 +282,24 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||
| 275 | 282 | ||
| 276 | 283 | // Unparent the new container before adding it to the stack | |
| 277 | 284 | // This is critical: even freshly created WebViews might have internal parent refs | |
| 278 | - | if newLeaf.container != 0 && webkit.WidgetIsValid(newLeaf.container) { | |
| 279 | - | parent := webkit.WidgetGetParent(newLeaf.container) | |
| 280 | - | if parent != 0 { | |
| 281 | - | log.Printf("[workspace] unparenting new pane container %#x from parent %#x before stack append", newLeaf.container, parent) | |
| 282 | - | webkit.WidgetUnparent(newLeaf.container) | |
| 285 | + | if newLeaf.container != nil { | |
| 286 | + | parentWidget := newLeaf.container.Parent() | |
| 287 | + | if parentWidget != nil { | |
| 288 | + | log.Printf("[workspace] unparenting new pane container %p from parent %p before stack append", newLeaf.container, parentWidget) | |
| 289 | + | newLeaf.container.Unparent() | |
| 283 | 290 | // Verify unparent succeeded | |
| 284 | - | if finalParent := webkit.WidgetGetParent(newLeaf.container); finalParent != 0 { | |
| 285 | - | log.Printf("[workspace] WARNING: container %#x still has parent %#x after unparent", newLeaf.container, finalParent) | |
| 291 | + | if finalParent := newLeaf.container.Parent(); finalParent != nil { | |
| 292 | + | log.Printf("[workspace] WARNING: container %p still has parent %p after unparent", newLeaf.container, finalParent) | |
| 286 | 293 | } | |
| 287 | 294 | } | |
| 288 | 295 | } | |
| 289 | 296 | ||
| 290 | 297 | // Add the new widgets to the internal stack box | |
| 291 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | |
| 292 | - | webkit.BoxAppend(stackNode.stackWrapper, newLeaf.titleBar) | |
| 298 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { | |
| 299 | + | box.Append(newLeaf.titleBar) | |
| 293 | 300 | } | |
| 294 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 { | |
| 295 | - | webkit.BoxAppend(stackNode.stackWrapper, newLeaf.container) | |
| 301 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { | |
| 302 | + | box.Append(newLeaf.container) | |
| 296 | 303 | } | |
| 297 | 304 | ||
| 298 | 305 | return stackNode, insertIndex, nil | |
| @@ -303,35 +310,35 @@ func (spm *StackedPaneManager) detachFromParent(target *paneNode, parent *paneNo | |||
| 303 | 310 | if parent == nil { | |
| 304 | 311 | // Target is the root - remove it from window | |
| 305 | 312 | if spm.wm.window != nil { | |
| 306 | - | spm.wm.window.SetChild(0) | |
| 313 | + | spm.wm.window.SetChild(nil) | |
| 307 | 314 | } | |
| 308 | 315 | // Unparent if it has a GTK parent | |
| 309 | - | if target.container != 0 && webkit.WidgetIsValid(target.container) { | |
| 310 | - | if webkit.WidgetGetParent(target.container) != 0 { | |
| 311 | - | webkit.WidgetUnparent(target.container) | |
| 316 | + | if target.container != nil { | |
| 317 | + | if target.container.Parent() != nil { | |
| 318 | + | target.container.Unparent() | |
| 312 | 319 | } | |
| 313 | 320 | } | |
| 314 | - | } else if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | |
| 321 | + | } else if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { | |
| 315 | 322 | // Target has a parent paned - remove it (automatically unparents in GTK4) | |
| 316 | 323 | if parent.left == target { | |
| 317 | - | webkit.PanedSetStartChild(parent.container, 0) | |
| 324 | + | paned.SetStartChild(nil) | |
| 318 | 325 | } else if parent.right == target { | |
| 319 | - | webkit.PanedSetEndChild(parent.container, 0) | |
| 326 | + | paned.SetEndChild(nil) | |
| 320 | 327 | } | |
| 321 | 328 | } | |
| 322 | 329 | } | |
| 323 | 330 | ||
| 324 | 331 | // reattachToParent attaches a container to the parent | |
| 325 | - | func (spm *StackedPaneManager) reattachToParent(container uintptr, target *paneNode, parent *paneNode) { | |
| 332 | + | func (spm *StackedPaneManager) reattachToParent(container gtk.Widgetter, target *paneNode, parent *paneNode) { | |
| 326 | 333 | if parent == nil { | |
| 327 | 334 | if spm.wm.window != nil { | |
| 328 | 335 | spm.wm.window.SetChild(container) | |
| 329 | 336 | } | |
| 330 | - | } else if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | |
| 337 | + | } else if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { | |
| 331 | 338 | if parent.left == target { | |
| 332 | - | webkit.PanedSetStartChild(parent.container, container) | |
| 339 | + | paned.SetStartChild(container) | |
| 333 | 340 | } else if parent.right == target { | |
| 334 | - | webkit.PanedSetEndChild(parent.container, container) | |
| 341 | + | paned.SetEndChild(container) | |
| 335 | 342 | } | |
| 336 | 343 | } | |
| 337 | 344 | } | |
| @@ -406,20 +413,20 @@ func (spm *StackedPaneManager) UpdateStackVisibility(stackNode *paneNode) { | |||
| 406 | 413 | for i, pane := range stackNode.stackedPanes { | |
| 407 | 414 | if i == activeIndex { | |
| 408 | 415 | // Active pane: show container, NEVER show title bar | |
| 409 | - | if pane.container != 0 { | |
| 410 | - | webkit.WidgetSetVisible(pane.container, true) | |
| 416 | + | if pane.container != nil { | |
| 417 | + | pane.container.SetVisible(true) | |
| 411 | 418 | } | |
| 412 | - | if pane.titleBar != 0 { | |
| 413 | - | webkit.WidgetSetVisible(pane.titleBar, false) // ABSOLUTE RULE: never visible for active pane | |
| 419 | + | if pane.titleBar != nil { | |
| 420 | + | pane.titleBar.SetVisible(false) // ABSOLUTE RULE: never visible for active pane | |
| 414 | 421 | } | |
| 415 | 422 | log.Printf("[workspace] active pane %d: container=visible, titleBar=HIDDEN", i) | |
| 416 | 423 | } else { | |
| 417 | 424 | // Inactive panes: hide container, show title bar | |
| 418 | - | if pane.container != 0 { | |
| 419 | - | webkit.WidgetSetVisible(pane.container, false) | |
| 425 | + | if pane.container != nil { | |
| 426 | + | pane.container.SetVisible(false) | |
| 420 | 427 | } | |
| 421 | - | if pane.titleBar != 0 { | |
| 422 | - | webkit.WidgetSetVisible(pane.titleBar, true) | |
| 428 | + | if pane.titleBar != nil { | |
| 429 | + | pane.titleBar.SetVisible(true) | |
| 423 | 430 | ||
| 424 | 431 | // Refresh title bar to pick up any newly downloaded favicons | |
| 425 | 432 | if pane.pane != nil && pane.pane.webView != nil { | |
| @@ -513,7 +520,7 @@ func (spm *StackedPaneManager) NavigateStack(direction string) bool { | |||
| 513 | 520 | } | |
| 514 | 521 | ||
| 515 | 522 | // createTitleBar creates a title bar widget for a pane in a stack | |
| 516 | - | func (spm *StackedPaneManager) createTitleBar(pane *paneNode) uintptr { | |
| 523 | + | func (spm *StackedPaneManager) createTitleBar(pane *paneNode) gtk.Widgetter { | |
| 517 | 524 | // Get the actual title and URL from the WebView | |
| 518 | 525 | var titleText, pageURL string | |
| 519 | 526 | if pane.pane != nil && pane.pane.webView != nil { | |
| @@ -527,15 +534,16 @@ func (spm *StackedPaneManager) createTitleBar(pane *paneNode) uintptr { | |||
| 527 | 534 | } | |
| 528 | 535 | ||
| 529 | 536 | titleBar := spm.createTitleBarWithTitle(titleText, pageURL) | |
| 530 | - | if titleBar != 0 { | |
| 537 | + | if titleBar != nil { | |
| 531 | 538 | // Store the mapping from titleBar ID to pane | |
| 532 | 539 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | |
| 533 | - | spm.titleBarToPane[uintptr(titleBarID)] = pane | |
| 540 | + | spm.titleBarToPane[titleBarID] = pane | |
| 534 | 541 | ||
| 535 | 542 | // Attach click handler | |
| 536 | - | webkit.WidgetAttachClickHandler(titleBar, func() { | |
| 537 | - | spm.handleTitleBarClick(uintptr(titleBarID)) | |
| 538 | - | }) | |
| 543 | + | // TODO: Implement click handler with gtk.GestureClick | |
| 544 | + | // webkit.WidgetAttachClickHandler(titleBar, func() { | |
| 545 | + | // spm.handleTitleBarClick(titleBarID) | |
| 546 | + | // }) | |
| 539 | 547 | } | |
| 540 | 548 | ||
| 541 | 549 | return titleBar | |
| @@ -554,7 +562,7 @@ func (spm *StackedPaneManager) UpdateTitleBar(webView *webkit.WebView, title str | |||
| 554 | 562 | } | |
| 555 | 563 | ||
| 556 | 564 | // Check if this pane is in a stack and has a title bar | |
| 557 | - | if node.parent != nil && node.parent.isStacked && node.titleBar != 0 { | |
| 565 | + | if node.parent != nil && node.parent.isStacked && node.titleBar != nil { | |
| 558 | 566 | // CRITICAL: Only update title bar for INACTIVE panes | |
| 559 | 567 | // Active panes should never show their title bar | |
| 560 | 568 | stackNode := node.parent | |
| @@ -582,7 +590,7 @@ func (spm *StackedPaneManager) UpdateTitleBar(webView *webkit.WebView, title str | |||
| 582 | 590 | ||
| 583 | 591 | // updateTitleBarLabel updates the label widget within a title bar by recreating it | |
| 584 | 592 | func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) { | |
| 585 | - | if node == nil || node.titleBar == 0 || node.parent == nil || !node.parent.isStacked { | |
| 593 | + | if node == nil || node.titleBar == nil || node.parent == nil || !node.parent.isStacked { | |
| 586 | 594 | return | |
| 587 | 595 | } | |
| 588 | 596 | ||
| @@ -594,22 +602,23 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||
| 594 | 602 | ||
| 595 | 603 | // Create new title bar with updated title and favicon | |
| 596 | 604 | newTitleBar := spm.createTitleBarWithTitle(title, pageURL) | |
| 597 | - | if newTitleBar == 0 { | |
| 605 | + | if newTitleBar == nil { | |
| 598 | 606 | log.Printf("[workspace] failed to create new title bar") | |
| 599 | 607 | return | |
| 600 | 608 | } | |
| 601 | 609 | ||
| 602 | 610 | // Store the mapping from titleBar ID to pane and attach click handler | |
| 603 | 611 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | |
| 604 | - | spm.titleBarToPane[uintptr(titleBarID)] = node | |
| 612 | + | spm.titleBarToPane[titleBarID] = node | |
| 605 | 613 | ||
| 606 | 614 | // Attach click handler | |
| 607 | - | webkit.WidgetAttachClickHandler(newTitleBar, func() { | |
| 608 | - | spm.handleTitleBarClick(uintptr(titleBarID)) | |
| 609 | - | }) | |
| 615 | + | // TODO: Implement with gtk.GestureClick | |
| 616 | + | // webkit.WidgetAttachClickHandler(newTitleBar, func() { | |
| 617 | + | // spm.handleTitleBarClick(titleBarID) | |
| 618 | + | // }) | |
| 610 | 619 | ||
| 611 | 620 | // Replace the old title bar in the stack | |
| 612 | - | if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != 0 { | |
| 621 | + | if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != nil { | |
| 613 | 622 | // Find the correct insertion position based on the pane's index in the stack | |
| 614 | 623 | paneIndex := -1 | |
| 615 | 624 | for i, stackedPane := range node.parent.stackedPanes { | |
| @@ -620,24 +629,24 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||
| 620 | 629 | } | |
| 621 | 630 | ||
| 622 | 631 | // Remove old title bar | |
| 623 | - | if node.titleBar != 0 && webkit.WidgetIsValid(node.titleBar) { | |
| 624 | - | webkit.WidgetUnparent(node.titleBar) | |
| 632 | + | if node.titleBar != nil { | |
| 633 | + | node.titleBar.Unparent() | |
| 625 | 634 | } | |
| 626 | 635 | ||
| 627 | 636 | // Update the node's title bar reference | |
| 628 | 637 | spm.wm.setTitleBar(node, newTitleBar) | |
| 629 | 638 | ||
| 630 | 639 | // Insert the new title bar at the correct position | |
| 631 | - | if paneIndex == 0 { | |
| 632 | - | // First pane - insert at the beginning | |
| 633 | - | if node.parent.stackWrapper != 0 { | |
| 634 | - | webkit.BoxPrepend(node.parent.stackWrapper, newTitleBar) | |
| 635 | - | } | |
| 636 | - | } else { | |
| 637 | - | // Insert after the previous pane's container | |
| 638 | - | prevPane := node.parent.stackedPanes[paneIndex-1] | |
| 639 | - | if node.parent.stackWrapper != 0 && prevPane.container != 0 { | |
| 640 | - | webkit.BoxInsertChildAfter(node.parent.stackWrapper, newTitleBar, prevPane.container) | |
| 640 | + | if box, ok := node.parent.stackWrapper.(*gtk.Box); ok { | |
| 641 | + | if paneIndex == 0 { | |
| 642 | + | // First pane - insert at the beginning | |
| 643 | + | box.Prepend(newTitleBar) | |
| 644 | + | } else { | |
| 645 | + | // Insert after the previous pane's container | |
| 646 | + | prevPane := node.parent.stackedPanes[paneIndex-1] | |
| 647 | + | if prevPane.container != nil { | |
| 648 | + | box.InsertChildAfter(newTitleBar, prevPane.container) | |
| 649 | + | } | |
| 641 | 650 | } | |
| 642 | 651 | } | |
| 643 | 652 | ||
| @@ -646,18 +655,18 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||
| 646 | 655 | } | |
| 647 | 656 | ||
| 648 | 657 | // createTitleBarWithTitle creates a title bar with a specific title and optional favicon | |
| 649 | - | func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL string) uintptr { | |
| 650 | - | titleBox := webkit.NewBox(webkit.OrientationHorizontal, 8) | |
| 651 | - | if titleBox == 0 { | |
| 652 | - | return 0 | |
| 658 | + | func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL string) gtk.Widgetter { | |
| 659 | + | titleBox := gtk.NewBox(gtk.OrientationHorizontal, 8) | |
| 660 | + | if titleBox == nil { | |
| 661 | + | return nil | |
| 653 | 662 | } | |
| 654 | 663 | ||
| 655 | - | webkit.WidgetAddCSSClass(titleBox, "stacked-pane-title") | |
| 656 | - | webkit.WidgetSetHExpand(titleBox, true) | |
| 657 | - | webkit.WidgetSetVExpand(titleBox, false) | |
| 664 | + | titleBox.AddCSSClass("stacked-pane-title") | |
| 665 | + | titleBox.SetHExpand(true) | |
| 666 | + | titleBox.SetVExpand(false) | |
| 658 | 667 | ||
| 659 | 668 | // Add favicon placeholder for debugging (always visible) | |
| 660 | - | var faviconImg uintptr | |
| 669 | + | var faviconImg gtk.Widgetter | |
| 661 | 670 | var debugReason string | |
| 662 | 671 | ||
| 663 | 672 | if pageURL == "" { | |
| @@ -691,10 +700,12 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str | |||
| 691 | 700 | faviconPath := faviconCache.GetCachedPath(faviconURL) | |
| 692 | 701 | if faviconPath != "" { | |
| 693 | 702 | log.Printf("[favicon] Loading cached favicon from: %s", faviconPath) | |
| 694 | - | faviconImg = webkit.ImageNewFromFile(faviconPath) | |
| 695 | - | if faviconImg != 0 { | |
| 696 | - | webkit.ImageSetPixelSize(faviconImg, 16) | |
| 697 | - | webkit.WidgetAddCSSClass(faviconImg, "stacked-pane-favicon") | |
| 703 | + | faviconImg = gtk.NewImageFromFile(faviconPath) | |
| 704 | + | if faviconImg != nil { | |
| 705 | + | if img, ok := faviconImg.(*gtk.Image); ok { | |
| 706 | + | img.SetPixelSize(16) | |
| 707 | + | } | |
| 708 | + | faviconImg.AddCSSClass("stacked-pane-favicon") | |
| 698 | 709 | log.Printf("[favicon] Successfully created favicon image widget") | |
| 699 | 710 | } else { | |
| 700 | 711 | debugReason = "image widget creation failed" | |
| @@ -711,32 +722,32 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str | |||
| 711 | 722 | } | |
| 712 | 723 | ||
| 713 | 724 | // Add favicon or placeholder | |
| 714 | - | if faviconImg != 0 { | |
| 715 | - | webkit.BoxAppend(titleBox, faviconImg) | |
| 716 | - | webkit.WidgetShow(faviconImg) | |
| 725 | + | if faviconImg != nil { | |
| 726 | + | titleBox.Append(faviconImg) | |
| 727 | + | faviconImg.Show() | |
| 717 | 728 | } else { | |
| 718 | 729 | // DEBUG: Add visible placeholder label to show code is running | |
| 719 | - | placeholderLabel := webkit.NewLabel(faviconPlaceholder) | |
| 720 | - | if placeholderLabel != 0 { | |
| 721 | - | webkit.WidgetAddCSSClass(placeholderLabel, "stacked-pane-favicon-placeholder") | |
| 722 | - | webkit.BoxAppend(titleBox, placeholderLabel) | |
| 723 | - | webkit.WidgetShow(placeholderLabel) | |
| 730 | + | placeholderLabel := gtk.NewLabel(faviconPlaceholder) | |
| 731 | + | if placeholderLabel != nil { | |
| 732 | + | placeholderLabel.AddCSSClass("stacked-pane-favicon-placeholder") | |
| 733 | + | titleBox.Append(placeholderLabel) | |
| 734 | + | placeholderLabel.Show() | |
| 724 | 735 | log.Printf("[favicon] Using placeholder for %s (reason: %s)", pageURL, debugReason) | |
| 725 | 736 | } | |
| 726 | 737 | } | |
| 727 | 738 | ||
| 728 | 739 | // Create title label with the specified title | |
| 729 | - | titleLabel := webkit.NewLabel(title) | |
| 730 | - | if titleLabel == 0 { | |
| 740 | + | titleLabel := gtk.NewLabel(title) | |
| 741 | + | if titleLabel == nil { | |
| 731 | 742 | return titleBox | |
| 732 | 743 | } | |
| 733 | 744 | ||
| 734 | - | webkit.WidgetAddCSSClass(titleLabel, "stacked-pane-title-text") | |
| 735 | - | webkit.LabelSetEllipsize(titleLabel, webkit.EllipsizeEnd) | |
| 745 | + | titleLabel.AddCSSClass("stacked-pane-title-text") | |
| 746 | + | titleLabel.SetEllipsize(2) // pango.EllipsizeEnd = 2 | |
| 736 | 747 | ||
| 737 | - | webkit.BoxAppend(titleBox, titleLabel) | |
| 738 | - | webkit.WidgetShow(titleBox) | |
| 739 | - | webkit.WidgetShow(titleLabel) | |
| 748 | + | titleBox.Append(titleLabel) | |
| 749 | + | titleBox.Show() | |
| 750 | + | titleLabel.Show() | |
| 740 | 751 | ||
| 741 | 752 | return titleBox | |
| 742 | 753 | } | |
| @@ -775,15 +786,15 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 775 | 786 | ||
| 776 | 787 | // Remove the pane widgets from the stack container before mutating the tree. | |
| 777 | 788 | stackBox := stackNode.stackWrapper | |
| 778 | - | if stackBox == 0 { | |
| 789 | + | if stackBox == nil { | |
| 779 | 790 | stackBox = stackNode.container | |
| 780 | 791 | } | |
| 781 | - | if stackBox != 0 { | |
| 782 | - | if node.titleBar != 0 { | |
| 783 | - | webkit.BoxRemove(stackBox, node.titleBar) | |
| 792 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil { | |
| 793 | + | if node.titleBar != nil { | |
| 794 | + | box.Remove(node.titleBar) | |
| 784 | 795 | } | |
| 785 | - | if node.container != 0 { | |
| 786 | - | webkit.BoxRemove(stackBox, node.container) | |
| 796 | + | if node.container != nil { | |
| 797 | + | box.Remove(node.container) | |
| 787 | 798 | } | |
| 788 | 799 | } | |
| 789 | 800 | ||
| @@ -796,30 +807,30 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 796 | 807 | parent := stackNode.parent | |
| 797 | 808 | if parent == nil { | |
| 798 | 809 | // Stack was the root of the workspace — closing this pane exits the app. | |
| 799 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | |
| 800 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | |
| 801 | - | webkit.WidgetUnparent(stackNode.container) | |
| 810 | + | if stackNode.container != nil { | |
| 811 | + | if stackNode.container.Parent() != nil { | |
| 812 | + | stackNode.container.Unparent() | |
| 802 | 813 | } | |
| 803 | 814 | } | |
| 804 | 815 | if spm.wm.window != nil { | |
| 805 | - | spm.wm.window.SetChild(0) | |
| 816 | + | spm.wm.window.SetChild(nil) | |
| 806 | 817 | } | |
| 807 | 818 | _, err := spm.wm.cleanupAndExit(node) | |
| 808 | 819 | return err | |
| 809 | 820 | } | |
| 810 | 821 | ||
| 811 | 822 | // Detach the empty stack container from its GtkPaned parent. | |
| 812 | - | if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | |
| 823 | + | if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { | |
| 813 | 824 | if parent.left == stackNode { | |
| 814 | - | webkit.PanedSetStartChild(parent.container, 0) | |
| 825 | + | paned.SetStartChild(nil) | |
| 815 | 826 | } else if parent.right == stackNode { | |
| 816 | - | webkit.PanedSetEndChild(parent.container, 0) | |
| 827 | + | paned.SetEndChild(nil) | |
| 817 | 828 | } | |
| 818 | - | webkit.WidgetQueueAllocate(parent.container) | |
| 829 | + | paned.QueueAllocate() | |
| 819 | 830 | } | |
| 820 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | |
| 821 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | |
| 822 | - | webkit.WidgetUnparent(stackNode.container) | |
| 831 | + | if stackNode.container != nil { | |
| 832 | + | if stackNode.container.Parent() != nil { | |
| 833 | + | stackNode.container.Unparent() | |
| 823 | 834 | } | |
| 824 | 835 | } | |
| 825 | 836 | ||
| @@ -840,8 +851,8 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 840 | 851 | } | |
| 841 | 852 | ||
| 842 | 853 | stackNode.parent = nil | |
| 843 | - | stackNode.container = 0 | |
| 844 | - | stackNode.stackWrapper = 0 | |
| 854 | + | stackNode.container = nil | |
| 855 | + | stackNode.stackWrapper = nil | |
| 845 | 856 | stackNode.stackedPanes = nil | |
| 846 | 857 | ||
| 847 | 858 | return nil | |
| @@ -852,25 +863,25 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 852 | 863 | parent := stackNode.parent | |
| 853 | 864 | lastPaneContainer := lastPane.container | |
| 854 | 865 | ||
| 855 | - | if lastPane.titleBar != 0 && stackBox != 0 { | |
| 856 | - | webkit.BoxRemove(stackBox, lastPane.titleBar) | |
| 866 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil && lastPane.titleBar != nil { | |
| 867 | + | box.Remove(lastPane.titleBar) | |
| 857 | 868 | } | |
| 858 | - | if stackBox != 0 && lastPaneContainer != 0 { | |
| 859 | - | webkit.BoxRemove(stackBox, lastPaneContainer) | |
| 869 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil && lastPaneContainer != nil { | |
| 870 | + | box.Remove(lastPaneContainer) | |
| 860 | 871 | } | |
| 861 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | |
| 862 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | |
| 863 | - | webkit.WidgetUnparent(stackNode.container) | |
| 872 | + | if stackNode.container != nil { | |
| 873 | + | if stackNode.container.Parent() != nil { | |
| 874 | + | stackNode.container.Unparent() | |
| 864 | 875 | } | |
| 865 | 876 | } | |
| 866 | 877 | ||
| 867 | 878 | if parent == nil { | |
| 868 | 879 | spm.wm.root = lastPane | |
| 869 | 880 | lastPane.parent = nil | |
| 870 | - | if spm.wm.window != nil && lastPaneContainer != 0 { | |
| 881 | + | if spm.wm.window != nil && lastPaneContainer != nil { | |
| 871 | 882 | spm.wm.window.SetChild(lastPaneContainer) | |
| 872 | - | webkit.WidgetQueueAllocate(lastPaneContainer) | |
| 873 | - | webkit.WidgetShow(lastPaneContainer) | |
| 883 | + | lastPaneContainer.QueueAllocate() | |
| 884 | + | lastPaneContainer.Show() | |
| 874 | 885 | } | |
| 875 | 886 | } else { | |
| 876 | 887 | if parent.left == stackNode { | |
| @@ -879,36 +890,36 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 879 | 890 | parent.right = lastPane | |
| 880 | 891 | } | |
| 881 | 892 | lastPane.parent = parent | |
| 882 | - | if parent.container != 0 && webkit.WidgetIsValid(parent.container) && lastPaneContainer != 0 { | |
| 893 | + | if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil && lastPaneContainer != nil { | |
| 883 | 894 | if parent.left == lastPane { | |
| 884 | - | webkit.PanedSetStartChild(parent.container, lastPaneContainer) | |
| 895 | + | paned.SetStartChild(lastPaneContainer) | |
| 885 | 896 | } else { | |
| 886 | - | webkit.PanedSetEndChild(parent.container, lastPaneContainer) | |
| 897 | + | paned.SetEndChild(lastPaneContainer) | |
| 887 | 898 | } | |
| 888 | - | webkit.WidgetQueueAllocate(parent.container) | |
| 899 | + | paned.QueueAllocate() | |
| 889 | 900 | } | |
| 890 | - | if lastPaneContainer != 0 { | |
| 891 | - | webkit.WidgetSetVisible(lastPaneContainer, true) | |
| 901 | + | if lastPaneContainer != nil { | |
| 902 | + | lastPaneContainer.SetVisible(true) | |
| 892 | 903 | } | |
| 893 | 904 | } | |
| 894 | 905 | ||
| 895 | 906 | lastPane.isStacked = false | |
| 896 | 907 | lastPane.stackedPanes = nil | |
| 897 | - | lastPane.titleBar = 0 | |
| 908 | + | lastPane.titleBar = nil | |
| 898 | 909 | spm.wm.viewToNode[lastPane.pane.webView] = lastPane | |
| 899 | 910 | ||
| 900 | 911 | generation := spm.wm.nextCleanupGeneration() | |
| 901 | 912 | spm.wm.cleanupPane(node, generation) | |
| 902 | 913 | ||
| 903 | 914 | spm.wm.SetActivePane(lastPane, SourceClose) | |
| 904 | - | if lastPaneWidget := lastPane.pane.webView.Widget(); lastPaneWidget != 0 { | |
| 905 | - | webkit.WidgetShow(lastPaneWidget) | |
| 906 | - | webkit.WidgetGrabFocus(lastPaneWidget) | |
| 915 | + | if lastPaneWidget := lastPane.pane.webView.Widget(); lastPaneWidget != nil { | |
| 916 | + | lastPaneWidget.Show() | |
| 917 | + | lastPaneWidget.GrabFocus() | |
| 907 | 918 | } | |
| 908 | 919 | ||
| 909 | 920 | stackNode.parent = nil | |
| 910 | - | stackNode.container = 0 | |
| 911 | - | stackNode.stackWrapper = 0 | |
| 921 | + | stackNode.container = nil | |
| 922 | + | stackNode.stackWrapper = nil | |
| 912 | 923 | stackNode.stackedPanes = nil | |
| 913 | 924 | ||
| 914 | 925 | log.Printf("[workspace] converted stack back to regular pane") | |
| @@ -939,7 +950,7 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||
| 939 | 950 | } | |
| 940 | 951 | ||
| 941 | 952 | // handleTitleBarClick handles clicks on title bars to switch the active pane | |
| 942 | - | func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uintptr) { | |
| 953 | + | func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uint64) { | |
| 943 | 954 | pane, exists := spm.titleBarToPane[titleBarID] | |
| 944 | 955 | if !exists || pane == nil || pane.parent == nil || !pane.parent.isStacked { | |
| 945 | 956 | return | |
| @@ -957,7 +968,7 @@ func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uintptr) { | |||
| 957 | 968 | ||
| 958 | 969 | // Update title bar for the pane transitioning from ACTIVE to INACTIVE | |
| 959 | 970 | currentActivePane := stackNode.stackedPanes[stackNode.activeStackIndex] | |
| 960 | - | if currentActivePane.pane != nil && currentActivePane.pane.webView != nil && currentActivePane.titleBar != 0 { | |
| 971 | + | if currentActivePane.pane != nil && currentActivePane.pane.webView != nil && currentActivePane.titleBar != nil { | |
| 961 | 972 | currentTitle := currentActivePane.pane.webView.GetTitle() | |
| 962 | 973 | if currentTitle != "" { | |
| 963 | 974 | spm.updateTitleBarLabel(currentActivePane, currentTitle) | |
| @@ -10,6 +10,7 @@ import ( | |||||
| 10 | 10 | ||||
| 11 | "github.com/bnema/dumber/internal/cache" | 11 | "github.com/bnema/dumber/internal/cache" | ||
| 12 | "github.com/bnema/dumber/pkg/webkit" | 12 | "github.com/bnema/dumber/pkg/webkit" | ||
| 13 | + | "github.com/diamondburned/gotk4/pkg/gtk/v4" | |||
| 13 | ) | 14 | ) | ||
| 14 | 15 | ||||
| 15 | const ( | 16 | const ( | ||
| @@ -20,7 +21,7 @@ const ( | |||||
| 20 | // StackedPaneManager handles all stacked pane operations | 21 | // StackedPaneManager handles all stacked pane operations | ||
| 21 | type StackedPaneManager struct { | 22 | type StackedPaneManager struct { | ||
| 22 | wm *WorkspaceManager | 23 | wm *WorkspaceManager | ||
| 23 | - | titleBarToPane map[uintptr]*paneNode | 24 | + | titleBarToPane map[uint64]*paneNode |
| 24 | nextTitleBarID uint64 | 25 | nextTitleBarID uint64 | ||
| 25 | } | 26 | } | ||
| 26 | 27 | ||||
| @@ -28,7 +29,7 @@ type StackedPaneManager struct { | |||||
| 28 | func NewStackedPaneManager(wm *WorkspaceManager) *StackedPaneManager { | 29 | func NewStackedPaneManager(wm *WorkspaceManager) *StackedPaneManager { | ||
| 29 | return &StackedPaneManager{ | 30 | return &StackedPaneManager{ | ||
| 30 | wm: wm, | 31 | wm: wm, | ||
| 31 | - | titleBarToPane: make(map[uintptr]*paneNode), | 32 | + | titleBarToPane: make(map[uint64]*paneNode), |
| 32 | } | 33 | } | ||
| 33 | } | 34 | } | ||
| 34 | 35 | ||||
| @@ -84,12 +85,12 @@ func (spm *StackedPaneManager) prepareNewStackedPane() (*paneNode, error) { | |||||
| 84 | } | 85 | } | ||
| 85 | 86 | ||||
| 86 | newContainer := newPane.webView.RootWidget() | 87 | newContainer := newPane.webView.RootWidget() | ||
| 87 | - | if newContainer == 0 { | 88 | + | if newContainer == nil { |
| 88 | return nil, errors.New("new pane missing container") | 89 | return nil, errors.New("new pane missing container") | ||
| 89 | } | 90 | } | ||
| 90 | - | webkit.WidgetSetHExpand(newContainer, true) | 91 | + | newContainer.SetHExpand(true) |
| 91 | - | webkit.WidgetSetVExpand(newContainer, true) | 92 | + | newContainer.SetVExpand(true) |
| 92 | - | webkit.WidgetRealizeInContainer(newContainer) | 93 | + | newContainer.Realize() |
| 93 | 94 | ||||
| 94 | // Create the new leaf node | 95 | // Create the new leaf node | ||
| 95 | newLeaf := &paneNode{ | 96 | newLeaf := &paneNode{ | ||
| @@ -104,8 +105,10 @@ func (spm *StackedPaneManager) prepareNewStackedPane() (*paneNode, error) { | |||||
| 104 | spm.wm.setTitleBar(newLeaf, newTitleBar) | 105 | spm.wm.setTitleBar(newLeaf, newTitleBar) | ||
| 105 | 106 | ||||
| 106 | // Keep new container hidden initially - will be shown after transition | 107 | // Keep new container hidden initially - will be shown after transition | ||
| 107 | - | webkit.WidgetSetVisible(newContainer, false) | 108 | + | newContainer.SetVisible(false) |
| 108 | - | webkit.WidgetSetVisible(newTitleBar, false) | 109 | + | if newTitleBar != nil { |
| 110 | + | newTitleBar.SetVisible(false) | |||
| 111 | + | } | |||
| 109 | 112 | ||||
| 110 | return newLeaf, nil | 113 | return newLeaf, nil | ||
| 111 | } | 114 | } | ||
| @@ -141,9 +144,9 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 141 | stackNode.stackedPanes[insertIndex] = newLeaf // Insert new pane | 144 | stackNode.stackedPanes[insertIndex] = newLeaf // Insert new pane | ||
| 142 | 145 | ||||
| 143 | // Unparent the new container before adding it to the stack (only if it has a parent) | 146 | // Unparent the new container before adding it to the stack (only if it has a parent) | ||
| 144 | - | if newLeaf.container != 0 && webkit.WidgetIsValid(newLeaf.container) { | 147 | + | if newLeaf.container != nil { |
| 145 | - | if webkit.WidgetGetParent(newLeaf.container) != 0 { | 148 | + | if newLeaf.container.Parent() != nil { |
| 146 | - | webkit.WidgetUnparent(newLeaf.container) | 149 | + | newLeaf.container.Unparent() |
| 147 | } | 150 | } | ||
| 148 | } | 151 | } | ||
| 149 | 152 | ||||
| @@ -151,32 +154,32 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 151 | // Each pane has 2 widgets (titleBar, container), so position = insertIndex * 2 | 154 | // Each pane has 2 widgets (titleBar, container), so position = insertIndex * 2 | ||
| 152 | 155 | ||||
| 153 | // Find the widget to insert after (based on insertIndex) | 156 | // Find the widget to insert after (based on insertIndex) | ||
| 154 | - | var insertAfterWidget uintptr = 0 | 157 | + | var insertAfterWidget gtk.Widgetter |
| 155 | if insertIndex > 0 { | 158 | if insertIndex > 0 { | ||
| 156 | // Insert after the previous pane's container | 159 | // Insert after the previous pane's container | ||
| 157 | prevPane := stackNode.stackedPanes[insertIndex-1] | 160 | prevPane := stackNode.stackedPanes[insertIndex-1] | ||
| 158 | - | if prevPane.container != 0 { | 161 | + | if prevPane.container != nil { |
| 159 | insertAfterWidget = prevPane.container | 162 | insertAfterWidget = prevPane.container | ||
| 160 | } | 163 | } | ||
| 161 | } | 164 | } | ||
| 162 | 165 | ||||
| 163 | - | if insertAfterWidget != 0 { | 166 | + | if insertAfterWidget != nil { |
| 164 | // Insert titleBar after the previous pane's container | 167 | // Insert titleBar after the previous pane's container | ||
| 165 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | 168 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { |
| 166 | - | webkit.BoxInsertChildAfter(stackNode.stackWrapper, newLeaf.titleBar, insertAfterWidget) | 169 | + | box.InsertChildAfter(newLeaf.titleBar, insertAfterWidget) |
| 167 | } | 170 | } | ||
| 168 | // Insert container after the newly inserted titleBar | 171 | // Insert container after the newly inserted titleBar | ||
| 169 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 && newLeaf.titleBar != 0 { | 172 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil && newLeaf.titleBar != nil { |
| 170 | - | webkit.BoxInsertChildAfter(stackNode.stackWrapper, newLeaf.container, newLeaf.titleBar) | 173 | + | box.InsertChildAfter(newLeaf.container, newLeaf.titleBar) |
| 171 | } | 174 | } | ||
| 172 | - | log.Printf("[workspace] inserted widgets at position %d (after widget %#x)", insertIndex, insertAfterWidget) | 175 | + | log.Printf("[workspace] inserted widgets at position %d (after widget %p)", insertIndex, insertAfterWidget) |
| 173 | } else { | 176 | } else { | ||
| 174 | // Insert at the beginning (insertIndex = 0) | 177 | // Insert at the beginning (insertIndex = 0) | ||
| 175 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 { | 178 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { |
| 176 | - | webkit.BoxPrepend(stackNode.stackWrapper, newLeaf.container) | 179 | + | box.Prepend(newLeaf.container) |
| 177 | } | 180 | } | ||
| 178 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | 181 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { |
| 179 | - | webkit.BoxPrepend(stackNode.stackWrapper, newLeaf.titleBar) | 182 | + | box.Prepend(newLeaf.titleBar) |
| 180 | } | 183 | } | ||
| 181 | log.Printf("[workspace] prepended widgets at position 0") | 184 | log.Printf("[workspace] prepended widgets at position 0") | ||
| 182 | } | 185 | } | ||
| @@ -189,27 +192,27 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||||
| 189 | log.Printf("[workspace] converting pane to stacked: %p", target) | 192 | log.Printf("[workspace] converting pane to stacked: %p", target) | ||
| 190 | 193 | ||||
| 191 | // Create the wrapper container - this is what will be used by splitNode | 194 | // Create the wrapper container - this is what will be used by splitNode | ||
| 192 | - | stackWrapperContainer := webkit.NewBox(webkit.OrientationVertical, 0) | 195 | + | stackWrapperContainer := gtk.NewBox(gtk.OrientationVertical, 0) |
| 193 | - | if stackWrapperContainer == 0 { | 196 | + | if stackWrapperContainer == nil { |
| 194 | return nil, 0, errors.New("failed to create stack wrapper container") | 197 | return nil, 0, errors.New("failed to create stack wrapper container") | ||
| 195 | } | 198 | } | ||
| 196 | - | webkit.WidgetSetHExpand(stackWrapperContainer, true) | 199 | + | stackWrapperContainer.SetHExpand(true) |
| 197 | - | webkit.WidgetSetVExpand(stackWrapperContainer, true) | 200 | + | stackWrapperContainer.SetVExpand(true) |
| 198 | - | webkit.WidgetRealizeInContainer(stackWrapperContainer) // Ensures size request is set like regular panes | 201 | + | stackWrapperContainer.Realize() // Ensures size request is set like regular panes |
| 199 | 202 | ||||
| 200 | // Add CSS class to the stack container for proper styling | 203 | // Add CSS class to the stack container for proper styling | ||
| 201 | - | webkit.WidgetAddCSSClass(stackWrapperContainer, stackContainerClass) | 204 | + | stackWrapperContainer.AddCSSClass(stackContainerClass) |
| 202 | 205 | ||||
| 203 | // Create the internal box for the actual stacked widgets (titles + webviews) | 206 | // Create the internal box for the actual stacked widgets (titles + webviews) | ||
| 204 | - | stackInternalBox := webkit.NewBox(webkit.OrientationVertical, 0) | 207 | + | stackInternalBox := gtk.NewBox(gtk.OrientationVertical, 0) |
| 205 | - | if stackInternalBox == 0 { | 208 | + | if stackInternalBox == nil { |
| 206 | return nil, 0, errors.New("failed to create stack internal box") | 209 | return nil, 0, errors.New("failed to create stack internal box") | ||
| 207 | } | 210 | } | ||
| 208 | - | webkit.WidgetSetHExpand(stackInternalBox, true) | 211 | + | stackInternalBox.SetHExpand(true) |
| 209 | - | webkit.WidgetSetVExpand(stackInternalBox, true) | 212 | + | stackInternalBox.SetVExpand(true) |
| 210 | 213 | ||||
| 211 | // The internal box goes inside the wrapper | 214 | // The internal box goes inside the wrapper | ||
| 212 | - | webkit.BoxAppend(stackWrapperContainer, stackInternalBox) | 215 | + | stackWrapperContainer.Append(stackInternalBox) |
| 213 | 216 | ||||
| 214 | // Get the existing container and parent info | 217 | // Get the existing container and parent info | ||
| 215 | existingContainer := target.container | 218 | existingContainer := target.container | ||
| @@ -219,15 +222,19 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||||
| 219 | titleBar := spm.createTitleBar(target) | 222 | titleBar := spm.createTitleBar(target) | ||
| 220 | 223 | ||||
| 221 | // Keep existing container visible during transition to prevent rendering glitch | 224 | // Keep existing container visible during transition to prevent rendering glitch | ||
| 222 | - | webkit.WidgetSetVisible(titleBar, false) | 225 | + | if titleBar != nil { |
| 226 | + | titleBar.SetVisible(false) | |||
| 227 | + | } | |||
| 223 | 228 | ||||
| 224 | // Detach existing container from its current parent first | 229 | // Detach existing container from its current parent first | ||
| 225 | spm.detachFromParent(target, parent) | 230 | spm.detachFromParent(target, parent) | ||
| 226 | 231 | ||||
| 227 | // Build the complete stack structure with hidden widgets | 232 | // Build the complete stack structure with hidden widgets | ||
| 228 | - | webkit.BoxAppend(stackInternalBox, titleBar) | 233 | + | if titleBar != nil { |
| 229 | - | if existingContainer != 0 { | 234 | + | stackInternalBox.Append(titleBar) |
| 230 | - | webkit.BoxAppend(stackInternalBox, existingContainer) | 235 | + | } |
| 236 | + | if existingContainer != nil { | |||
| 237 | + | stackInternalBox.Append(existingContainer) | |||
| 231 | } | 238 | } | ||
| 232 | 239 | ||||
| 233 | // Immediately reattach the stack wrapper to minimize visibility gap | 240 | // Immediately reattach the stack wrapper to minimize visibility gap | ||
| @@ -275,24 +282,24 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||||
| 275 | 282 | ||||
| 276 | // Unparent the new container before adding it to the stack | 283 | // Unparent the new container before adding it to the stack | ||
| 277 | // This is critical: even freshly created WebViews might have internal parent refs | 284 | // This is critical: even freshly created WebViews might have internal parent refs | ||
| 278 | - | if newLeaf.container != 0 && webkit.WidgetIsValid(newLeaf.container) { | 285 | + | if newLeaf.container != nil { |
| 279 | - | parent := webkit.WidgetGetParent(newLeaf.container) | 286 | + | parentWidget := newLeaf.container.Parent() |
| 280 | - | if parent != 0 { | 287 | + | if parentWidget != nil { |
| 281 | - | log.Printf("[workspace] unparenting new pane container %#x from parent %#x before stack append", newLeaf.container, parent) | 288 | + | log.Printf("[workspace] unparenting new pane container %p from parent %p before stack append", newLeaf.container, parentWidget) |
| 282 | - | webkit.WidgetUnparent(newLeaf.container) | 289 | + | newLeaf.container.Unparent() |
| 283 | // Verify unparent succeeded | 290 | // Verify unparent succeeded | ||
| 284 | - | if finalParent := webkit.WidgetGetParent(newLeaf.container); finalParent != 0 { | 291 | + | if finalParent := newLeaf.container.Parent(); finalParent != nil { |
| 285 | - | log.Printf("[workspace] WARNING: container %#x still has parent %#x after unparent", newLeaf.container, finalParent) | 292 | + | log.Printf("[workspace] WARNING: container %p still has parent %p after unparent", newLeaf.container, finalParent) |
| 286 | } | 293 | } | ||
| 287 | } | 294 | } | ||
| 288 | } | 295 | } | ||
| 289 | 296 | ||||
| 290 | // Add the new widgets to the internal stack box | 297 | // Add the new widgets to the internal stack box | ||
| 291 | - | if stackNode.stackWrapper != 0 && newLeaf.titleBar != 0 { | 298 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { |
| 292 | - | webkit.BoxAppend(stackNode.stackWrapper, newLeaf.titleBar) | 299 | + | box.Append(newLeaf.titleBar) |
| 293 | } | 300 | } | ||
| 294 | - | if stackNode.stackWrapper != 0 && newLeaf.container != 0 { | 301 | + | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { |
| 295 | - | webkit.BoxAppend(stackNode.stackWrapper, newLeaf.container) | 302 | + | box.Append(newLeaf.container) |
| 296 | } | 303 | } | ||
| 297 | 304 | ||||
| 298 | return stackNode, insertIndex, nil | 305 | return stackNode, insertIndex, nil | ||
| @@ -303,35 +310,35 @@ func (spm *StackedPaneManager) detachFromParent(target *paneNode, parent *paneNo | |||||
| 303 | if parent == nil { | 310 | if parent == nil { | ||
| 304 | // Target is the root - remove it from window | 311 | // Target is the root - remove it from window | ||
| 305 | if spm.wm.window != nil { | 312 | if spm.wm.window != nil { | ||
| 306 | - | spm.wm.window.SetChild(0) | 313 | + | spm.wm.window.SetChild(nil) |
| 307 | } | 314 | } | ||
| 308 | // Unparent if it has a GTK parent | 315 | // Unparent if it has a GTK parent | ||
| 309 | - | if target.container != 0 && webkit.WidgetIsValid(target.container) { | 316 | + | if target.container != nil { |
| 310 | - | if webkit.WidgetGetParent(target.container) != 0 { | 317 | + | if target.container.Parent() != nil { |
| 311 | - | webkit.WidgetUnparent(target.container) | 318 | + | target.container.Unparent() |
| 312 | } | 319 | } | ||
| 313 | } | 320 | } | ||
| 314 | - | } else if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | 321 | + | } else if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { |
| 315 | // Target has a parent paned - remove it (automatically unparents in GTK4) | 322 | // Target has a parent paned - remove it (automatically unparents in GTK4) | ||
| 316 | if parent.left == target { | 323 | if parent.left == target { | ||
| 317 | - | webkit.PanedSetStartChild(parent.container, 0) | 324 | + | paned.SetStartChild(nil) |
| 318 | } else if parent.right == target { | 325 | } else if parent.right == target { | ||
| 319 | - | webkit.PanedSetEndChild(parent.container, 0) | 326 | + | paned.SetEndChild(nil) |
| 320 | } | 327 | } | ||
| 321 | } | 328 | } | ||
| 322 | } | 329 | } | ||
| 323 | 330 | ||||
| 324 | // reattachToParent attaches a container to the parent | 331 | // reattachToParent attaches a container to the parent | ||
| 325 | - | func (spm *StackedPaneManager) reattachToParent(container uintptr, target *paneNode, parent *paneNode) { | 332 | + | func (spm *StackedPaneManager) reattachToParent(container gtk.Widgetter, target *paneNode, parent *paneNode) { |
| 326 | if parent == nil { | 333 | if parent == nil { | ||
| 327 | if spm.wm.window != nil { | 334 | if spm.wm.window != nil { | ||
| 328 | spm.wm.window.SetChild(container) | 335 | spm.wm.window.SetChild(container) | ||
| 329 | } | 336 | } | ||
| 330 | - | } else if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | 337 | + | } else if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { |
| 331 | if parent.left == target { | 338 | if parent.left == target { | ||
| 332 | - | webkit.PanedSetStartChild(parent.container, container) | 339 | + | paned.SetStartChild(container) |
| 333 | } else if parent.right == target { | 340 | } else if parent.right == target { | ||
| 334 | - | webkit.PanedSetEndChild(parent.container, container) | 341 | + | paned.SetEndChild(container) |
| 335 | } | 342 | } | ||
| 336 | } | 343 | } | ||
| 337 | } | 344 | } | ||
| @@ -406,20 +413,20 @@ func (spm *StackedPaneManager) UpdateStackVisibility(stackNode *paneNode) { | |||||
| 406 | for i, pane := range stackNode.stackedPanes { | 413 | for i, pane := range stackNode.stackedPanes { | ||
| 407 | if i == activeIndex { | 414 | if i == activeIndex { | ||
| 408 | // Active pane: show container, NEVER show title bar | 415 | // Active pane: show container, NEVER show title bar | ||
| 409 | - | if pane.container != 0 { | 416 | + | if pane.container != nil { |
| 410 | - | webkit.WidgetSetVisible(pane.container, true) | 417 | + | pane.container.SetVisible(true) |
| 411 | } | 418 | } | ||
| 412 | - | if pane.titleBar != 0 { | 419 | + | if pane.titleBar != nil { |
| 413 | - | webkit.WidgetSetVisible(pane.titleBar, false) // ABSOLUTE RULE: never visible for active pane | 420 | + | pane.titleBar.SetVisible(false) // ABSOLUTE RULE: never visible for active pane |
| 414 | } | 421 | } | ||
| 415 | log.Printf("[workspace] active pane %d: container=visible, titleBar=HIDDEN", i) | 422 | log.Printf("[workspace] active pane %d: container=visible, titleBar=HIDDEN", i) | ||
| 416 | } else { | 423 | } else { | ||
| 417 | // Inactive panes: hide container, show title bar | 424 | // Inactive panes: hide container, show title bar | ||
| 418 | - | if pane.container != 0 { | 425 | + | if pane.container != nil { |
| 419 | - | webkit.WidgetSetVisible(pane.container, false) | 426 | + | pane.container.SetVisible(false) |
| 420 | } | 427 | } | ||
| 421 | - | if pane.titleBar != 0 { | 428 | + | if pane.titleBar != nil { |
| 422 | - | webkit.WidgetSetVisible(pane.titleBar, true) | 429 | + | pane.titleBar.SetVisible(true) |
| 423 | 430 | ||||
| 424 | // Refresh title bar to pick up any newly downloaded favicons | 431 | // Refresh title bar to pick up any newly downloaded favicons | ||
| 425 | if pane.pane != nil && pane.pane.webView != nil { | 432 | if pane.pane != nil && pane.pane.webView != nil { | ||
| @@ -513,7 +520,7 @@ func (spm *StackedPaneManager) NavigateStack(direction string) bool { | |||||
| 513 | } | 520 | } | ||
| 514 | 521 | ||||
| 515 | // createTitleBar creates a title bar widget for a pane in a stack | 522 | // createTitleBar creates a title bar widget for a pane in a stack | ||
| 516 | - | func (spm *StackedPaneManager) createTitleBar(pane *paneNode) uintptr { | 523 | + | func (spm *StackedPaneManager) createTitleBar(pane *paneNode) gtk.Widgetter { |
| 517 | // Get the actual title and URL from the WebView | 524 | // Get the actual title and URL from the WebView | ||
| 518 | var titleText, pageURL string | 525 | var titleText, pageURL string | ||
| 519 | if pane.pane != nil && pane.pane.webView != nil { | 526 | if pane.pane != nil && pane.pane.webView != nil { | ||
| @@ -527,15 +534,16 @@ func (spm *StackedPaneManager) createTitleBar(pane *paneNode) uintptr { | |||||
| 527 | } | 534 | } | ||
| 528 | 535 | ||||
| 529 | titleBar := spm.createTitleBarWithTitle(titleText, pageURL) | 536 | titleBar := spm.createTitleBarWithTitle(titleText, pageURL) | ||
| 530 | - | if titleBar != 0 { | 537 | + | if titleBar != nil { |
| 531 | // Store the mapping from titleBar ID to pane | 538 | // Store the mapping from titleBar ID to pane | ||
| 532 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | 539 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | ||
| 533 | - | spm.titleBarToPane[uintptr(titleBarID)] = pane | 540 | + | spm.titleBarToPane[titleBarID] = pane |
| 534 | 541 | ||||
| 535 | // Attach click handler | 542 | // Attach click handler | ||
| 536 | - | webkit.WidgetAttachClickHandler(titleBar, func() { | 543 | + | // TODO: Implement click handler with gtk.GestureClick |
| 537 | - | spm.handleTitleBarClick(uintptr(titleBarID)) | 544 | + | // webkit.WidgetAttachClickHandler(titleBar, func() { |
| 538 | - | }) | 545 | + | // spm.handleTitleBarClick(titleBarID) |
| 546 | + | // }) | |||
| 539 | } | 547 | } | ||
| 540 | 548 | ||||
| 541 | return titleBar | 549 | return titleBar | ||
| @@ -554,7 +562,7 @@ func (spm *StackedPaneManager) UpdateTitleBar(webView *webkit.WebView, title str | |||||
| 554 | } | 562 | } | ||
| 555 | 563 | ||||
| 556 | // Check if this pane is in a stack and has a title bar | 564 | // Check if this pane is in a stack and has a title bar | ||
| 557 | - | if node.parent != nil && node.parent.isStacked && node.titleBar != 0 { | 565 | + | if node.parent != nil && node.parent.isStacked && node.titleBar != nil { |
| 558 | // CRITICAL: Only update title bar for INACTIVE panes | 566 | // CRITICAL: Only update title bar for INACTIVE panes | ||
| 559 | // Active panes should never show their title bar | 567 | // Active panes should never show their title bar | ||
| 560 | stackNode := node.parent | 568 | stackNode := node.parent | ||
| @@ -582,7 +590,7 @@ func (spm *StackedPaneManager) UpdateTitleBar(webView *webkit.WebView, title str | |||||
| 582 | 590 | ||||
| 583 | // updateTitleBarLabel updates the label widget within a title bar by recreating it | 591 | // updateTitleBarLabel updates the label widget within a title bar by recreating it | ||
| 584 | func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) { | 592 | func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) { | ||
| 585 | - | if node == nil || node.titleBar == 0 || node.parent == nil || !node.parent.isStacked { | 593 | + | if node == nil || node.titleBar == nil || node.parent == nil || !node.parent.isStacked { |
| 586 | return | 594 | return | ||
| 587 | } | 595 | } | ||
| 588 | 596 | ||||
| @@ -594,22 +602,23 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||||
| 594 | 602 | ||||
| 595 | // Create new title bar with updated title and favicon | 603 | // Create new title bar with updated title and favicon | ||
| 596 | newTitleBar := spm.createTitleBarWithTitle(title, pageURL) | 604 | newTitleBar := spm.createTitleBarWithTitle(title, pageURL) | ||
| 597 | - | if newTitleBar == 0 { | 605 | + | if newTitleBar == nil { |
| 598 | log.Printf("[workspace] failed to create new title bar") | 606 | log.Printf("[workspace] failed to create new title bar") | ||
| 599 | return | 607 | return | ||
| 600 | } | 608 | } | ||
| 601 | 609 | ||||
| 602 | // Store the mapping from titleBar ID to pane and attach click handler | 610 | // Store the mapping from titleBar ID to pane and attach click handler | ||
| 603 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | 611 | titleBarID := atomic.AddUint64(&spm.nextTitleBarID, 1) | ||
| 604 | - | spm.titleBarToPane[uintptr(titleBarID)] = node | 612 | + | spm.titleBarToPane[titleBarID] = node |
| 605 | 613 | ||||
| 606 | // Attach click handler | 614 | // Attach click handler | ||
| 607 | - | webkit.WidgetAttachClickHandler(newTitleBar, func() { | 615 | + | // TODO: Implement with gtk.GestureClick |
| 608 | - | spm.handleTitleBarClick(uintptr(titleBarID)) | 616 | + | // webkit.WidgetAttachClickHandler(newTitleBar, func() { |
| 609 | - | }) | 617 | + | // spm.handleTitleBarClick(titleBarID) |
| 618 | + | // }) | |||
| 610 | 619 | ||||
| 611 | // Replace the old title bar in the stack | 620 | // Replace the old title bar in the stack | ||
| 612 | - | if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != 0 { | 621 | + | if node.parent != nil && node.parent.isStacked && node.parent.stackWrapper != nil { |
| 613 | // Find the correct insertion position based on the pane's index in the stack | 622 | // Find the correct insertion position based on the pane's index in the stack | ||
| 614 | paneIndex := -1 | 623 | paneIndex := -1 | ||
| 615 | for i, stackedPane := range node.parent.stackedPanes { | 624 | for i, stackedPane := range node.parent.stackedPanes { | ||
| @@ -620,24 +629,24 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||||
| 620 | } | 629 | } | ||
| 621 | 630 | ||||
| 622 | // Remove old title bar | 631 | // Remove old title bar | ||
| 623 | - | if node.titleBar != 0 && webkit.WidgetIsValid(node.titleBar) { | 632 | + | if node.titleBar != nil { |
| 624 | - | webkit.WidgetUnparent(node.titleBar) | 633 | + | node.titleBar.Unparent() |
| 625 | } | 634 | } | ||
| 626 | 635 | ||||
| 627 | // Update the node's title bar reference | 636 | // Update the node's title bar reference | ||
| 628 | spm.wm.setTitleBar(node, newTitleBar) | 637 | spm.wm.setTitleBar(node, newTitleBar) | ||
| 629 | 638 | ||||
| 630 | // Insert the new title bar at the correct position | 639 | // Insert the new title bar at the correct position | ||
| 631 | - | if paneIndex == 0 { | 640 | + | if box, ok := node.parent.stackWrapper.(*gtk.Box); ok { |
| 632 | - | // First pane - insert at the beginning | 641 | + | if paneIndex == 0 { |
| 633 | - | if node.parent.stackWrapper != 0 { | 642 | + | // First pane - insert at the beginning |
| 634 | - | webkit.BoxPrepend(node.parent.stackWrapper, newTitleBar) | 643 | + | box.Prepend(newTitleBar) |
| 635 | - | } | 644 | + | } else { |
| 636 | - | } else { | 645 | + | // Insert after the previous pane's container |
| 637 | - | // Insert after the previous pane's container | 646 | + | prevPane := node.parent.stackedPanes[paneIndex-1] |
| 638 | - | prevPane := node.parent.stackedPanes[paneIndex-1] | 647 | + | if prevPane.container != nil { |
| 639 | - | if node.parent.stackWrapper != 0 && prevPane.container != 0 { | 648 | + | box.InsertChildAfter(newTitleBar, prevPane.container) |
| 640 | - | webkit.BoxInsertChildAfter(node.parent.stackWrapper, newTitleBar, prevPane.container) | 649 | + | } |
| 641 | } | 650 | } | ||
| 642 | } | 651 | } | ||
| 643 | 652 | ||||
| @@ -646,18 +655,18 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||||
| 646 | } | 655 | } | ||
| 647 | 656 | ||||
| 648 | // createTitleBarWithTitle creates a title bar with a specific title and optional favicon | 657 | // createTitleBarWithTitle creates a title bar with a specific title and optional favicon | ||
| 649 | - | func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL string) uintptr { | 658 | + | func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL string) gtk.Widgetter { |
| 650 | - | titleBox := webkit.NewBox(webkit.OrientationHorizontal, 8) | 659 | + | titleBox := gtk.NewBox(gtk.OrientationHorizontal, 8) |
| 651 | - | if titleBox == 0 { | 660 | + | if titleBox == nil { |
| 652 | - | return 0 | 661 | + | return nil |
| 653 | } | 662 | } | ||
| 654 | 663 | ||||
| 655 | - | webkit.WidgetAddCSSClass(titleBox, "stacked-pane-title") | 664 | + | titleBox.AddCSSClass("stacked-pane-title") |
| 656 | - | webkit.WidgetSetHExpand(titleBox, true) | 665 | + | titleBox.SetHExpand(true) |
| 657 | - | webkit.WidgetSetVExpand(titleBox, false) | 666 | + | titleBox.SetVExpand(false) |
| 658 | 667 | ||||
| 659 | // Add favicon placeholder for debugging (always visible) | 668 | // Add favicon placeholder for debugging (always visible) | ||
| 660 | - | var faviconImg uintptr | 669 | + | var faviconImg gtk.Widgetter |
| 661 | var debugReason string | 670 | var debugReason string | ||
| 662 | 671 | ||||
| 663 | if pageURL == "" { | 672 | if pageURL == "" { | ||
| @@ -691,10 +700,12 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str | |||||
| 691 | faviconPath := faviconCache.GetCachedPath(faviconURL) | 700 | faviconPath := faviconCache.GetCachedPath(faviconURL) | ||
| 692 | if faviconPath != "" { | 701 | if faviconPath != "" { | ||
| 693 | log.Printf("[favicon] Loading cached favicon from: %s", faviconPath) | 702 | log.Printf("[favicon] Loading cached favicon from: %s", faviconPath) | ||
| 694 | - | faviconImg = webkit.ImageNewFromFile(faviconPath) | 703 | + | faviconImg = gtk.NewImageFromFile(faviconPath) |
| 695 | - | if faviconImg != 0 { | 704 | + | if faviconImg != nil { |
| 696 | - | webkit.ImageSetPixelSize(faviconImg, 16) | 705 | + | if img, ok := faviconImg.(*gtk.Image); ok { |
| 697 | - | webkit.WidgetAddCSSClass(faviconImg, "stacked-pane-favicon") | 706 | + | img.SetPixelSize(16) |
| 707 | + | } | |||
| 708 | + | faviconImg.AddCSSClass("stacked-pane-favicon") | |||
| 698 | log.Printf("[favicon] Successfully created favicon image widget") | 709 | log.Printf("[favicon] Successfully created favicon image widget") | ||
| 699 | } else { | 710 | } else { | ||
| 700 | debugReason = "image widget creation failed" | 711 | debugReason = "image widget creation failed" | ||
| @@ -711,32 +722,32 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str | |||||
| 711 | } | 722 | } | ||
| 712 | 723 | ||||
| 713 | // Add favicon or placeholder | 724 | // Add favicon or placeholder | ||
| 714 | - | if faviconImg != 0 { | 725 | + | if faviconImg != nil { |
| 715 | - | webkit.BoxAppend(titleBox, faviconImg) | 726 | + | titleBox.Append(faviconImg) |
| 716 | - | webkit.WidgetShow(faviconImg) | 727 | + | faviconImg.Show() |
| 717 | } else { | 728 | } else { | ||
| 718 | // DEBUG: Add visible placeholder label to show code is running | 729 | // DEBUG: Add visible placeholder label to show code is running | ||
| 719 | - | placeholderLabel := webkit.NewLabel(faviconPlaceholder) | 730 | + | placeholderLabel := gtk.NewLabel(faviconPlaceholder) |
| 720 | - | if placeholderLabel != 0 { | 731 | + | if placeholderLabel != nil { |
| 721 | - | webkit.WidgetAddCSSClass(placeholderLabel, "stacked-pane-favicon-placeholder") | 732 | + | placeholderLabel.AddCSSClass("stacked-pane-favicon-placeholder") |
| 722 | - | webkit.BoxAppend(titleBox, placeholderLabel) | 733 | + | titleBox.Append(placeholderLabel) |
| 723 | - | webkit.WidgetShow(placeholderLabel) | 734 | + | placeholderLabel.Show() |
| 724 | log.Printf("[favicon] Using placeholder for %s (reason: %s)", pageURL, debugReason) | 735 | log.Printf("[favicon] Using placeholder for %s (reason: %s)", pageURL, debugReason) | ||
| 725 | } | 736 | } | ||
| 726 | } | 737 | } | ||
| 727 | 738 | ||||
| 728 | // Create title label with the specified title | 739 | // Create title label with the specified title | ||
| 729 | - | titleLabel := webkit.NewLabel(title) | 740 | + | titleLabel := gtk.NewLabel(title) |
| 730 | - | if titleLabel == 0 { | 741 | + | if titleLabel == nil { |
| 731 | return titleBox | 742 | return titleBox | ||
| 732 | } | 743 | } | ||
| 733 | 744 | ||||
| 734 | - | webkit.WidgetAddCSSClass(titleLabel, "stacked-pane-title-text") | 745 | + | titleLabel.AddCSSClass("stacked-pane-title-text") |
| 735 | - | webkit.LabelSetEllipsize(titleLabel, webkit.EllipsizeEnd) | 746 | + | titleLabel.SetEllipsize(2) // pango.EllipsizeEnd = 2 |
| 736 | 747 | ||||
| 737 | - | webkit.BoxAppend(titleBox, titleLabel) | 748 | + | titleBox.Append(titleLabel) |
| 738 | - | webkit.WidgetShow(titleBox) | 749 | + | titleBox.Show() |
| 739 | - | webkit.WidgetShow(titleLabel) | 750 | + | titleLabel.Show() |
| 740 | 751 | ||||
| 741 | return titleBox | 752 | return titleBox | ||
| 742 | } | 753 | } | ||
| @@ -775,15 +786,15 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 775 | 786 | ||||
| 776 | // Remove the pane widgets from the stack container before mutating the tree. | 787 | // Remove the pane widgets from the stack container before mutating the tree. | ||
| 777 | stackBox := stackNode.stackWrapper | 788 | stackBox := stackNode.stackWrapper | ||
| 778 | - | if stackBox == 0 { | 789 | + | if stackBox == nil { |
| 779 | stackBox = stackNode.container | 790 | stackBox = stackNode.container | ||
| 780 | } | 791 | } | ||
| 781 | - | if stackBox != 0 { | 792 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil { |
| 782 | - | if node.titleBar != 0 { | 793 | + | if node.titleBar != nil { |
| 783 | - | webkit.BoxRemove(stackBox, node.titleBar) | 794 | + | box.Remove(node.titleBar) |
| 784 | } | 795 | } | ||
| 785 | - | if node.container != 0 { | 796 | + | if node.container != nil { |
| 786 | - | webkit.BoxRemove(stackBox, node.container) | 797 | + | box.Remove(node.container) |
| 787 | } | 798 | } | ||
| 788 | } | 799 | } | ||
| 789 | 800 | ||||
| @@ -796,30 +807,30 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 796 | parent := stackNode.parent | 807 | parent := stackNode.parent | ||
| 797 | if parent == nil { | 808 | if parent == nil { | ||
| 798 | // Stack was the root of the workspace — closing this pane exits the app. | 809 | // Stack was the root of the workspace — closing this pane exits the app. | ||
| 799 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | 810 | + | if stackNode.container != nil { |
| 800 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | 811 | + | if stackNode.container.Parent() != nil { |
| 801 | - | webkit.WidgetUnparent(stackNode.container) | 812 | + | stackNode.container.Unparent() |
| 802 | } | 813 | } | ||
| 803 | } | 814 | } | ||
| 804 | if spm.wm.window != nil { | 815 | if spm.wm.window != nil { | ||
| 805 | - | spm.wm.window.SetChild(0) | 816 | + | spm.wm.window.SetChild(nil) |
| 806 | } | 817 | } | ||
| 807 | _, err := spm.wm.cleanupAndExit(node) | 818 | _, err := spm.wm.cleanupAndExit(node) | ||
| 808 | return err | 819 | return err | ||
| 809 | } | 820 | } | ||
| 810 | 821 | ||||
| 811 | // Detach the empty stack container from its GtkPaned parent. | 822 | // Detach the empty stack container from its GtkPaned parent. | ||
| 812 | - | if parent.container != 0 && webkit.WidgetIsValid(parent.container) { | 823 | + | if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil { |
| 813 | if parent.left == stackNode { | 824 | if parent.left == stackNode { | ||
| 814 | - | webkit.PanedSetStartChild(parent.container, 0) | 825 | + | paned.SetStartChild(nil) |
| 815 | } else if parent.right == stackNode { | 826 | } else if parent.right == stackNode { | ||
| 816 | - | webkit.PanedSetEndChild(parent.container, 0) | 827 | + | paned.SetEndChild(nil) |
| 817 | } | 828 | } | ||
| 818 | - | webkit.WidgetQueueAllocate(parent.container) | 829 | + | paned.QueueAllocate() |
| 819 | } | 830 | } | ||
| 820 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | 831 | + | if stackNode.container != nil { |
| 821 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | 832 | + | if stackNode.container.Parent() != nil { |
| 822 | - | webkit.WidgetUnparent(stackNode.container) | 833 | + | stackNode.container.Unparent() |
| 823 | } | 834 | } | ||
| 824 | } | 835 | } | ||
| 825 | 836 | ||||
| @@ -840,8 +851,8 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 840 | } | 851 | } | ||
| 841 | 852 | ||||
| 842 | stackNode.parent = nil | 853 | stackNode.parent = nil | ||
| 843 | - | stackNode.container = 0 | 854 | + | stackNode.container = nil |
| 844 | - | stackNode.stackWrapper = 0 | 855 | + | stackNode.stackWrapper = nil |
| 845 | stackNode.stackedPanes = nil | 856 | stackNode.stackedPanes = nil | ||
| 846 | 857 | ||||
| 847 | return nil | 858 | return nil | ||
| @@ -852,25 +863,25 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 852 | parent := stackNode.parent | 863 | parent := stackNode.parent | ||
| 853 | lastPaneContainer := lastPane.container | 864 | lastPaneContainer := lastPane.container | ||
| 854 | 865 | ||||
| 855 | - | if lastPane.titleBar != 0 && stackBox != 0 { | 866 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil && lastPane.titleBar != nil { |
| 856 | - | webkit.BoxRemove(stackBox, lastPane.titleBar) | 867 | + | box.Remove(lastPane.titleBar) |
| 857 | } | 868 | } | ||
| 858 | - | if stackBox != 0 && lastPaneContainer != 0 { | 869 | + | if box, ok := stackBox.(*gtk.Box); ok && box != nil && lastPaneContainer != nil { |
| 859 | - | webkit.BoxRemove(stackBox, lastPaneContainer) | 870 | + | box.Remove(lastPaneContainer) |
| 860 | } | 871 | } | ||
| 861 | - | if stackNode.container != 0 && webkit.WidgetIsValid(stackNode.container) { | 872 | + | if stackNode.container != nil { |
| 862 | - | if webkit.WidgetGetParent(stackNode.container) != 0 { | 873 | + | if stackNode.container.Parent() != nil { |
| 863 | - | webkit.WidgetUnparent(stackNode.container) | 874 | + | stackNode.container.Unparent() |
| 864 | } | 875 | } | ||
| 865 | } | 876 | } | ||
| 866 | 877 | ||||
| 867 | if parent == nil { | 878 | if parent == nil { | ||
| 868 | spm.wm.root = lastPane | 879 | spm.wm.root = lastPane | ||
| 869 | lastPane.parent = nil | 880 | lastPane.parent = nil | ||
| 870 | - | if spm.wm.window != nil && lastPaneContainer != 0 { | 881 | + | if spm.wm.window != nil && lastPaneContainer != nil { |
| 871 | spm.wm.window.SetChild(lastPaneContainer) | 882 | spm.wm.window.SetChild(lastPaneContainer) | ||
| 872 | - | webkit.WidgetQueueAllocate(lastPaneContainer) | 883 | + | lastPaneContainer.QueueAllocate() |
| 873 | - | webkit.WidgetShow(lastPaneContainer) | 884 | + | lastPaneContainer.Show() |
| 874 | } | 885 | } | ||
| 875 | } else { | 886 | } else { | ||
| 876 | if parent.left == stackNode { | 887 | if parent.left == stackNode { | ||
| @@ -879,36 +890,36 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 879 | parent.right = lastPane | 890 | parent.right = lastPane | ||
| 880 | } | 891 | } | ||
| 881 | lastPane.parent = parent | 892 | lastPane.parent = parent | ||
| 882 | - | if parent.container != 0 && webkit.WidgetIsValid(parent.container) && lastPaneContainer != 0 { | 893 | + | if paned, ok := parent.container.(*gtk.Paned); ok && paned != nil && lastPaneContainer != nil { |
| 883 | if parent.left == lastPane { | 894 | if parent.left == lastPane { | ||
| 884 | - | webkit.PanedSetStartChild(parent.container, lastPaneContainer) | 895 | + | paned.SetStartChild(lastPaneContainer) |
| 885 | } else { | 896 | } else { | ||
| 886 | - | webkit.PanedSetEndChild(parent.container, lastPaneContainer) | 897 | + | paned.SetEndChild(lastPaneContainer) |
| 887 | } | 898 | } | ||
| 888 | - | webkit.WidgetQueueAllocate(parent.container) | 899 | + | paned.QueueAllocate() |
| 889 | } | 900 | } | ||
| 890 | - | if lastPaneContainer != 0 { | 901 | + | if lastPaneContainer != nil { |
| 891 | - | webkit.WidgetSetVisible(lastPaneContainer, true) | 902 | + | lastPaneContainer.SetVisible(true) |
| 892 | } | 903 | } | ||
| 893 | } | 904 | } | ||
| 894 | 905 | ||||
| 895 | lastPane.isStacked = false | 906 | lastPane.isStacked = false | ||
| 896 | lastPane.stackedPanes = nil | 907 | lastPane.stackedPanes = nil | ||
| 897 | - | lastPane.titleBar = 0 | 908 | + | lastPane.titleBar = nil |
| 898 | spm.wm.viewToNode[lastPane.pane.webView] = lastPane | 909 | spm.wm.viewToNode[lastPane.pane.webView] = lastPane | ||
| 899 | 910 | ||||
| 900 | generation := spm.wm.nextCleanupGeneration() | 911 | generation := spm.wm.nextCleanupGeneration() | ||
| 901 | spm.wm.cleanupPane(node, generation) | 912 | spm.wm.cleanupPane(node, generation) | ||
| 902 | 913 | ||||
| 903 | spm.wm.SetActivePane(lastPane, SourceClose) | 914 | spm.wm.SetActivePane(lastPane, SourceClose) | ||
| 904 | - | if lastPaneWidget := lastPane.pane.webView.Widget(); lastPaneWidget != 0 { | 915 | + | if lastPaneWidget := lastPane.pane.webView.Widget(); lastPaneWidget != nil { |
| 905 | - | webkit.WidgetShow(lastPaneWidget) | 916 | + | lastPaneWidget.Show() |
| 906 | - | webkit.WidgetGrabFocus(lastPaneWidget) | 917 | + | lastPaneWidget.GrabFocus() |
| 907 | } | 918 | } | ||
| 908 | 919 | ||||
| 909 | stackNode.parent = nil | 920 | stackNode.parent = nil | ||
| 910 | - | stackNode.container = 0 | 921 | + | stackNode.container = nil |
| 911 | - | stackNode.stackWrapper = 0 | 922 | + | stackNode.stackWrapper = nil |
| 912 | stackNode.stackedPanes = nil | 923 | stackNode.stackedPanes = nil | ||
| 913 | 924 | ||||
| 914 | log.Printf("[workspace] converted stack back to regular pane") | 925 | log.Printf("[workspace] converted stack back to regular pane") | ||
| @@ -939,7 +950,7 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 939 | } | 950 | } | ||
| 940 | 951 | ||||
| 941 | // handleTitleBarClick handles clicks on title bars to switch the active pane | 952 | // handleTitleBarClick handles clicks on title bars to switch the active pane | ||
| 942 | - | func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uintptr) { | 953 | + | func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uint64) { |
| 943 | pane, exists := spm.titleBarToPane[titleBarID] | 954 | pane, exists := spm.titleBarToPane[titleBarID] | ||
| 944 | if !exists || pane == nil || pane.parent == nil || !pane.parent.isStacked { | 955 | if !exists || pane == nil || pane.parent == nil || !pane.parent.isStacked { | ||
| 945 | return | 956 | return | ||
| @@ -957,7 +968,7 @@ func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uintptr) { | |||||
| 957 | 968 | ||||
| 958 | // Update title bar for the pane transitioning from ACTIVE to INACTIVE | 969 | // Update title bar for the pane transitioning from ACTIVE to INACTIVE | ||
| 959 | currentActivePane := stackNode.stackedPanes[stackNode.activeStackIndex] | 970 | currentActivePane := stackNode.stackedPanes[stackNode.activeStackIndex] | ||
| 960 | - | if currentActivePane.pane != nil && currentActivePane.pane.webView != nil && currentActivePane.titleBar != 0 { | 971 | + | if currentActivePane.pane != nil && currentActivePane.pane.webView != nil && currentActivePane.titleBar != nil { |
| 961 | currentTitle := currentActivePane.pane.webView.GetTitle() | 972 | currentTitle := currentActivePane.pane.webView.GetTitle() | ||
| 962 | if currentTitle != "" { | 973 | if currentTitle != "" { | ||
| 963 | spm.updateTitleBarLabel(currentActivePane, currentTitle) | 974 | spm.updateTitleBarLabel(currentActivePane, currentTitle) | ||