fix(workspace): ensure root pane redraw after reattachment
- commit
- 6611f459944b8df65e45d417fe148c0bc4dc10c4
- author
- Brice · · 2025-10-04 20:09 UTC
- committer
- Brice · · 2025-10-05 13:54 UTC
- parent
- b29fd2f
- signature
- unsigned
1 file · +49 -17
M internal/app/browser/workspace_pane_ops.go +49 -17
| @@ -456,6 +456,18 @@ func (wm *WorkspaceManager) insertPopupPane(target *paneNode, newPane *BrowserPa | |||
| 456 | 456 | ||
| 457 | 457 | webkit.WidgetShow(paned) | |
| 458 | 458 | ||
| 459 | + | // Force GTK to re-layout the new GtkPaned container once both children are attached. | |
| 460 | + | wm.scheduleIdleGuarded(func() bool { | |
| 461 | + | if split == nil || !split.widgetValid { | |
| 462 | + | return false | |
| 463 | + | } | |
| 464 | + | if split.container != 0 && webkit.WidgetIsValid(split.container) { | |
| 465 | + | webkit.WidgetQueueResize(split.container) | |
| 466 | + | webkit.WidgetQueueDraw(split.container) | |
| 467 | + | } | |
| 468 | + | return false | |
| 469 | + | }, split) | |
| 470 | + | ||
| 459 | 471 | // CRITICAL: Set initial 50/50 split position after showing paned | |
| 460 | 472 | // GTK needs the widget to be realized before we can set position based on allocation | |
| 461 | 473 | // Schedule this to run after GTK has allocated space to the paned | |
| @@ -946,12 +958,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||
| 946 | 958 | ||
| 947 | 959 | // Attach to window | |
| 948 | 960 | if onlyChild.container != 0 && webkit.WidgetIsValid(onlyChild.container) { | |
| 949 | - | // GTK4 window.SetChild automatically handles unparenting | |
| 950 | - | wm.window.SetChild(onlyChild.container) | |
| 951 | - | webkit.WidgetSetHExpand(onlyChild.container, true) | |
| 952 | - | webkit.WidgetSetVExpand(onlyChild.container, true) | |
| 953 | - | webkit.WidgetQueueAllocate(onlyChild.container) | |
| 954 | - | webkit.WidgetShow(onlyChild.container) | |
| 961 | + | wm.attachRoot(onlyChild) | |
| 955 | 962 | } | |
| 956 | 963 | ||
| 957 | 964 | // Cleanup the now-orphaned paned | |
| @@ -1008,19 +1015,44 @@ func (wm *WorkspaceManager) attachRoot(root *paneNode) { | |||
| 1008 | 1015 | if root == nil || root.container == 0 || wm.window == nil { | |
| 1009 | 1016 | return | |
| 1010 | 1017 | } | |
| 1011 | - | if webkit.WidgetIsValid(root.container) { | |
| 1012 | - | // GTK4 window.SetChild automatically handles unparenting from old parent | |
| 1013 | - | // DO NOT unparent manually as it can destroy complex widgets like GtkBox | |
| 1014 | - | wm.window.SetChild(root.container) | |
| 1015 | - | webkit.WidgetQueueAllocate(root.container) | |
| 1016 | - | webkit.WidgetShow(root.container) | |
| 1018 | + | if !webkit.WidgetIsValid(root.container) { | |
| 1019 | + | return | |
| 1020 | + | } | |
| 1017 | 1021 | ||
| 1018 | - | // Verify attachment succeeded | |
| 1019 | - | if finalParent := webkit.WidgetGetParent(root.container); finalParent == 0 { | |
| 1020 | - | log.Printf("[workspace] WARNING: widget %#x has no parent after SetChild", root.container) | |
| 1021 | - | } else { | |
| 1022 | - | log.Printf("[workspace] attachRoot successful: widget %#x now child of %#x", root.container, finalParent) | |
| 1022 | + | // Clear any existing window child to avoid GTK warnings when swapping roots. | |
| 1023 | + | wm.window.SetChild(0) | |
| 1024 | + | ||
| 1025 | + | // Unparent from the previous container (paned, stack, etc.) before reattaching. | |
| 1026 | + | if parent := webkit.WidgetGetParent(root.container); parent != 0 { | |
| 1027 | + | log.Printf("[workspace] unparenting widget %#x from parent %#x before window attach", root.container, parent) | |
| 1028 | + | webkit.WidgetUnparent(root.container) | |
| 1029 | + | } | |
| 1030 | + | ||
| 1031 | + | webkit.WidgetSetHExpand(root.container, true) | |
| 1032 | + | webkit.WidgetSetVExpand(root.container, true) | |
| 1033 | + | ||
| 1034 | + | wm.window.SetChild(root.container) | |
| 1035 | + | webkit.WidgetQueueAllocate(root.container) | |
| 1036 | + | webkit.WidgetShow(root.container) | |
| 1037 | + | webkit.WidgetQueueResize(root.container) | |
| 1038 | + | webkit.WidgetQueueDraw(root.container) | |
| 1039 | + | ||
| 1040 | + | wm.scheduleIdleGuarded(func() bool { | |
| 1041 | + | if root == nil || !root.widgetValid { | |
| 1042 | + | return false | |
| 1043 | + | } | |
| 1044 | + | if root.container != 0 && webkit.WidgetIsValid(root.container) { | |
| 1045 | + | webkit.WidgetQueueResize(root.container) | |
| 1046 | + | webkit.WidgetQueueDraw(root.container) | |
| 1023 | 1047 | } | |
| 1048 | + | return false | |
| 1049 | + | }, root) | |
| 1050 | + | ||
| 1051 | + | // Verify attachment succeeded | |
| 1052 | + | if finalParent := webkit.WidgetGetParent(root.container); finalParent == 0 { | |
| 1053 | + | log.Printf("[workspace] WARNING: widget %#x has no parent after SetChild", root.container) | |
| 1054 | + | } else { | |
| 1055 | + | log.Printf("[workspace] attachRoot successful: widget %#x now child of %#x", root.container, finalParent) | |
| 1024 | 1056 | } | |
| 1025 | 1057 | } | |
| 1026 | 1058 | ||
| @@ -456,6 +456,18 @@ func (wm *WorkspaceManager) insertPopupPane(target *paneNode, newPane *BrowserPa | |||||
| 456 | 456 | ||||
| 457 | webkit.WidgetShow(paned) | 457 | webkit.WidgetShow(paned) | ||
| 458 | 458 | ||||
| 459 | + | // Force GTK to re-layout the new GtkPaned container once both children are attached. | |||
| 460 | + | wm.scheduleIdleGuarded(func() bool { | |||
| 461 | + | if split == nil || !split.widgetValid { | |||
| 462 | + | return false | |||
| 463 | + | } | |||
| 464 | + | if split.container != 0 && webkit.WidgetIsValid(split.container) { | |||
| 465 | + | webkit.WidgetQueueResize(split.container) | |||
| 466 | + | webkit.WidgetQueueDraw(split.container) | |||
| 467 | + | } | |||
| 468 | + | return false | |||
| 469 | + | }, split) | |||
| 470 | + | ||||
| 459 | // CRITICAL: Set initial 50/50 split position after showing paned | 471 | // CRITICAL: Set initial 50/50 split position after showing paned | ||
| 460 | // GTK needs the widget to be realized before we can set position based on allocation | 472 | // GTK needs the widget to be realized before we can set position based on allocation | ||
| 461 | // Schedule this to run after GTK has allocated space to the paned | 473 | // Schedule this to run after GTK has allocated space to the paned | ||
| @@ -946,12 +958,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||||
| 946 | 958 | ||||
| 947 | // Attach to window | 959 | // Attach to window | ||
| 948 | if onlyChild.container != 0 && webkit.WidgetIsValid(onlyChild.container) { | 960 | if onlyChild.container != 0 && webkit.WidgetIsValid(onlyChild.container) { | ||
| 949 | - | // GTK4 window.SetChild automatically handles unparenting | 961 | + | wm.attachRoot(onlyChild) |
| 950 | - | wm.window.SetChild(onlyChild.container) | |||
| 951 | - | webkit.WidgetSetHExpand(onlyChild.container, true) | |||
| 952 | - | webkit.WidgetSetVExpand(onlyChild.container, true) | |||
| 953 | - | webkit.WidgetQueueAllocate(onlyChild.container) | |||
| 954 | - | webkit.WidgetShow(onlyChild.container) | |||
| 955 | } | 962 | } | ||
| 956 | 963 | ||||
| 957 | // Cleanup the now-orphaned paned | 964 | // Cleanup the now-orphaned paned | ||
| @@ -1008,19 +1015,44 @@ func (wm *WorkspaceManager) attachRoot(root *paneNode) { | |||||
| 1008 | if root == nil || root.container == 0 || wm.window == nil { | 1015 | if root == nil || root.container == 0 || wm.window == nil { | ||
| 1009 | return | 1016 | return | ||
| 1010 | } | 1017 | } | ||
| 1011 | - | if webkit.WidgetIsValid(root.container) { | 1018 | + | if !webkit.WidgetIsValid(root.container) { |
| 1012 | - | // GTK4 window.SetChild automatically handles unparenting from old parent | 1019 | + | return |
| 1013 | - | // DO NOT unparent manually as it can destroy complex widgets like GtkBox | 1020 | + | } |
| 1014 | - | wm.window.SetChild(root.container) | |||
| 1015 | - | webkit.WidgetQueueAllocate(root.container) | |||
| 1016 | - | webkit.WidgetShow(root.container) | |||
| 1017 | 1021 | ||||
| 1018 | - | // Verify attachment succeeded | 1022 | + | // Clear any existing window child to avoid GTK warnings when swapping roots. |
| 1019 | - | if finalParent := webkit.WidgetGetParent(root.container); finalParent == 0 { | 1023 | + | wm.window.SetChild(0) |
| 1020 | - | log.Printf("[workspace] WARNING: widget %#x has no parent after SetChild", root.container) | 1024 | + | |
| 1021 | - | } else { | 1025 | + | // Unparent from the previous container (paned, stack, etc.) before reattaching. |
| 1022 | - | log.Printf("[workspace] attachRoot successful: widget %#x now child of %#x", root.container, finalParent) | 1026 | + | if parent := webkit.WidgetGetParent(root.container); parent != 0 { |
| 1027 | + | log.Printf("[workspace] unparenting widget %#x from parent %#x before window attach", root.container, parent) | |||
| 1028 | + | webkit.WidgetUnparent(root.container) | |||
| 1029 | + | } | |||
| 1030 | + | ||||
| 1031 | + | webkit.WidgetSetHExpand(root.container, true) | |||
| 1032 | + | webkit.WidgetSetVExpand(root.container, true) | |||
| 1033 | + | ||||
| 1034 | + | wm.window.SetChild(root.container) | |||
| 1035 | + | webkit.WidgetQueueAllocate(root.container) | |||
| 1036 | + | webkit.WidgetShow(root.container) | |||
| 1037 | + | webkit.WidgetQueueResize(root.container) | |||
| 1038 | + | webkit.WidgetQueueDraw(root.container) | |||
| 1039 | + | ||||
| 1040 | + | wm.scheduleIdleGuarded(func() bool { | |||
| 1041 | + | if root == nil || !root.widgetValid { | |||
| 1042 | + | return false | |||
| 1043 | + | } | |||
| 1044 | + | if root.container != 0 && webkit.WidgetIsValid(root.container) { | |||
| 1045 | + | webkit.WidgetQueueResize(root.container) | |||
| 1046 | + | webkit.WidgetQueueDraw(root.container) | |||
| 1023 | } | 1047 | } | ||
| 1048 | + | return false | |||
| 1049 | + | }, root) | |||
| 1050 | + | ||||
| 1051 | + | // Verify attachment succeeded | |||
| 1052 | + | if finalParent := webkit.WidgetGetParent(root.container); finalParent == 0 { | |||
| 1053 | + | log.Printf("[workspace] WARNING: widget %#x has no parent after SetChild", root.container) | |||
| 1054 | + | } else { | |||
| 1055 | + | log.Printf("[workspace] attachRoot successful: widget %#x now child of %#x", root.container, finalParent) | |||
| 1024 | } | 1056 | } | ||
| 1025 | } | 1057 | } | ||
| 1026 | 1058 | ||||