Make header action cluster own menus

This commit is contained in:
Joe Julian
2026-04-10 15:48:57 -07:00
parent 44da1e6599
commit ac3478889c
2 changed files with 72 additions and 39 deletions
+65 -39
View File
@@ -39,68 +39,87 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
if u.shouldShowLifecycleSetup() || u.isVaultLocked() || u.shouldShowDesktopWorkingHeader() {
return layout.Dimensions{}
}
spacing := gtx.Dp(unit.Dp(8))
metrics := headerlayout.ActionMetrics{Spacing: spacing, SyncInnerSpacing: gtx.Dp(unit.Dp(3))}
if !u.usesCompactViewport() {
metrics.SyncInnerSpacing = gtx.Dp(unit.Dp(4))
}
actionCluster := func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
metrics.SyncDims, metrics.SyncPrimaryDims, metrics.SyncToggleDims = u.syncButtonGroupWithMetrics(gtx)
return metrics.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")
metrics.LockDims = btn.Layout(gtx)
return metrics.LockDims
}),
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
metrics.MainDims = u.mainMenuButtonGroup(gtx)
return metrics.MainDims
}),
)
}
rowOps := op.Record(gtx.Ops)
metrics.RowDims = actionCluster(gtx)
rowCall := rowOps.Stop()
metrics.RowOriginX = max(0, gtx.Constraints.Max.X-metrics.RowDims.Size.X)
cluster := u.newHeaderActionCluster(gtx)
surface := headerlayout.DropdownSurface{ContainerWidth: gtx.Constraints.Max.X, LeftInset: 0, TopInset: 0}
rowDims := layout.E.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
rowCall.Add(gtx.Ops)
return metrics.RowDims
cluster.RowCall.Add(gtx.Ops)
return cluster.Metrics.RowDims
})
if u.usesCompactViewport() {
u.maybeLogHeaderBounds(newHeaderButtonBounds(image.Pt(u.frameInsetPx, u.frameInsetPx), metrics.Bounds()))
u.maybeLogHeaderBounds(newHeaderButtonBounds(image.Pt(u.frameInsetPx, u.frameInsetPx), cluster.Metrics.Bounds()))
}
if u.usesCompactViewport() {
if u.syncMenuOpen {
u.phoneSyncMenuVisible = true
u.phoneSyncMenuAnchor = metrics.SyncAnchor().Point()
u.phoneSyncMenuAnchor = cluster.Metrics.SyncAnchor().Point()
}
if u.mainMenuOpen {
u.phoneMainMenuVisible = true
u.phoneMainMenuAnchor = metrics.MainAnchor().Point()
u.phoneMainMenuAnchor = cluster.Metrics.MainAnchor().Point()
}
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, rowDims.Size.Y)}
}
if u.syncMenuOpen {
surface.Draw(gtx, metrics.SyncAnchor(), u.syncMenu)
if cluster.ShowSyncMenu() {
surface.Draw(gtx, cluster.Metrics.SyncAnchor(), cluster.SyncMenu)
}
if u.mainMenuOpen {
surface.Draw(gtx, metrics.MainAnchor(), u.mainMenu)
if cluster.ShowMainMenu() {
surface.Draw(gtx, cluster.Metrics.MainAnchor(), cluster.MainMenu)
}
return rowDims
}
type headerActionCluster struct {
Metrics headerlayout.ActionMetrics
RowCall op.CallOp
SyncMenu layout.Widget
MainMenu layout.Widget
}
func (c headerActionCluster) ShowSyncMenu() bool { return c.SyncMenu != nil }
func (c headerActionCluster) ShowMainMenu() bool { return c.MainMenu != nil }
func (u *ui) newHeaderActionCluster(gtx layout.Context) headerActionCluster {
cluster := headerActionCluster{
SyncMenu: u.syncMenuWidget(),
MainMenu: u.mainMenuWidget(),
}
spacing := gtx.Dp(unit.Dp(8))
cluster.Metrics = headerlayout.ActionMetrics{Spacing: spacing, SyncInnerSpacing: gtx.Dp(unit.Dp(3))}
if !u.usesCompactViewport() {
cluster.Metrics.SyncInnerSpacing = gtx.Dp(unit.Dp(4))
}
rowOps := op.Record(gtx.Ops)
cluster.Metrics.RowDims = cluster.layoutRow(gtx, u)
cluster.RowCall = rowOps.Stop()
cluster.Metrics.RowOriginX = max(0, gtx.Constraints.Max.X-cluster.Metrics.RowDims.Size.X)
return cluster
}
func (c *headerActionCluster) layoutRow(gtx layout.Context, u *ui) layout.Dimensions {
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
c.Metrics.SyncDims, c.Metrics.SyncPrimaryDims, c.Metrics.SyncToggleDims = u.syncButtonGroupWithMetrics(gtx)
return c.Metrics.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")
c.Metrics.LockDims = btn.Layout(gtx)
return c.Metrics.LockDims
}),
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
c.Metrics.MainDims = u.mainMenuButtonGroup(gtx)
return c.Metrics.MainDims
}),
)
}
type headerButtonBounds struct {
SyncPrimary image.Rectangle
SyncToggle image.Rectangle
@@ -212,6 +231,13 @@ func (u *ui) brandImage(gtx layout.Context, src paint.ImageOp, widthDP, heightDP
return img.Layout(gtx)
}
func (u *ui) mainMenuWidget() layout.Widget {
if !u.mainMenuOpen {
return nil
}
return u.mainMenu
}
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
rows := []layout.Widget{
func(gtx layout.Context) layout.Dimensions {
+7
View File
@@ -58,6 +58,13 @@ func (u *ui) syncMenuToggle(gtx layout.Context) layout.Dimensions {
return btn.Layout(gtx)
}
func (u *ui) syncMenuWidget() layout.Widget {
if !u.syncMenuOpen {
return nil
}
return u.syncMenu
}
func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
model := u.buildSyncMenuModel()
profiles := u.availableRemoteProfiles()