diff --git a/main.go b/main.go index 66a9e93..8cfc4d4 100644 --- a/main.go +++ b/main.go @@ -5527,27 +5527,67 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions { if u.shouldShowDesktopWorkingHeader() { return layout.Dimensions{} } + spacing := gtx.Dp(unit.Dp(8)) + rowOriginX := 0 + var syncDims, lockDims, mainDims layout.Dimensions row := func(gtx layout.Context) layout.Dimensions { return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, - layout.Rigid(u.syncButtonGroup), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + syncDims = u.syncButtonGroup(gtx) + return syncDims + }), layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { btn := material.Button(u.theme, &u.lockVault, "Lock") - return btn.Layout(gtx) + lockDims = btn.Layout(gtx) + return lockDims }), layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout), - layout.Rigid(u.mainMenuButtonGroup), - ) - } - if u.mode == "phone" { - return layout.Flex{Alignment: layout.Middle}.Layout(gtx, - layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { - return layout.Dimensions{Size: gtx.Constraints.Min} + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + mainDims = u.mainMenuButtonGroup(gtx) + return mainDims }), - layout.Rigid(row), ) } - return row(gtx) + + rowOps := op.Record(gtx.Ops) + rowDims := row(gtx) + rowCall := rowOps.Stop() + + if u.mode == "phone" { + rowOriginX = max(0, gtx.Constraints.Max.X-rowDims.Size.X) + } + + drawMenu := func(menu layout.Widget, triggerRightX, triggerBottomY int) { + menuGTX := gtx + menuGTX.Constraints.Min = image.Point{} + menuGTX.Constraints.Max.X = gtx.Constraints.Max.X + menuOps := op.Record(gtx.Ops) + menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, menu) + menuCall := menuOps.Stop() + menuX := anchoredMenuOriginX(gtx.Constraints.Max.X, rowOriginX, triggerRightX, menuDims.Size.X) + stack := op.Offset(image.Pt(menuX, triggerBottomY)).Push(gtx.Ops) + menuCall.Add(gtx.Ops) + stack.Pop() + } + + rowStack := op.Offset(image.Pt(rowOriginX, 0)).Push(gtx.Ops) + rowCall.Add(gtx.Ops) + rowStack.Pop() + + if u.syncMenuOpen { + drawMenu(u.syncMenu, syncDims.Size.X, rowDims.Size.Y) + } + if u.mainMenuOpen { + triggerRightX := syncDims.Size.X + spacing + lockDims.Size.X + spacing + mainDims.Size.X + drawMenu(u.mainMenu, triggerRightX, rowDims.Size.Y) + } + + width := rowDims.Size.X + if u.mode == "phone" { + width = gtx.Constraints.Max.X + } + return layout.Dimensions{Size: image.Pt(width, rowDims.Size.Y)} } func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions { @@ -5670,22 +5710,7 @@ func (u *ui) syncButtonGroup(gtx layout.Context) layout.Dimensions { }), ) } - rowDims := row(gtx) - if !u.syncMenuOpen { - return rowDims - } - - menuGTX := gtx - menuGTX.Constraints.Min = image.Point{} - menuOps := op.Record(gtx.Ops) - menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, u.syncMenu) - menuCall := menuOps.Stop() - - menuX := anchoredMenuX(rowDims.Size.X, menuDims.Size.X) - stack := op.Offset(image.Pt(menuX, rowDims.Size.Y)).Push(gtx.Ops) - menuCall.Add(gtx.Ops) - stack.Pop() - return rowDims + return row(gtx) } func (u *ui) syncMenuToggle(gtx layout.Context) layout.Dimensions { @@ -7233,23 +7258,7 @@ func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions { btn.Inset = layout.UniformInset(unit.Dp(8)) return btn.Layout(gtx) } - - buttonDims := button(gtx) - if !u.mainMenuOpen { - return buttonDims - } - - menuGTX := gtx - menuGTX.Constraints.Min = image.Point{} - menuOps := op.Record(gtx.Ops) - menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, u.mainMenu) - menuCall := menuOps.Stop() - - menuX := anchoredMenuX(buttonDims.Size.X, menuDims.Size.X) - stack := op.Offset(image.Pt(menuX, buttonDims.Size.Y)).Push(gtx.Ops) - menuCall.Add(gtx.Ops) - stack.Pop() - return buttonDims + return button(gtx) } func (u *ui) syncMenuDropsBelowTrigger() bool { @@ -7272,6 +7281,17 @@ func anchoredMenuX(triggerWidth, menuWidth int) int { return triggerWidth - menuWidth } +func anchoredMenuOriginX(containerWidth, rowOriginX, triggerRightX, menuWidth int) int { + x := rowOriginX + triggerRightX - menuWidth + if x < 0 { + return 0 + } + if x+menuWidth > containerWidth { + return max(0, containerWidth-menuWidth) + } + return x +} + func detailLine(th *material.Theme, label, value string) layout.Widget { return func(gtx layout.Context) layout.Dimensions { valueSize := unit.Sp(16) diff --git a/main_test.go b/main_test.go index 6678494..72ae6e3 100644 --- a/main_test.go +++ b/main_test.go @@ -367,6 +367,17 @@ func TestAnchoredMenuXAllowsWiderMenusToExtendLeft(t *testing.T) { } } +func TestAnchoredMenuOriginXClampsToVisibleContainer(t *testing.T) { + t.Parallel() + + if got := anchoredMenuOriginX(360, 312, 360, 140); got != 220 { + t.Fatalf("anchoredMenuOriginX should keep a right-aligned menu visible, got %d want 220", got) + } + if got := anchoredMenuOriginX(360, 0, 44, 160); got != 0 { + t.Fatalf("anchoredMenuOriginX should clamp oversized left overflow to zero, got %d want 0", got) + } +} + func TestUICurrentVaultSummary(t *testing.T) { t.Parallel()