177 lines
5.3 KiB
Go
177 lines
5.3 KiB
Go
package appui
|
|
|
|
import (
|
|
"image"
|
|
|
|
"gioui.org/layout"
|
|
"gioui.org/op"
|
|
"gioui.org/op/paint"
|
|
"gioui.org/unit"
|
|
"gioui.org/widget"
|
|
"gioui.org/widget/material"
|
|
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
|
)
|
|
|
|
func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
|
if u.usesCompactViewport() {
|
|
if u.shouldShowLifecycleSetup() || u.isVaultLocked() {
|
|
return layout.Dimensions{}
|
|
}
|
|
gtx.Constraints.Min.X = gtx.Constraints.Max.X
|
|
return u.headerActions(gtx)
|
|
}
|
|
if u.shouldShowDesktopWorkingHeader() {
|
|
return 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 {
|
|
return u.brandMark(gtx, 196, 56)
|
|
}),
|
|
layout.Rigid(u.headerActions),
|
|
)
|
|
})
|
|
}
|
|
|
|
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}
|
|
row := 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)
|
|
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 = row(gtx)
|
|
rowCall := rowOps.Stop()
|
|
|
|
if u.usesCompactViewport() {
|
|
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()
|
|
|
|
if u.usesCompactViewport() {
|
|
if u.syncMenuOpen {
|
|
u.phoneSyncMenuVisible = true
|
|
u.phoneSyncMenuAnchor = metrics.SyncAnchor().Point()
|
|
}
|
|
if u.mainMenuOpen {
|
|
u.phoneMainMenuVisible = true
|
|
u.phoneMainMenuAnchor = metrics.MainAnchor().Point()
|
|
}
|
|
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, metrics.RowDims.Size.Y)}
|
|
}
|
|
|
|
if u.syncMenuOpen {
|
|
surface.Draw(gtx, metrics.SyncAnchor(), u.syncMenu)
|
|
}
|
|
if u.mainMenuOpen {
|
|
surface.Draw(gtx, metrics.MainAnchor(), u.mainMenu)
|
|
}
|
|
|
|
return layout.Dimensions{Size: image.Pt(metrics.RowDims.Size.X, metrics.RowDims.Size.Y)}
|
|
}
|
|
|
|
func (u *ui) topRightActionOrder() []string {
|
|
if u.isVaultLocked() {
|
|
return nil
|
|
}
|
|
return []string{"Sync", "Lock", "Menu"}
|
|
}
|
|
|
|
func (u *ui) phoneHeaderMenus(gtx layout.Context) layout.Dimensions {
|
|
if !u.usesCompactViewport() || (!u.syncMenuVisibleOnPhone() && !u.mainMenuVisibleOnPhone()) {
|
|
return layout.Dimensions{}
|
|
}
|
|
gtx.Constraints.Min = gtx.Constraints.Max
|
|
contentInsetPx := gtx.Dp(unit.Dp(16))
|
|
surface := headerlayout.DropdownSurface{
|
|
ContainerWidth: max(0, gtx.Constraints.Max.X-(contentInsetPx*2)),
|
|
LeftInset: contentInsetPx,
|
|
TopInset: contentInsetPx,
|
|
}
|
|
|
|
if u.syncMenuVisibleOnPhone() {
|
|
surface.Draw(gtx, headerlayout.DropdownAnchor{TriggerRightX: u.phoneSyncMenuAnchor.X, TriggerBottomY: u.phoneSyncMenuAnchor.Y}, u.syncMenu)
|
|
}
|
|
if u.mainMenuVisibleOnPhone() {
|
|
surface.Draw(gtx, headerlayout.DropdownAnchor{TriggerRightX: u.phoneMainMenuAnchor.X, TriggerBottomY: u.phoneMainMenuAnchor.Y}, u.mainMenu)
|
|
}
|
|
return layout.Dimensions{Size: gtx.Constraints.Max}
|
|
}
|
|
|
|
func (u *ui) syncMenuVisibleOnPhone() bool {
|
|
return u.usesCompactViewport() && u.phoneSyncMenuVisible && u.syncMenuOpen
|
|
}
|
|
|
|
func (u *ui) mainMenuVisibleOnPhone() bool {
|
|
return u.usesCompactViewport() && u.phoneMainMenuVisible && u.mainMenuOpen
|
|
}
|
|
|
|
func (u *ui) syncMenuDropsBelowTrigger() bool { return true }
|
|
|
|
func (u *ui) syncMenuRightAlignsToTrigger() bool { return true }
|
|
|
|
func (u *ui) headerMenusUseOverlayModel() bool { return true }
|
|
|
|
func (u *ui) mainMenuDropsBelowTrigger() bool { return true }
|
|
|
|
func (u *ui) mainMenuRightAlignsToTrigger() bool { return true }
|
|
|
|
func (u *ui) lifecycleBranding(gtx layout.Context) layout.Dimensions {
|
|
if !u.usesCompactViewport() {
|
|
return layout.Dimensions{}
|
|
}
|
|
return layout.Dimensions{}
|
|
}
|
|
|
|
func (u *ui) brandMark(gtx layout.Context, widthDP, heightDP float32) layout.Dimensions {
|
|
if u.usesCompactViewport() {
|
|
return u.brandImage(gtx, u.splashSquare, widthDP, heightDP)
|
|
}
|
|
return u.brandImage(gtx, u.logoHorizontal, widthDP, heightDP)
|
|
}
|
|
|
|
func (u *ui) brandImage(gtx layout.Context, src paint.ImageOp, widthDP, heightDP float32) layout.Dimensions {
|
|
width := gtx.Dp(unit.Dp(widthDP))
|
|
height := gtx.Dp(unit.Dp(heightDP))
|
|
if width > gtx.Constraints.Max.X {
|
|
width = gtx.Constraints.Max.X
|
|
}
|
|
if height > gtx.Constraints.Max.Y && gtx.Constraints.Max.Y > 0 {
|
|
height = gtx.Constraints.Max.Y
|
|
}
|
|
img := widget.Image{
|
|
Src: src,
|
|
Fit: widget.Contain,
|
|
Position: layout.W,
|
|
Scale: 1.0 / gtx.Metric.PxPerDp,
|
|
}
|
|
gtx.Constraints.Min = image.Point{}
|
|
gtx.Constraints.Max = image.Pt(width, height)
|
|
return img.Layout(gtx)
|
|
}
|