From 168927713c1ea21568a3797b2590c882bf9fbc55 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Wed, 8 Apr 2026 20:40:20 -0700 Subject: [PATCH] Use desktop overlay model for phone header menus --- main.go | 61 +++++++++++++++++++--------------------------------- main_test.go | 13 +++++++++++ 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/main.go b/main.go index 11d860e..415d06f 100644 --- a/main.go +++ b/main.go @@ -5512,36 +5512,8 @@ func (u *ui) header(gtx layout.Context) layout.Dimensions { if u.shouldShowLifecycleSetup() || u.isVaultLocked() { return layout.Dimensions{} } - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - gtx.Constraints.Min.X = gtx.Constraints.Max.X - return u.headerActions(gtx) - }), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - if !u.syncMenuOpen && !u.mainMenuOpen { - return layout.Dimensions{} - } - return layout.Inset{Top: unit.Dp(6), Left: unit.Dp(16), Right: unit.Dp(16)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - return layout.E.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - var menu layout.Widget - if u.syncMenuOpen { - menu = u.syncMenu - } else { - menu = u.mainMenu - } - measureGTX := gtx - measureGTX.Constraints.Min = image.Point{} - measureGTX.Constraints.Max.X = gtx.Constraints.Max.X - macro := op.Record(gtx.Ops) - dims := menu(measureGTX) - _ = macro.Stop() - gtx.Constraints.Min.X = dims.Size.X - gtx.Constraints.Max.X = dims.Size.X - return menu(gtx) - }) - }) - }), - ) + gtx.Constraints.Min.X = gtx.Constraints.Max.X + return u.headerActions(gtx) } if u.shouldShowDesktopWorkingHeader() { return layout.Dimensions{} @@ -7348,22 +7320,29 @@ func (u *ui) phoneHeaderMenus(gtx layout.Context) layout.Dimensions { return layout.Dimensions{} } gtx.Constraints.Min = gtx.Constraints.Max + contentInsetPx := gtx.Dp(unit.Dp(16)) + contentWidth := max(0, gtx.Constraints.Max.X-(contentInsetPx*2)) drawMenu := func(anchor image.Point, menu layout.Widget) layout.Dimensions { - _ = anchor - return layout.NE.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - return layout.Inset{ - Top: unit.Dp(56), - Right: unit.Dp(16), - }.Layout(gtx, menu) - }) + menuGTX := gtx + menuGTX.Constraints.Min = image.Point{} + menuGTX.Constraints.Max.X = contentWidth + menuOps := op.Record(gtx.Ops) + menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, menu) + menuCall := menuOps.Stop() + menuX := contentInsetPx + anchoredMenuOriginX(contentWidth, 0, anchor.X, menuDims.Size.X) + menuY := contentInsetPx + anchor.Y + stack := op.Offset(image.Pt(menuX, menuY)).Push(gtx.Ops) + menuCall.Add(gtx.Ops) + stack.Pop() + return layout.Dimensions{Size: gtx.Constraints.Max} } if u.syncMenuVisibleOnPhone() { - drawMenu(u.phoneSyncMenuAnchor, u.syncMenu) + _ = drawMenu(u.phoneSyncMenuAnchor, u.syncMenu) } if u.mainMenuVisibleOnPhone() { - drawMenu(u.phoneMainMenuAnchor, u.mainMenu) + _ = drawMenu(u.phoneMainMenuAnchor, u.mainMenu) } return layout.Dimensions{Size: gtx.Constraints.Max} } @@ -7384,6 +7363,10 @@ func (u *ui) syncMenuRightAlignsToTrigger() bool { return true } +func (u *ui) headerMenusUseOverlayModel() bool { + return true +} + func (u *ui) mainMenuDropsBelowTrigger() bool { return true } diff --git a/main_test.go b/main_test.go index 72ae6e3..eeefb47 100644 --- a/main_test.go +++ b/main_test.go @@ -356,6 +356,19 @@ func TestUIMainMenuAnchorsMatchAcrossModes(t *testing.T) { } } +func TestUIHeaderMenusUseOverlayModelAcrossModes(t *testing.T) { + t.Parallel() + + desktop := newUIWithSession("desktop", summarySession{hasVault: true}) + desktop.state.Section = appstate.SectionEntries + phone := newUIWithSession("phone", summarySession{hasVault: true}) + phone.state.Section = appstate.SectionEntries + + if !desktop.headerMenusUseOverlayModel() || !phone.headerMenusUseOverlayModel() { + t.Fatal("header menus should use the same overlay model across desktop and phone") + } +} + func TestAnchoredMenuXAllowsWiderMenusToExtendLeft(t *testing.T) { t.Parallel()