diff --git a/internal/appui/header.go b/internal/appui/header.go index d937235..3b81a32 100644 --- a/internal/appui/header.go +++ b/internal/appui/header.go @@ -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) +} diff --git a/internal/appui/header_menu_layout.go b/internal/appui/header/layout/menu.go similarity index 72% rename from internal/appui/header_menu_layout.go rename to internal/appui/header/layout/menu.go index 458bc90..baaee43 100644 --- a/internal/appui/header_menu_layout.go +++ b/internal/appui/header/layout/menu.go @@ -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) } diff --git a/internal/appui/header/menu.go b/internal/appui/header/menu.go new file mode 100644 index 0000000..2716dbb --- /dev/null +++ b/internal/appui/header/menu.go @@ -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) +} diff --git a/internal/appui/header_main_menu.go b/internal/appui/header_main_menu.go deleted file mode 100644 index d7fdbd2..0000000 --- a/internal/appui/header_main_menu.go +++ /dev/null @@ -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) -}