Local-first remote sync and cross-platform UI parity #2
+65
-39
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user