Local-first remote sync and cross-platform UI parity #2
+54
-13
@@ -9,6 +9,7 @@ import (
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
headerview "git.julianfamily.org/keepassgo/internal/appui/header"
|
||||
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
||||
)
|
||||
|
||||
@@ -25,10 +26,10 @@ func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
return card(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return u.brandMark(gtx, 196, 56)
|
||||
}),
|
||||
layout.Rigid(u.headerActions),
|
||||
layout.Flexed(1, u.headerActions),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -39,7 +40,7 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
spacing := gtx.Dp(unit.Dp(8))
|
||||
metrics := headerlayout.ActionMetrics{Spacing: spacing}
|
||||
row := func(gtx layout.Context) layout.Dimensions {
|
||||
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 = u.syncButtonGroup(gtx)
|
||||
@@ -60,18 +61,16 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
rowOps := op.Record(gtx.Ops)
|
||||
metrics.RowDims = row(gtx)
|
||||
metrics.RowDims = actionCluster(gtx)
|
||||
rowCall := rowOps.Stop()
|
||||
|
||||
if u.usesCompactViewport() {
|
||||
metrics.RowOriginX = max(0, gtx.Constraints.Max.X-metrics.RowDims.Size.X)
|
||||
}
|
||||
metrics.RowOriginX = max(0, gtx.Constraints.Max.X-metrics.RowDims.Size.X)
|
||||
|
||||
surface := headerlayout.DropdownSurface{ContainerWidth: gtx.Constraints.Max.X, LeftInset: 0, TopInset: 0}
|
||||
|
||||
rowStack := op.Offset(image.Pt(metrics.RowOriginX, 0)).Push(gtx.Ops)
|
||||
rowCall.Add(gtx.Ops)
|
||||
rowStack.Pop()
|
||||
rowDims := layout.E.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
rowCall.Add(gtx.Ops)
|
||||
return metrics.RowDims
|
||||
})
|
||||
|
||||
if u.usesCompactViewport() {
|
||||
if u.syncMenuOpen {
|
||||
@@ -82,7 +81,7 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||
u.phoneMainMenuVisible = true
|
||||
u.phoneMainMenuAnchor = metrics.MainAnchor().Point()
|
||||
}
|
||||
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, metrics.RowDims.Size.Y)}
|
||||
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, rowDims.Size.Y)}
|
||||
}
|
||||
|
||||
if u.syncMenuOpen {
|
||||
@@ -92,7 +91,7 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||
surface.Draw(gtx, metrics.MainAnchor(), u.mainMenu)
|
||||
}
|
||||
|
||||
return layout.Dimensions{Size: image.Pt(metrics.RowDims.Size.X, metrics.RowDims.Size.Y)}
|
||||
return rowDims
|
||||
}
|
||||
|
||||
func (u *ui) topRightActionOrder() []string {
|
||||
@@ -174,3 +173,45 @@ func (u *ui) brandImage(gtx layout.Context, src paint.ImageOp, widthDP, heightDP
|
||||
gtx.Constraints.Max = image.Pt(width, height)
|
||||
return img.Layout(gtx)
|
||||
}
|
||||
|
||||
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
||||
rows := []layout.Widget{
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showEntries, "Entries")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showRecycle, "Recycle Bin")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showAPITokens, "API Tokens")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showAPIAudit, "API Audit")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.showAbout, "About") },
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.openSecuritySettings, "Settings")
|
||||
},
|
||||
}
|
||||
return headerview.MainMenu(gtx, u.theme, rows, compactCard)
|
||||
}
|
||||
|
||||
func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions {
|
||||
icon := u.menuIcon
|
||||
if icon == nil {
|
||||
icon = u.settingsIcon
|
||||
}
|
||||
return headerview.MainMenuButtonGroup(gtx, u.theme, &u.toggleMainMenu, icon, u.mainMenuOpen, selectedColor, accentColor)
|
||||
}
|
||||
|
||||
func intrinsicCompactCard(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||
return headerlayout.IntrinsicCompactCard(gtx, w, compactCard)
|
||||
}
|
||||
|
||||
func menuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||
return headerlayout.MenuActionWidth(gtx, rows)
|
||||
}
|
||||
|
||||
func rightAlignedMenuAction(gtx layout.Context, width int, child layout.Widget) layout.Dimensions {
|
||||
return headerlayout.RightAlignedAction(gtx, width, child)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package appui
|
||||
package layout
|
||||
|
||||
import (
|
||||
"image"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"gioui.org/unit"
|
||||
)
|
||||
|
||||
func intrinsicCompactCard(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||
func IntrinsicCompactCard(gtx layout.Context, w layout.Widget, card func(layout.Context, layout.Widget) layout.Dimensions) layout.Dimensions {
|
||||
measureGTX := gtx
|
||||
measureGTX.Constraints.Min = image.Point{}
|
||||
measureGTX.Constraints.Max.X = gtx.Constraints.Max.X
|
||||
@@ -24,10 +24,10 @@ func intrinsicCompactCard(gtx layout.Context, w layout.Widget) layout.Dimensions
|
||||
gtx.Constraints.Min.X = width
|
||||
gtx.Constraints.Max.X = width
|
||||
}
|
||||
return compactCard(gtx, w)
|
||||
return card(gtx, w)
|
||||
}
|
||||
|
||||
func menuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||
func MenuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||
width := 0
|
||||
for _, row := range rows {
|
||||
measureGTX := gtx
|
||||
@@ -42,7 +42,7 @@ func menuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||
return width
|
||||
}
|
||||
|
||||
func rightAlignedMenuAction(gtx layout.Context, width int, child layout.Widget) layout.Dimensions {
|
||||
func RightAlignedAction(gtx layout.Context, width int, child layout.Widget) layout.Dimensions {
|
||||
if width <= 0 {
|
||||
return child(gtx)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package header
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
||||
)
|
||||
|
||||
func MainMenu(gtx layout.Context, theme *material.Theme, rows []layout.Widget, card func(layout.Context, layout.Widget) layout.Dimensions) layout.Dimensions {
|
||||
rowWidth := headerlayout.MenuActionWidth(gtx, rows)
|
||||
return headerlayout.IntrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
children := make([]layout.FlexChild, 0, (len(rows)*2)-1)
|
||||
for i, row := range rows {
|
||||
if i > 0 {
|
||||
children = append(children, layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout))
|
||||
}
|
||||
current := row
|
||||
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return headerlayout.RightAlignedAction(gtx, rowWidth, current)
|
||||
}))
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
|
||||
}, card)
|
||||
}
|
||||
|
||||
func MainMenuButtonGroup(gtx layout.Context, theme *material.Theme, click *widget.Clickable, icon *widget.Icon, open bool, selectedColor, accentColor color.NRGBA) layout.Dimensions {
|
||||
btn := material.IconButton(theme, click, icon, "Menu")
|
||||
if open {
|
||||
btn.Background = accentColor
|
||||
btn.Color = color.NRGBA{R: 255, G: 252, B: 247, A: 255}
|
||||
} else {
|
||||
btn.Background = selectedColor
|
||||
btn.Color = accentColor
|
||||
}
|
||||
btn.Size = unit.Dp(18)
|
||||
btn.Inset = layout.UniformInset(unit.Dp(8))
|
||||
return btn.Layout(gtx)
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package appui
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget/material"
|
||||
)
|
||||
|
||||
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
||||
rows := []layout.Widget{
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showEntries, "Entries")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showRecycle, "Recycle Bin")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showAPITokens, "API Tokens")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.showAPIAudit, "API Audit")
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.showAbout, "About") },
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.openSecuritySettings, "Settings")
|
||||
},
|
||||
}
|
||||
rowWidth := menuActionWidth(gtx, rows)
|
||||
return intrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[0]) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[1]) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[2]) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[3]) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[4]) }),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return rightAlignedMenuAction(gtx, rowWidth, rows[5]) }),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions {
|
||||
icon := u.menuIcon
|
||||
if icon == nil {
|
||||
icon = u.settingsIcon
|
||||
}
|
||||
btn := material.IconButton(u.theme, &u.toggleMainMenu, icon, "Menu")
|
||||
if u.mainMenuOpen {
|
||||
btn.Background = accentColor
|
||||
btn.Color = color.NRGBA{R: 255, G: 252, B: 247, A: 255}
|
||||
} else {
|
||||
btn.Background = selectedColor
|
||||
btn.Color = accentColor
|
||||
}
|
||||
btn.Size = unit.Dp(18)
|
||||
btn.Inset = layout.UniformInset(unit.Dp(8))
|
||||
return btn.Layout(gtx)
|
||||
}
|
||||
Reference in New Issue
Block a user