feat(window): add overlay infrastructure for positioned UI elements
- commit
- adc2dc4430a9b1e9b700a9921680d3d5b78f5d87
- author
- bnema · · 2025-12-16 21:41 UTC
- parent
- 12a6535
- signature
- unsigned
1 file · +31 -6
M internal/ui/window/main_window.go +31 -6
| @@ -19,10 +19,11 @@ const ( | |||
| 19 | 19 | ||
| 20 | 20 | // MainWindow represents the main browser window. | |
| 21 | 21 | type MainWindow struct { | |
| 22 | - | window *gtk.ApplicationWindow | |
| 23 | - | rootBox *gtk.Box // Vertical: tab bar + content | |
| 24 | - | tabBar *component.TabBar | |
| 25 | - | contentArea *gtk.Box // Container for workspace content | |
| 22 | + | window *gtk.ApplicationWindow | |
| 23 | + | rootBox *gtk.Box // Vertical: tab bar + content | |
| 24 | + | tabBar *component.TabBar | |
| 25 | + | contentOverlay *gtk.Overlay // Overlay for content + omnibox | |
| 26 | + | contentArea *gtk.Box // Container for workspace content | |
| 26 | 27 | ||
| 27 | 28 | cfg *config.Config | |
| 28 | 29 | logger zerolog.Logger | |
| @@ -66,9 +67,22 @@ func New(ctx context.Context, app *gtk.Application, cfg *config.Config) (*MainWi | |||
| 66 | 67 | return nil, ErrWidgetCreationFailed("tabBar") | |
| 67 | 68 | } | |
| 68 | 69 | ||
| 70 | + | // Create content overlay (for omnibox and other overlays) | |
| 71 | + | mw.contentOverlay = gtk.NewOverlay() | |
| 72 | + | if mw.contentOverlay == nil { | |
| 73 | + | mw.tabBar.Destroy() | |
| 74 | + | mw.rootBox.Unref() | |
| 75 | + | mw.window.Unref() | |
| 76 | + | return nil, ErrWidgetCreationFailed("contentOverlay") | |
| 77 | + | } | |
| 78 | + | mw.contentOverlay.SetHexpand(true) | |
| 79 | + | mw.contentOverlay.SetVexpand(true) | |
| 80 | + | mw.contentOverlay.SetVisible(true) | |
| 81 | + | ||
| 69 | 82 | // Create content area | |
| 70 | 83 | mw.contentArea = gtk.NewBox(gtk.OrientationVerticalValue, 0) | |
| 71 | 84 | if mw.contentArea == nil { | |
| 85 | + | mw.contentOverlay.Unref() | |
| 72 | 86 | mw.tabBar.Destroy() | |
| 73 | 87 | mw.rootBox.Unref() | |
| 74 | 88 | mw.window.Unref() | |
| @@ -79,6 +93,9 @@ func New(ctx context.Context, app *gtk.Application, cfg *config.Config) (*MainWi | |||
| 79 | 93 | mw.contentArea.SetVisible(true) | |
| 80 | 94 | mw.contentArea.AddCssClass("content-area") | |
| 81 | 95 | ||
| 96 | + | // Set content area as the main child of the overlay | |
| 97 | + | mw.contentOverlay.SetChild(&mw.contentArea.Widget) | |
| 98 | + | ||
| 82 | 99 | // Assemble layout based on tab bar position | |
| 83 | 100 | mw.assembleLayout() | |
| 84 | 101 | ||
| @@ -97,12 +114,12 @@ func (mw *MainWindow) assembleLayout() { | |||
| 97 | 114 | ||
| 98 | 115 | if tabBarPos == "bottom" { | |
| 99 | 116 | // Content first, then tab bar | |
| 100 | - | mw.rootBox.Append(&mw.contentArea.Widget) | |
| 117 | + | mw.rootBox.Append(&mw.contentOverlay.Widget) | |
| 101 | 118 | mw.rootBox.Append(mw.tabBar.Widget()) | |
| 102 | 119 | } else { | |
| 103 | 120 | // Tab bar first, then content (default: top) | |
| 104 | 121 | mw.rootBox.Append(mw.tabBar.Widget()) | |
| 105 | - | mw.rootBox.Append(&mw.contentArea.Widget) | |
| 122 | + | mw.rootBox.Append(&mw.contentOverlay.Widget) | |
| 106 | 123 | } | |
| 107 | 124 | ||
| 108 | 125 | mw.logger.Debug(). | |
| @@ -148,6 +165,14 @@ func (mw *MainWindow) Window() *gtk.ApplicationWindow { | |||
| 148 | 165 | return mw.window | |
| 149 | 166 | } | |
| 150 | 167 | ||
| 168 | + | // AddOverlay adds a widget to the content overlay. | |
| 169 | + | // The widget will be displayed on top of the workspace content. | |
| 170 | + | func (mw *MainWindow) AddOverlay(widget *gtk.Widget) { | |
| 171 | + | if mw.contentOverlay != nil && widget != nil { | |
| 172 | + | mw.contentOverlay.AddOverlay(widget) | |
| 173 | + | } | |
| 174 | + | } | |
| 175 | + | ||
| 151 | 176 | // Destroy cleans up window resources. | |
| 152 | 177 | func (mw *MainWindow) Destroy() { | |
| 153 | 178 | if mw.tabBar != nil { | |
| @@ -19,10 +19,11 @@ const ( | |||||
| 19 | 19 | ||||
| 20 | // MainWindow represents the main browser window. | 20 | // MainWindow represents the main browser window. | ||
| 21 | type MainWindow struct { | 21 | type MainWindow struct { | ||
| 22 | - | window *gtk.ApplicationWindow | 22 | + | window *gtk.ApplicationWindow |
| 23 | - | rootBox *gtk.Box // Vertical: tab bar + content | 23 | + | rootBox *gtk.Box // Vertical: tab bar + content |
| 24 | - | tabBar *component.TabBar | 24 | + | tabBar *component.TabBar |
| 25 | - | contentArea *gtk.Box // Container for workspace content | 25 | + | contentOverlay *gtk.Overlay // Overlay for content + omnibox |
| 26 | + | contentArea *gtk.Box // Container for workspace content | |||
| 26 | 27 | ||||
| 27 | cfg *config.Config | 28 | cfg *config.Config | ||
| 28 | logger zerolog.Logger | 29 | logger zerolog.Logger | ||
| @@ -66,9 +67,22 @@ func New(ctx context.Context, app *gtk.Application, cfg *config.Config) (*MainWi | |||||
| 66 | return nil, ErrWidgetCreationFailed("tabBar") | 67 | return nil, ErrWidgetCreationFailed("tabBar") | ||
| 67 | } | 68 | } | ||
| 68 | 69 | ||||
| 70 | + | // Create content overlay (for omnibox and other overlays) | |||
| 71 | + | mw.contentOverlay = gtk.NewOverlay() | |||
| 72 | + | if mw.contentOverlay == nil { | |||
| 73 | + | mw.tabBar.Destroy() | |||
| 74 | + | mw.rootBox.Unref() | |||
| 75 | + | mw.window.Unref() | |||
| 76 | + | return nil, ErrWidgetCreationFailed("contentOverlay") | |||
| 77 | + | } | |||
| 78 | + | mw.contentOverlay.SetHexpand(true) | |||
| 79 | + | mw.contentOverlay.SetVexpand(true) | |||
| 80 | + | mw.contentOverlay.SetVisible(true) | |||
| 81 | + | ||||
| 69 | // Create content area | 82 | // Create content area | ||
| 70 | mw.contentArea = gtk.NewBox(gtk.OrientationVerticalValue, 0) | 83 | mw.contentArea = gtk.NewBox(gtk.OrientationVerticalValue, 0) | ||
| 71 | if mw.contentArea == nil { | 84 | if mw.contentArea == nil { | ||
| 85 | + | mw.contentOverlay.Unref() | |||
| 72 | mw.tabBar.Destroy() | 86 | mw.tabBar.Destroy() | ||
| 73 | mw.rootBox.Unref() | 87 | mw.rootBox.Unref() | ||
| 74 | mw.window.Unref() | 88 | mw.window.Unref() | ||
| @@ -79,6 +93,9 @@ func New(ctx context.Context, app *gtk.Application, cfg *config.Config) (*MainWi | |||||
| 79 | mw.contentArea.SetVisible(true) | 93 | mw.contentArea.SetVisible(true) | ||
| 80 | mw.contentArea.AddCssClass("content-area") | 94 | mw.contentArea.AddCssClass("content-area") | ||
| 81 | 95 | ||||
| 96 | + | // Set content area as the main child of the overlay | |||
| 97 | + | mw.contentOverlay.SetChild(&mw.contentArea.Widget) | |||
| 98 | + | ||||
| 82 | // Assemble layout based on tab bar position | 99 | // Assemble layout based on tab bar position | ||
| 83 | mw.assembleLayout() | 100 | mw.assembleLayout() | ||
| 84 | 101 | ||||
| @@ -97,12 +114,12 @@ func (mw *MainWindow) assembleLayout() { | |||||
| 97 | 114 | ||||
| 98 | if tabBarPos == "bottom" { | 115 | if tabBarPos == "bottom" { | ||
| 99 | // Content first, then tab bar | 116 | // Content first, then tab bar | ||
| 100 | - | mw.rootBox.Append(&mw.contentArea.Widget) | 117 | + | mw.rootBox.Append(&mw.contentOverlay.Widget) |
| 101 | mw.rootBox.Append(mw.tabBar.Widget()) | 118 | mw.rootBox.Append(mw.tabBar.Widget()) | ||
| 102 | } else { | 119 | } else { | ||
| 103 | // Tab bar first, then content (default: top) | 120 | // Tab bar first, then content (default: top) | ||
| 104 | mw.rootBox.Append(mw.tabBar.Widget()) | 121 | mw.rootBox.Append(mw.tabBar.Widget()) | ||
| 105 | - | mw.rootBox.Append(&mw.contentArea.Widget) | 122 | + | mw.rootBox.Append(&mw.contentOverlay.Widget) |
| 106 | } | 123 | } | ||
| 107 | 124 | ||||
| 108 | mw.logger.Debug(). | 125 | mw.logger.Debug(). | ||
| @@ -148,6 +165,14 @@ func (mw *MainWindow) Window() *gtk.ApplicationWindow { | |||||
| 148 | return mw.window | 165 | return mw.window | ||
| 149 | } | 166 | } | ||
| 150 | 167 | ||||
| 168 | + | // AddOverlay adds a widget to the content overlay. | |||
| 169 | + | // The widget will be displayed on top of the workspace content. | |||
| 170 | + | func (mw *MainWindow) AddOverlay(widget *gtk.Widget) { | |||
| 171 | + | if mw.contentOverlay != nil && widget != nil { | |||
| 172 | + | mw.contentOverlay.AddOverlay(widget) | |||
| 173 | + | } | |||
| 174 | + | } | |||
| 175 | + | ||||
| 151 | // Destroy cleans up window resources. | 176 | // Destroy cleans up window resources. | ||
| 152 | func (mw *MainWindow) Destroy() { | 177 | func (mw *MainWindow) Destroy() { | ||
| 153 | if mw.tabBar != nil { | 178 | if mw.tabBar != nil { | ||