Compare commits
6 Commits
54f13d352c
...
0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| a9c15c2d23 | |||
| b7d6dbdc97 | |||
| 2deca549f5 | |||
| fe3c07e3dd | |||
| c4f110e0ad | |||
| 56a0711860 |
+15
-35
@@ -489,10 +489,10 @@ type ui struct {
|
|||||||
deleteGroupPath []string
|
deleteGroupPath []string
|
||||||
apiPolicyGroupScope bool
|
apiPolicyGroupScope bool
|
||||||
apiTokenSecret string
|
apiTokenSecret string
|
||||||
phoneSyncMenuAnchor image.Point
|
|
||||||
phoneMainMenuAnchor image.Point
|
|
||||||
phoneSyncMenuOrigin image.Point
|
phoneSyncMenuOrigin image.Point
|
||||||
phoneMainMenuOrigin image.Point
|
phoneMainMenuOrigin image.Point
|
||||||
|
phoneSyncMenuSize image.Point
|
||||||
|
phoneMainMenuSize image.Point
|
||||||
phoneSyncMenuCall op.CallOp
|
phoneSyncMenuCall op.CallOp
|
||||||
phoneMainMenuCall op.CallOp
|
phoneMainMenuCall op.CallOp
|
||||||
phoneSyncMenuVisible bool
|
phoneSyncMenuVisible bool
|
||||||
@@ -1850,8 +1850,17 @@ func (u *ui) navigationHeaderLabel() string {
|
|||||||
|
|
||||||
func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item entry) layout.Dimensions {
|
func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item entry) layout.Dimensions {
|
||||||
for click.Clicked(gtx) {
|
for click.Clicked(gtx) {
|
||||||
_ = u.state.ToggleVisibleIndex(idx)
|
if !u.shouldShowDetailPane() {
|
||||||
|
if idx >= 0 && idx < len(u.visible) {
|
||||||
|
u.state.SelectedEntryID = u.visible[idx].ID
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ = u.state.ToggleVisibleIndex(idx)
|
||||||
|
}
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
|
if u.invalidate != nil {
|
||||||
|
u.invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return click.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
return click.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
selected := item.ID == u.state.SelectedEntryID
|
selected := item.ID == u.state.SelectedEntryID
|
||||||
@@ -2014,21 +2023,8 @@ func (u *ui) detailPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
return panel(gtx, func(gtx layout.Context) layout.Dimensions {
|
return panel(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
if u.shouldShowDesktopWorkingHeader() {
|
if u.shouldShowDesktopWorkingHeader() {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(u.headerActions),
|
||||||
return layout.Flex{Alignment: layout.Middle, Spacing: layout.SpaceStart}.Layout(gtx,
|
layout.Rigid(u.desktopHeaderMenus),
|
||||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
|
||||||
return layout.Dimensions{}
|
|
||||||
}),
|
|
||||||
layout.Rigid(u.syncButtonGroup),
|
|
||||||
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")
|
|
||||||
return btn.Layout(gtx)
|
|
||||||
}),
|
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
|
||||||
layout.Rigid(u.mainMenuButtonGroup),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
||||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.detailPanelContent(gtx)
|
return u.detailPanelContent(gtx)
|
||||||
@@ -2049,7 +2045,7 @@ func (u *ui) detailPanelContent(gtx layout.Context) layout.Dimensions {
|
|||||||
panel := u.staticDetailPanel()
|
panel := u.staticDetailPanel()
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, panel...)
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, panel...)
|
||||||
case detaillayout.ModeEmpty:
|
case detaillayout.ModeEmpty:
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, u.emptyDetailChildren()...)
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
item, ok := u.selectedEntry()
|
item, ok := u.selectedEntry()
|
||||||
if mode == detaillayout.ModeEditor {
|
if mode == detaillayout.ModeEditor {
|
||||||
@@ -2089,22 +2085,6 @@ func (u *ui) staticDetailPanel() []layout.FlexChild {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) emptyDetailChildren() []layout.FlexChild {
|
|
||||||
return []layout.FlexChild{
|
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|
||||||
lbl := material.Label(u.theme, unit.Sp(18), "Entry details")
|
|
||||||
lbl.Color = accentColor
|
|
||||||
return lbl.Layout(gtx)
|
|
||||||
}),
|
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|
||||||
lbl := material.Label(u.theme, unit.Sp(16), u.detailPlaceholderMessage())
|
|
||||||
lbl.Color = mutedColor
|
|
||||||
return lbl.Layout(gtx)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ui) detailEditorContent(gtx layout.Context, hasSelected bool) layout.Dimensions {
|
func (u *ui) detailEditorContent(gtx layout.Context, hasSelected bool) layout.Dimensions {
|
||||||
rows := []layout.Widget{
|
rows := []layout.Widget{
|
||||||
func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
|
|||||||
+20
-3
@@ -20,6 +20,7 @@ import (
|
|||||||
"git.julianfamily.org/keepassgo/internal/apiaudit"
|
"git.julianfamily.org/keepassgo/internal/apiaudit"
|
||||||
"git.julianfamily.org/keepassgo/internal/apitokens"
|
"git.julianfamily.org/keepassgo/internal/apitokens"
|
||||||
"git.julianfamily.org/keepassgo/internal/appstate"
|
"git.julianfamily.org/keepassgo/internal/appstate"
|
||||||
|
detaillayout "git.julianfamily.org/keepassgo/internal/appui/detail/layout"
|
||||||
"git.julianfamily.org/keepassgo/internal/clipboard"
|
"git.julianfamily.org/keepassgo/internal/clipboard"
|
||||||
"git.julianfamily.org/keepassgo/internal/session"
|
"git.julianfamily.org/keepassgo/internal/session"
|
||||||
)
|
)
|
||||||
@@ -593,6 +594,8 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
u.phoneMainMenuVisible = false
|
u.phoneMainMenuVisible = false
|
||||||
u.phoneSyncMenuOrigin = image.Point{}
|
u.phoneSyncMenuOrigin = image.Point{}
|
||||||
u.phoneMainMenuOrigin = image.Point{}
|
u.phoneMainMenuOrigin = image.Point{}
|
||||||
|
u.phoneSyncMenuSize = image.Point{}
|
||||||
|
u.phoneMainMenuSize = image.Point{}
|
||||||
u.phoneSyncMenuCall = op.CallOp{}
|
u.phoneSyncMenuCall = op.CallOp{}
|
||||||
u.phoneMainMenuCall = op.CallOp{}
|
u.phoneMainMenuCall = op.CallOp{}
|
||||||
u.syncHostedAPI()
|
u.syncHostedAPI()
|
||||||
@@ -620,9 +623,7 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
layout.Stacked(u.securityDialogOverlay),
|
layout.Stacked(u.securityDialogOverlay),
|
||||||
layout.Stacked(u.remotePrefsDialogOverlay),
|
layout.Stacked(u.remotePrefsDialogOverlay),
|
||||||
layout.Stacked(u.approvalDialogOverlay),
|
layout.Stacked(u.approvalDialogOverlay),
|
||||||
layout.Expanded(func(gtx layout.Context) layout.Dimensions {
|
layout.Expanded(u.phoneHeaderMenus),
|
||||||
return u.phoneHeaderMenus(gtx)
|
|
||||||
}),
|
|
||||||
layout.Stacked(u.statusToast),
|
layout.Stacked(u.statusToast),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1310,6 +1311,11 @@ func (u *ui) primaryContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return u.lifecycleScreen(gtx)
|
return u.lifecycleScreen(gtx)
|
||||||
case u.shouldUseLockedSinglePane():
|
case u.shouldUseLockedSinglePane():
|
||||||
return u.detailPanel(gtx)
|
return u.detailPanel(gtx)
|
||||||
|
case !u.shouldShowDetailPane():
|
||||||
|
if u.usesCompactViewport() {
|
||||||
|
return u.listPanel(gtx)
|
||||||
|
}
|
||||||
|
return u.widePrimaryContent(gtx)
|
||||||
case u.usesCompactViewport():
|
case u.usesCompactViewport():
|
||||||
return u.compactPrimaryContent(gtx)
|
return u.compactPrimaryContent(gtx)
|
||||||
default:
|
default:
|
||||||
@@ -1317,6 +1323,17 @@ func (u *ui) primaryContent(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) shouldShowDetailPane() bool {
|
||||||
|
_, hasSelectedEntry := u.selectedEntry()
|
||||||
|
mode := detaillayout.Resolve(
|
||||||
|
u.isVaultLocked(),
|
||||||
|
u.state.Section == appstate.SectionAPITokens || u.state.Section == appstate.SectionAPIAudit || u.state.Section == appstate.SectionAbout,
|
||||||
|
hasSelectedEntry,
|
||||||
|
u.editingEntry,
|
||||||
|
)
|
||||||
|
return mode != detaillayout.ModeEmpty
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) compactPrimaryContent(gtx layout.Context) layout.Dimensions {
|
func (u *ui) compactPrimaryContent(gtx layout.Context) layout.Dimensions {
|
||||||
u.phoneSpan = gtx.Constraints.Max.Y
|
u.phoneSpan = gtx.Constraints.Max.Y
|
||||||
listHeight := int(float32(gtx.Constraints.Max.Y) * u.phoneSplit.Value)
|
listHeight := int(float32(gtx.Constraints.Max.Y) * u.phoneSplit.Value)
|
||||||
|
|||||||
+82
-65
@@ -12,7 +12,6 @@ import (
|
|||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
headerview "git.julianfamily.org/keepassgo/internal/appui/header"
|
headerview "git.julianfamily.org/keepassgo/internal/appui/header"
|
||||||
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
||||||
"git.julianfamily.org/keepassgo/internal/appui/platform"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -37,58 +36,19 @@ func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
||||||
if u.shouldShowLifecycleSetup() || u.isVaultLocked() || u.shouldShowDesktopWorkingHeader() {
|
if u.shouldShowLifecycleSetup() || u.isVaultLocked() {
|
||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
cluster := u.newHeaderActionCluster(gtx)
|
cluster := u.newHeaderActionCluster(gtx)
|
||||||
surface := headerlayout.DropdownSurface{ContainerWidth: gtx.Constraints.Max.X, LeftInset: 0, TopInset: 0}
|
|
||||||
rowDims := cluster.layout(gtx, u)
|
rowDims := cluster.layout(gtx, u)
|
||||||
if u.usesCompactViewport() {
|
if u.usesCompactViewport() {
|
||||||
u.maybeLogHeaderBounds(newHeaderButtonBounds(image.Pt(u.frameInsetPx, u.frameInsetPx), cluster.Metrics.Bounds()))
|
u.maybeLogHeaderBounds(newHeaderButtonBounds(image.Pt(u.frameInsetPx, u.frameInsetPx), cluster.Metrics.Bounds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.usesCompactViewport() {
|
if u.usesCompactViewport() {
|
||||||
compactSurface := headerlayout.DropdownSurface{
|
cluster.prepareCompactMenus(gtx, u)
|
||||||
ContainerWidth: gtx.Constraints.Max.X,
|
|
||||||
LeftInset: u.frameInsetPx,
|
|
||||||
TopInset: u.frameInsetPx,
|
|
||||||
}
|
|
||||||
if u.syncMenuOpen {
|
|
||||||
u.phoneSyncMenuVisible = true
|
|
||||||
u.phoneSyncMenuAnchor = cluster.Metrics.SyncAnchor().Point()
|
|
||||||
u.maybeLogHeaderMenuToggle("sync-visible", true)
|
|
||||||
placement, menuCall := compactSurface.Place(gtx, cluster.Metrics.SyncAnchor(), u.syncMenu)
|
|
||||||
u.phoneSyncMenuOrigin = placement.Origin
|
|
||||||
u.phoneSyncMenuCall = menuCall
|
|
||||||
u.maybeLogHeaderMenuPlacement("sync-phone", compactSurface, placement)
|
|
||||||
}
|
|
||||||
if u.mainMenuOpen {
|
|
||||||
u.phoneMainMenuVisible = true
|
|
||||||
u.phoneMainMenuAnchor = cluster.Metrics.MainAnchor().Point()
|
|
||||||
u.maybeLogHeaderMenuToggle("main-visible", true)
|
|
||||||
placement, menuCall := compactSurface.Place(gtx, cluster.Metrics.MainAnchor(), u.mainMenu)
|
|
||||||
u.phoneMainMenuOrigin = placement.Origin
|
|
||||||
u.phoneMainMenuCall = menuCall
|
|
||||||
u.maybeLogHeaderMenuPlacement("main-phone", compactSurface, placement)
|
|
||||||
}
|
|
||||||
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, rowDims.Size.Y)}
|
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, rowDims.Size.Y)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.ShowSyncMenu() {
|
|
||||||
placement, menuCall := surface.Place(gtx, cluster.Metrics.SyncAnchor(), cluster.SyncMenu)
|
|
||||||
u.maybeLogHeaderMenuPlacement("sync", surface, placement)
|
|
||||||
stack := op.Offset(placement.Origin).Push(gtx.Ops)
|
|
||||||
menuCall.Add(gtx.Ops)
|
|
||||||
stack.Pop()
|
|
||||||
}
|
|
||||||
if cluster.ShowMainMenu() {
|
|
||||||
placement, menuCall := surface.Place(gtx, cluster.Metrics.MainAnchor(), cluster.MainMenu)
|
|
||||||
u.maybeLogHeaderMenuPlacement("main", surface, placement)
|
|
||||||
stack := op.Offset(placement.Origin).Push(gtx.Ops)
|
|
||||||
menuCall.Add(gtx.Ops)
|
|
||||||
stack.Pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
return rowDims
|
return rowDims
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +86,17 @@ func (c *headerActionCluster) layout(gtx layout.Context, u *ui) layout.Dimension
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c headerActionCluster) activeMenu() layout.Widget {
|
||||||
|
switch {
|
||||||
|
case c.ShowSyncMenu():
|
||||||
|
return c.SyncMenu
|
||||||
|
case c.ShowMainMenu():
|
||||||
|
return c.MainMenu
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *headerActionCluster) layoutRow(gtx layout.Context, u *ui) layout.Dimensions {
|
func (c *headerActionCluster) layoutRow(gtx layout.Context, u *ui) layout.Dimensions {
|
||||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -146,6 +117,44 @@ func (c *headerActionCluster) layoutRow(gtx layout.Context, u *ui) layout.Dimens
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *headerActionCluster) prepareCompactMenus(gtx layout.Context, u *ui) {
|
||||||
|
compactSurface := headerlayout.DropdownSurface{
|
||||||
|
ContainerWidth: gtx.Constraints.Max.X,
|
||||||
|
LeftInset: u.frameInsetPx,
|
||||||
|
TopInset: u.frameInsetPx,
|
||||||
|
}
|
||||||
|
if c.ShowSyncMenu() {
|
||||||
|
u.phoneSyncMenuVisible = true
|
||||||
|
u.maybeLogHeaderMenuToggle("sync-visible", true)
|
||||||
|
placement, menuCall := compactSurface.Place(gtx, c.Metrics.SyncAnchor(), c.SyncMenu)
|
||||||
|
u.phoneSyncMenuOrigin = placement.Origin
|
||||||
|
u.phoneSyncMenuSize = placement.Size
|
||||||
|
u.phoneSyncMenuCall = menuCall
|
||||||
|
u.maybeLogHeaderMenuPlacement("sync-phone", compactSurface, placement)
|
||||||
|
}
|
||||||
|
if c.ShowMainMenu() {
|
||||||
|
u.phoneMainMenuVisible = true
|
||||||
|
u.maybeLogHeaderMenuToggle("main-visible", true)
|
||||||
|
placement, menuCall := compactSurface.Place(gtx, c.Metrics.MainAnchor(), c.MainMenu)
|
||||||
|
u.phoneMainMenuOrigin = placement.Origin
|
||||||
|
u.phoneMainMenuSize = placement.Size
|
||||||
|
u.phoneMainMenuCall = menuCall
|
||||||
|
u.maybeLogHeaderMenuPlacement("main-phone", compactSurface, placement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c headerActionCluster) layoutMenuRow(gtx layout.Context) layout.Dimensions {
|
||||||
|
menu := c.activeMenu()
|
||||||
|
if menu == nil {
|
||||||
|
return layout.Dimensions{}
|
||||||
|
}
|
||||||
|
fullWidthGTX := gtx
|
||||||
|
fullWidthGTX.Constraints.Min = image.Point{}
|
||||||
|
fullWidthGTX.Constraints.Min.X = fullWidthGTX.Constraints.Max.X
|
||||||
|
dims := layout.E.Layout(fullWidthGTX, menu)
|
||||||
|
return layout.Dimensions{Size: image.Pt(fullWidthGTX.Constraints.Max.X, dims.Size.Y)}
|
||||||
|
}
|
||||||
|
|
||||||
type headerButtonBounds struct {
|
type headerButtonBounds struct {
|
||||||
SyncPrimary image.Rectangle
|
SyncPrimary image.Rectangle
|
||||||
SyncToggle image.Rectangle
|
SyncToggle image.Rectangle
|
||||||
@@ -185,35 +194,43 @@ func (u *ui) topRightActionOrder() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) phoneHeaderMenus(gtx layout.Context) layout.Dimensions {
|
func (u *ui) phoneHeaderMenus(gtx layout.Context) layout.Dimensions {
|
||||||
if u.debugLogHeaderBounds {
|
|
||||||
platform.LogInfo("KeePassGO", fmt.Sprintf(
|
|
||||||
"keepassgo phone-header-menus compact=%t syncVisible=%t syncOpen=%t mainVisible=%t mainOpen=%t syncCall=%t mainCall=%t max=%dx%d",
|
|
||||||
u.usesCompactViewport(),
|
|
||||||
u.phoneSyncMenuVisible,
|
|
||||||
u.syncMenuOpen,
|
|
||||||
u.phoneMainMenuVisible,
|
|
||||||
u.mainMenuOpen,
|
|
||||||
u.phoneSyncMenuCall != (op.CallOp{}),
|
|
||||||
u.phoneMainMenuCall != (op.CallOp{}),
|
|
||||||
gtx.Constraints.Max.X,
|
|
||||||
gtx.Constraints.Max.Y,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
if !u.usesCompactViewport() || (!u.syncMenuVisibleOnPhone() && !u.mainMenuVisibleOnPhone()) {
|
if !u.usesCompactViewport() || (!u.syncMenuVisibleOnPhone() && !u.mainMenuVisibleOnPhone()) {
|
||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cluster := u.newHeaderActionCluster(gtx)
|
||||||
if u.syncMenuVisibleOnPhone() {
|
if u.syncMenuVisibleOnPhone() {
|
||||||
stack := op.Offset(u.phoneSyncMenuOrigin).Push(gtx.Ops)
|
return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
u.phoneSyncMenuCall.Add(gtx.Ops)
|
stack := op.Offset(image.Pt(0, max(0, u.phoneSyncMenuOrigin.Y-u.frameInsetPx))).Push(gtx.Ops)
|
||||||
stack.Pop()
|
defer stack.Pop()
|
||||||
|
dims := cluster.layoutMenuRow(gtx)
|
||||||
|
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, max(dims.Size.Y, u.phoneSyncMenuOrigin.Y))}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if u.mainMenuVisibleOnPhone() {
|
if u.mainMenuVisibleOnPhone() {
|
||||||
stack := op.Offset(u.phoneMainMenuOrigin).Push(gtx.Ops)
|
return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
u.phoneMainMenuCall.Add(gtx.Ops)
|
stack := op.Offset(image.Pt(0, max(0, u.phoneMainMenuOrigin.Y-u.frameInsetPx))).Push(gtx.Ops)
|
||||||
stack.Pop()
|
defer stack.Pop()
|
||||||
|
dims := cluster.layoutMenuRow(gtx)
|
||||||
|
return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, max(dims.Size.Y, u.phoneMainMenuOrigin.Y))}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
return layout.Dimensions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) desktopHeaderMenus(gtx layout.Context) layout.Dimensions {
|
||||||
|
if u.usesCompactViewport() || (!u.syncMenuOpen && !u.mainMenuOpen) {
|
||||||
|
return layout.Dimensions{}
|
||||||
|
}
|
||||||
|
cluster := u.newHeaderActionCluster(gtx)
|
||||||
|
dims := cluster.layoutMenuRow(gtx)
|
||||||
|
if dims.Size.Y == 0 {
|
||||||
|
return dims
|
||||||
|
}
|
||||||
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return dims }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) syncMenuVisibleOnPhone() bool {
|
func (u *ui) syncMenuVisibleOnPhone() bool {
|
||||||
@@ -294,7 +311,7 @@ func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
|||||||
return tonedButton(gtx, u.theme, &u.openSecuritySettings, "Settings")
|
return tonedButton(gtx, u.theme, &u.openSecuritySettings, "Settings")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return headerview.MainMenu(gtx, u.theme, rows, compactCard)
|
return headerview.MainMenu(gtx, u.theme, rows, compactCard, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions {
|
func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -306,7 +323,7 @@ func (u *ui) mainMenuButtonGroup(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func intrinsicCompactCard(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
func intrinsicCompactCard(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||||
return headerlayout.IntrinsicCompactCard(gtx, w, compactCard)
|
return headerlayout.IntrinsicCompactCard(gtx, w, compactCard, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func menuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
func menuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ import (
|
|||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IntrinsicCompactCard(gtx layout.Context, w layout.Widget, card func(layout.Context, layout.Widget) layout.Dimensions) layout.Dimensions {
|
func IntrinsicCompactCard(gtx layout.Context, w layout.Widget, card func(layout.Context, layout.Widget) layout.Dimensions, logger func(name string, constraints layout.Constraints, dims layout.Dimensions)) layout.Dimensions {
|
||||||
measureGTX := gtx
|
measureGTX := gtx
|
||||||
measureGTX.Constraints.Min = image.Point{}
|
measureGTX.Constraints.Min = image.Point{}
|
||||||
measureGTX.Constraints.Max.X = gtx.Constraints.Max.X
|
measureGTX.Constraints.Max.X = gtx.Constraints.Max.X
|
||||||
macro := op.Record(gtx.Ops)
|
macro := op.Record(gtx.Ops)
|
||||||
contentDims := w(measureGTX)
|
contentDims := w(measureGTX)
|
||||||
_ = macro.Stop()
|
_ = macro.Stop()
|
||||||
|
if logger != nil {
|
||||||
|
logger("intrinsic-measure", measureGTX.Constraints, contentDims)
|
||||||
|
}
|
||||||
width := contentDims.Size.X + gtx.Dp(unit.Dp(20))
|
width := contentDims.Size.X + gtx.Dp(unit.Dp(20))
|
||||||
maxWidth := gtx.Constraints.Max.X
|
maxWidth := gtx.Constraints.Max.X
|
||||||
if maxWidth > 0 && width > maxWidth {
|
if maxWidth > 0 && width > maxWidth {
|
||||||
@@ -24,7 +27,11 @@ func IntrinsicCompactCard(gtx layout.Context, w layout.Widget, card func(layout.
|
|||||||
gtx.Constraints.Min.X = width
|
gtx.Constraints.Min.X = width
|
||||||
gtx.Constraints.Max.X = width
|
gtx.Constraints.Max.X = width
|
||||||
}
|
}
|
||||||
return card(gtx, w)
|
dims := card(gtx, w)
|
||||||
|
if logger != nil {
|
||||||
|
logger("intrinsic-card", gtx.Constraints, dims)
|
||||||
|
}
|
||||||
|
return dims
|
||||||
}
|
}
|
||||||
|
|
||||||
func MenuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
func MenuActionWidth(gtx layout.Context, rows []layout.Widget) int {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package header
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image"
|
||||||
|
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
@@ -10,9 +11,12 @@ import (
|
|||||||
headerlayout "git.julianfamily.org/keepassgo/internal/appui/header/layout"
|
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 {
|
func MainMenu(gtx layout.Context, theme *material.Theme, rows []layout.Widget, card func(layout.Context, layout.Widget) layout.Dimensions, logger func(name string, constraints layout.Constraints, dims layout.Dimensions)) layout.Dimensions {
|
||||||
rowWidth := headerlayout.MenuActionWidth(gtx, rows)
|
rowWidth := headerlayout.MenuActionWidth(gtx, rows)
|
||||||
return headerlayout.IntrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
if logger != nil {
|
||||||
|
logger("row-width", gtx.Constraints, layout.Dimensions{Size: image.Pt(rowWidth, 0)})
|
||||||
|
}
|
||||||
|
dims := headerlayout.IntrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
children := make([]layout.FlexChild, 0, (len(rows)*2)-1)
|
children := make([]layout.FlexChild, 0, (len(rows)*2)-1)
|
||||||
for i, row := range rows {
|
for i, row := range rows {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@@ -23,8 +27,16 @@ func MainMenu(gtx layout.Context, theme *material.Theme, rows []layout.Widget, c
|
|||||||
return headerlayout.RightAlignedAction(gtx, rowWidth, current)
|
return headerlayout.RightAlignedAction(gtx, rowWidth, current)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
|
dims := layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...)
|
||||||
}, card)
|
if logger != nil {
|
||||||
|
logger("rows", gtx.Constraints, dims)
|
||||||
|
}
|
||||||
|
return dims
|
||||||
|
}, card, logger)
|
||||||
|
if logger != nil {
|
||||||
|
logger("card", gtx.Constraints, dims)
|
||||||
|
}
|
||||||
|
return dims
|
||||||
}
|
}
|
||||||
|
|
||||||
func MainMenuButtonGroup(gtx layout.Context, theme *material.Theme, click *widget.Clickable, icon *widget.Icon, open bool, selectedColor, accentColor color.NRGBA) layout.Dimensions {
|
func MainMenuButtonGroup(gtx layout.Context, theme *material.Theme, click *widget.Clickable, icon *widget.Icon, open bool, selectedColor, accentColor color.NRGBA) layout.Dimensions {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package appui
|
package appui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
|
"gioui.org/op"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
@@ -14,11 +16,6 @@ import (
|
|||||||
"git.julianfamily.org/keepassgo/internal/vault"
|
"git.julianfamily.org/keepassgo/internal/vault"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (u *ui) syncButtonGroup(gtx layout.Context) layout.Dimensions {
|
|
||||||
group, _, _ := u.syncButtonGroupWithMetrics(gtx)
|
|
||||||
return group
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ui) syncButtonGroupWithMetrics(gtx layout.Context) (layout.Dimensions, layout.Dimensions, layout.Dimensions) {
|
func (u *ui) syncButtonGroupWithMetrics(gtx layout.Context) (layout.Dimensions, layout.Dimensions, layout.Dimensions) {
|
||||||
spacing := unit.Dp(4)
|
spacing := unit.Dp(4)
|
||||||
if u.usesCompactViewport() {
|
if u.usesCompactViewport() {
|
||||||
@@ -77,9 +74,44 @@ func (u *ui) syncMenu(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
actionRows := u.syncMenuActionRows(model)
|
actionRows := u.syncMenuActionRows(model)
|
||||||
actionWidth := menuActionWidth(gtx, actionRows)
|
actionWidth := menuActionWidth(gtx, actionRows)
|
||||||
return intrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
menu := func(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, u.syncMenuRows(model, profiles, credentials, actionWidth)...)
|
return intrinsicCompactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
})
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, u.syncMenuRows(model, profiles, credentials, actionWidth)...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
reserveWidth := u.syncMenuTrailingReserveWidth(gtx)
|
||||||
|
if reserveWidth <= 0 {
|
||||||
|
return menu(gtx)
|
||||||
|
}
|
||||||
|
return layout.Flex{}.Layout(gtx,
|
||||||
|
layout.Rigid(menu),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return layout.Dimensions{Size: image.Pt(reserveWidth, 0)}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) syncMenuTrailingReserveWidth(gtx layout.Context) int {
|
||||||
|
spacing := gtx.Dp(unit.Dp(8))
|
||||||
|
if u.usesCompactViewport() {
|
||||||
|
spacing = gtx.Dp(unit.Dp(8))
|
||||||
|
}
|
||||||
|
|
||||||
|
measureGTX := gtx
|
||||||
|
measureGTX.Constraints.Min = image.Point{}
|
||||||
|
|
||||||
|
lockOps := op.Record(gtx.Ops)
|
||||||
|
lockDims := func(gtx layout.Context) layout.Dimensions {
|
||||||
|
btn := material.Button(u.theme, &u.lockVault, "Lock")
|
||||||
|
return btn.Layout(gtx)
|
||||||
|
}(measureGTX)
|
||||||
|
_ = lockOps.Stop()
|
||||||
|
|
||||||
|
menuOps := op.Record(gtx.Ops)
|
||||||
|
menuDims := u.mainMenuButtonGroup(measureGTX)
|
||||||
|
_ = menuOps.Stop()
|
||||||
|
|
||||||
|
return spacing + lockDims.Size.X + spacing + menuDims.Size.X
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) syncMenuActionRows(model syncmodel.MenuModel) []layout.Widget {
|
func (u *ui) syncMenuActionRows(model syncmodel.MenuModel) []layout.Widget {
|
||||||
@@ -126,14 +158,7 @@ func (u *ui) syncMenuRows(model syncmodel.MenuModel, profiles []vault.RemoteProf
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) syncMenuPrimaryRows(model syncmodel.MenuModel, actionWidth int) []layout.FlexChild {
|
func (u *ui) syncMenuPrimaryRows(model syncmodel.MenuModel, actionWidth int) []layout.FlexChild {
|
||||||
rows := []layout.FlexChild{
|
rows := []layout.FlexChild{}
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|
||||||
lbl := material.Label(u.theme, unit.Sp(11), "Need another source or direction?")
|
|
||||||
lbl.Color = mutedColor
|
|
||||||
return lbl.Layout(gtx)
|
|
||||||
}),
|
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
|
||||||
}
|
|
||||||
if model.ShowShare {
|
if model.ShowShare {
|
||||||
rows = append(rows, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
rows = append(rows, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
|
|||||||
Reference in New Issue
Block a user