Simplify phone vault screen and settings navigation
This commit is contained in:
@@ -246,6 +246,8 @@ type ui struct {
|
|||||||
apiPolicyList widget.List
|
apiPolicyList widget.List
|
||||||
lifecycleList widget.List
|
lifecycleList widget.List
|
||||||
phonePanelList widget.List
|
phonePanelList widget.List
|
||||||
|
securityDialogList widget.List
|
||||||
|
remotePrefsDialogList widget.List
|
||||||
recentVaultListState widget.List
|
recentVaultListState widget.List
|
||||||
recentRemoteListState widget.List
|
recentRemoteListState widget.List
|
||||||
copyUser widget.Clickable
|
copyUser widget.Clickable
|
||||||
@@ -261,6 +263,7 @@ type ui struct {
|
|||||||
changeMasterKey widget.Clickable
|
changeMasterKey widget.Clickable
|
||||||
synchronizeVault widget.Clickable
|
synchronizeVault widget.Clickable
|
||||||
toggleSyncMenu widget.Clickable
|
toggleSyncMenu widget.Clickable
|
||||||
|
toggleMainMenu widget.Clickable
|
||||||
openAdvancedSync widget.Clickable
|
openAdvancedSync widget.Clickable
|
||||||
openSecuritySettings widget.Clickable
|
openSecuritySettings widget.Clickable
|
||||||
openRemotePrefsHelp widget.Clickable
|
openRemotePrefsHelp widget.Clickable
|
||||||
@@ -396,6 +399,7 @@ type ui struct {
|
|||||||
expandLessIcon *widget.Icon
|
expandLessIcon *widget.Icon
|
||||||
chevronDownIcon *widget.Icon
|
chevronDownIcon *widget.Icon
|
||||||
settingsIcon *widget.Icon
|
settingsIcon *widget.Icon
|
||||||
|
menuIcon *widget.Icon
|
||||||
clipboardWriter clipboard.Writer
|
clipboardWriter clipboard.Writer
|
||||||
loadingMessage string
|
loadingMessage string
|
||||||
loadingActionLabel string
|
loadingActionLabel string
|
||||||
@@ -409,6 +413,7 @@ type ui struct {
|
|||||||
syncRemotePassword widget.Editor
|
syncRemotePassword widget.Editor
|
||||||
syncDialogOpen bool
|
syncDialogOpen bool
|
||||||
syncMenuOpen bool
|
syncMenuOpen bool
|
||||||
|
mainMenuOpen bool
|
||||||
securityDialogOpen bool
|
securityDialogOpen bool
|
||||||
remotePrefsDialogOpen bool
|
remotePrefsDialogOpen bool
|
||||||
showSyncPassword bool
|
showSyncPassword bool
|
||||||
@@ -560,6 +565,12 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
phonePanelList: widget.List{
|
phonePanelList: widget.List{
|
||||||
List: layout.List{Axis: layout.Vertical},
|
List: layout.List{Axis: layout.Vertical},
|
||||||
},
|
},
|
||||||
|
securityDialogList: widget.List{
|
||||||
|
List: layout.List{Axis: layout.Vertical},
|
||||||
|
},
|
||||||
|
remotePrefsDialogList: widget.List{
|
||||||
|
List: layout.List{Axis: layout.Vertical},
|
||||||
|
},
|
||||||
recentVaultListState: widget.List{
|
recentVaultListState: widget.List{
|
||||||
List: layout.List{Axis: layout.Vertical},
|
List: layout.List{Axis: layout.Vertical},
|
||||||
},
|
},
|
||||||
@@ -604,6 +615,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
u.expandLessIcon, _ = widget.NewIcon(icons.NavigationExpandLess)
|
u.expandLessIcon, _ = widget.NewIcon(icons.NavigationExpandLess)
|
||||||
u.chevronDownIcon, _ = widget.NewIcon(icons.NavigationArrowDropDown)
|
u.chevronDownIcon, _ = widget.NewIcon(icons.NavigationArrowDropDown)
|
||||||
u.settingsIcon, _ = widget.NewIcon(icons.ActionSettings)
|
u.settingsIcon, _ = widget.NewIcon(icons.ActionSettings)
|
||||||
|
u.menuIcon, _ = widget.NewIcon(icons.NavigationMenu)
|
||||||
u.passwordProfile.SetText("strong")
|
u.passwordProfile.SetText("strong")
|
||||||
u.securityCipher.SetText(vault.CipherChaCha20)
|
u.securityCipher.SetText(vault.CipherChaCha20)
|
||||||
u.securityKDF.SetText(vault.KDFArgon2)
|
u.securityKDF.SetText(vault.KDFArgon2)
|
||||||
@@ -721,6 +733,7 @@ func (u *ui) selectedAttachmentNames() []string {
|
|||||||
func (u *ui) showEntriesSection() {
|
func (u *ui) showEntriesSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
u.state.ShowSection(appstate.SectionEntries)
|
u.state.ShowSection(appstate.SectionEntries)
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.restoreEntriesSectionState()
|
u.restoreEntriesSectionState()
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
@@ -729,6 +742,7 @@ func (u *ui) showTemplatesSection() {
|
|||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
u.rememberEntriesSectionState()
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionTemplates)
|
u.state.ShowSection(appstate.SectionTemplates)
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,6 +750,7 @@ func (u *ui) showRecycleBinSection() {
|
|||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
u.rememberEntriesSectionState()
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionRecycleBin)
|
u.state.ShowSection(appstate.SectionRecycleBin)
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,6 +758,7 @@ func (u *ui) showAPITokensSection() {
|
|||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
u.rememberEntriesSectionState()
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionAPITokens)
|
u.state.ShowSection(appstate.SectionAPITokens)
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.loadSelectedAPITokenIntoEditor()
|
u.loadSelectedAPITokenIntoEditor()
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
@@ -751,10 +767,37 @@ func (u *ui) showAPIAuditSection() {
|
|||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
u.rememberEntriesSectionState()
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionAPIAudit)
|
u.state.ShowSection(appstate.SectionAPIAudit)
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.selectedAuditIndex = -1
|
u.selectedAuditIndex = -1
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) returnToMainEntries() {
|
||||||
|
u.clearDeleteGroupConfirmation()
|
||||||
|
u.showEntriesSection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) handlePhoneBack() bool {
|
||||||
|
if u.mode != "phone" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case u.securityDialogOpen:
|
||||||
|
u.securityDialogOpen = false
|
||||||
|
case u.remotePrefsDialogOpen:
|
||||||
|
u.remotePrefsDialogOpen = false
|
||||||
|
case u.syncDialogOpen:
|
||||||
|
u.syncDialogOpen = false
|
||||||
|
case u.mainMenuOpen:
|
||||||
|
u.mainMenuOpen = false
|
||||||
|
case u.state.Section != appstate.SectionEntries:
|
||||||
|
u.returnToMainEntries()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) resetPasswordPeek() {
|
func (u *ui) resetPasswordPeek() {
|
||||||
u.showPassword = false
|
u.showPassword = false
|
||||||
}
|
}
|
||||||
@@ -2706,6 +2749,9 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
for u.toggleSyncMenu.Clicked(gtx) {
|
for u.toggleSyncMenu.Clicked(gtx) {
|
||||||
u.syncMenuOpen = !u.syncMenuOpen
|
u.syncMenuOpen = !u.syncMenuOpen
|
||||||
}
|
}
|
||||||
|
for u.toggleMainMenu.Clicked(gtx) {
|
||||||
|
u.mainMenuOpen = !u.mainMenuOpen
|
||||||
|
}
|
||||||
for u.openAdvancedSync.Clicked(gtx) {
|
for u.openAdvancedSync.Clicked(gtx) {
|
||||||
u.openAdvancedSyncDialog()
|
u.openAdvancedSyncDialog()
|
||||||
}
|
}
|
||||||
@@ -2713,6 +2759,7 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
u.loadSecuritySettingsFromSession()
|
u.loadSecuritySettingsFromSession()
|
||||||
u.loadSettingsFormFromPreferences()
|
u.loadSettingsFormFromPreferences()
|
||||||
u.loadSettingsDraft()
|
u.loadSettingsDraft()
|
||||||
|
u.mainMenuOpen = false
|
||||||
u.securityDialogOpen = true
|
u.securityDialogOpen = true
|
||||||
}
|
}
|
||||||
for u.openRemotePrefsHelp.Clicked(gtx) {
|
for u.openRemotePrefsHelp.Clicked(gtx) {
|
||||||
@@ -3308,20 +3355,20 @@ func (u *ui) remotePrefsDialog(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
rows := []layout.Widget{
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(20), "Settings")
|
lbl := material.Label(u.theme, unit.Sp(20), "Settings")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), "ACCESSIBILITY")
|
lbl := material.Label(u.theme, unit.Sp(12), "ACCESSIBILITY")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
layout.Spacer{Height: unit.Dp(4)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsPreferenceCard(gtx, "Display Density", "Adjust editor height and control spacing for denser or roomier forms.", func(gtx layout.Context) layout.Dimensions {
|
return u.settingsPreferenceCard(gtx, "Display Density", "Adjust editor height and control spacing for denser or roomier forms.", func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsChoiceRow(
|
return u.settingsChoiceRow(
|
||||||
gtx,
|
gtx,
|
||||||
@@ -3329,9 +3376,9 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
choiceSpec{Click: &u.settingsDensityComfortable, Label: "Comfortable", Active: u.settingsDraft.Accessibility.DisplayDensity == displayDensityComfortable},
|
choiceSpec{Click: &u.settingsDensityComfortable, Label: "Comfortable", Active: u.settingsDraft.Accessibility.DisplayDensity == displayDensityComfortable},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsPreferenceCard(gtx, "Contrast", "Increase focus and selection contrast without changing unrelated vault behavior.", func(gtx layout.Context) layout.Dimensions {
|
return u.settingsPreferenceCard(gtx, "Contrast", "Increase focus and selection contrast without changing unrelated vault behavior.", func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsChoiceRow(
|
return u.settingsChoiceRow(
|
||||||
gtx,
|
gtx,
|
||||||
@@ -3339,9 +3386,9 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
choiceSpec{Click: &u.settingsContrastHigh, Label: "High Contrast", Active: u.settingsDraft.Accessibility.Contrast == contrastHigh},
|
choiceSpec{Click: &u.settingsContrastHigh, Label: "High Contrast", Active: u.settingsDraft.Accessibility.Contrast == contrastHigh},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsPreferenceCard(gtx, "Reduced Motion", "Keep transient status toasts steady instead of auto-dismissing after a short timeout.", func(gtx layout.Context) layout.Dimensions {
|
return u.settingsPreferenceCard(gtx, "Reduced Motion", "Keep transient status toasts steady instead of auto-dismissing after a short timeout.", func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsChoiceRow(
|
return u.settingsChoiceRow(
|
||||||
gtx,
|
gtx,
|
||||||
@@ -3349,9 +3396,9 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
choiceSpec{Click: &u.settingsReducedMotionOn, Label: "On", Active: u.settingsDraft.Accessibility.ReducedMotion},
|
choiceSpec{Click: &u.settingsReducedMotionOn, Label: "On", Active: u.settingsDraft.Accessibility.ReducedMotion},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsPreferenceCard(gtx, "Keyboard Focus", "Strengthen the visible focus ring and focused selection treatment for keyboard-first navigation.", func(gtx layout.Context) layout.Dimensions {
|
return u.settingsPreferenceCard(gtx, "Keyboard Focus", "Strengthen the visible focus ring and focused selection treatment for keyboard-first navigation.", func(gtx layout.Context) layout.Dimensions {
|
||||||
return u.settingsChoiceRow(
|
return u.settingsChoiceRow(
|
||||||
gtx,
|
gtx,
|
||||||
@@ -3359,52 +3406,52 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
choiceSpec{Click: &u.settingsKeyboardFocusProminent, Label: "Prominent", Active: u.settingsDraft.Accessibility.KeyboardFocus == keyboardFocusProminent},
|
choiceSpec{Click: &u.settingsKeyboardFocusProminent, Label: "Prominent", Active: u.settingsDraft.Accessibility.KeyboardFocus == keyboardFocusProminent},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(14), "Choose how KeePassGO remembers UI layout behavior, sync defaults, and KDBX security defaults without crowding the main vault flow.")
|
lbl := material.Label(u.theme, unit.Sp(14), "Choose how KeePassGO remembers UI layout behavior, sync defaults, and KDBX security defaults without crowding the main vault flow.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(16), "UI Preferences")
|
lbl := material.Label(u.theme, unit.Sp(16), "UI Preferences")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
check := material.CheckBox(u.theme, &u.settingsGroupControls, "Keep Group Tools collapsed")
|
check := material.CheckBox(u.theme, &u.settingsGroupControls, "Keep Group Tools collapsed")
|
||||||
return check.Layout(gtx)
|
return check.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
check := material.CheckBox(u.theme, &u.settingsLifecycleAdvanced, "Keep advanced lifecycle controls collapsed")
|
check := material.CheckBox(u.theme, &u.settingsLifecycleAdvanced, "Keep advanced lifecycle controls collapsed")
|
||||||
return check.Layout(gtx)
|
return check.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
check := material.CheckBox(u.theme, &u.settingsHistory, "Keep entry history collapsed")
|
check := material.CheckBox(u.theme, &u.settingsHistory, "Keep entry history collapsed")
|
||||||
return check.Layout(gtx)
|
return check.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
layout.Spacer{Height: unit.Dp(14)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(16), "Vault Security")
|
lbl := material.Label(u.theme, unit.Sp(16), "Vault Security")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(labeledEditorHelp(u.theme, "Cipher", "Supported values: "+strings.Join([]string{vault.CipherAES256, vault.CipherChaCha20}, ", "), &u.securityCipher, false)),
|
labeledEditorHelp(u.theme, "Cipher", "Supported values: "+strings.Join([]string{vault.CipherAES256, vault.CipherChaCha20}, ", "), &u.securityCipher, false),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(labeledEditorHelp(u.theme, "KDF", "Supported values: "+strings.Join([]string{vault.KDFAES, vault.KDFArgon2}, ", "), &u.securityKDF, false)),
|
labeledEditorHelp(u.theme, "KDF", "Supported values: "+strings.Join([]string{vault.KDFAES, vault.KDFArgon2}, ", "), &u.securityKDF, false),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(syncDialogSectionLabel(u.theme, "Sync Defaults")),
|
syncDialogSectionLabel(u.theme, "Sync Defaults"),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(13), "Advanced Sync starts from these defaults. You can still change the source or direction before a single run.")
|
lbl := material.Label(u.theme, unit.Sp(13), "Advanced Sync starts from these defaults. You can still change the source or direction before a single run.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPull, "Pull Into Current Vault", u.settingsDraft.Sync.DirectionDefault == syncDirectionPull)
|
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPull, "Pull Into Current Vault", u.settingsDraft.Sync.DirectionDefault == syncDirectionPull)
|
||||||
@@ -3414,9 +3461,9 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPush, "Push Current Vault Out", u.settingsDraft.Sync.DirectionDefault == syncDirectionPush)
|
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncPush, "Push Current Vault Out", u.settingsDraft.Sync.DirectionDefault == syncDirectionPush)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncLocal, "Local File", u.settingsDraft.Sync.SourceDefault == syncSourceLocal)
|
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncLocal, "Local File", u.settingsDraft.Sync.SourceDefault == syncSourceLocal)
|
||||||
@@ -3426,41 +3473,41 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncRemote, "Remote WebDAV", u.settingsDraft.Sync.SourceDefault == syncSourceRemote)
|
return syncChoiceButton(gtx, u.theme, &u.showSettingsSyncRemote, "Remote WebDAV", u.settingsDraft.Sync.SourceDefault == syncSourceRemote)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return syncDialogSummaryCard(gtx, u.theme, u.settingsDraft.Sync.SourceDefault, u.settingsDraft.Sync.DirectionDefault)
|
return syncDialogSummaryCard(gtx, u.theme, u.settingsDraft.Sync.SourceDefault, u.settingsDraft.Sync.DirectionDefault)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), "Conflict handling stays retry-safe: merged entry changes keep history, while remote save conflicts still require reopening the vault and retrying the save.")
|
lbl := material.Label(u.theme, unit.Sp(12), "Conflict handling stays retry-safe: merged entry changes keep history, while remote save conflicts still require reopening the vault and retrying the save.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(syncDialogSectionLabel(u.theme, "Background Sync")),
|
syncDialogSectionLabel(u.theme, "Background Sync"),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), "Future background sync controls belong here so source, direction, and unattended behavior stay in one settings surface.")
|
lbl := material.Label(u.theme, unit.Sp(12), "Future background sync controls belong here so source, direction, and unattended behavior stay in one settings surface.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(syncDialogSectionLabel(u.theme, "Feedback")),
|
syncDialogSectionLabel(u.theme, "Feedback"),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(14), "Success and reminder banners")
|
lbl := material.Label(u.theme, unit.Sp(14), "Success and reminder banners")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
layout.Spacer{Height: unit.Dp(4)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), "Choose how long noncritical status banners stay visible.")
|
lbl := material.Label(u.theme, unit.Sp(12), "Choose how long noncritical status banners stay visible.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return syncChoiceButton(gtx, u.theme, &u.setStatusBannerShort, "Short", u.statusBannerTTL == 2*time.Second)
|
return syncChoiceButton(gtx, u.theme, &u.setStatusBannerShort, "Short", u.statusBannerTTL == 2*time.Second)
|
||||||
@@ -3474,21 +3521,21 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return syncChoiceButton(gtx, u.theme, &u.setStatusBannerLong, "Long", u.statusBannerTTL == statusBannerLong)
|
return syncChoiceButton(gtx, u.theme, &u.setStatusBannerLong, "Long", u.statusBannerTTL == statusBannerLong)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
layout.Spacer{Height: unit.Dp(10)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(14), "Autofill notices")
|
lbl := material.Label(u.theme, unit.Sp(14), "Autofill notices")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
layout.Spacer{Height: unit.Dp(4)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), "Keep recent autofill results visible, reduce them to approval-only, or hide them.")
|
lbl := material.Label(u.theme, unit.Sp(12), "Keep recent autofill results visible, reduce them to approval-only, or hide them.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return syncChoiceButton(gtx, u.theme, &u.showAllAutofillNotices, "All", u.autofillNoticePreference == autofillNoticeAll)
|
return syncChoiceButton(gtx, u.theme, &u.showAllAutofillNotices, "All", u.autofillNoticePreference == autofillNoticeAll)
|
||||||
@@ -3502,27 +3549,27 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return syncChoiceButton(gtx, u.theme, &u.hideAutofillNotices, "Hidden", u.autofillNoticePreference == autofillNoticeSuppressed)
|
return syncChoiceButton(gtx, u.theme, &u.hideAutofillNotices, "Hidden", u.autofillNoticePreference == autofillNoticeSuppressed)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(syncDialogSectionLabel(u.theme, "Privacy")),
|
syncDialogSectionLabel(u.theme, "Privacy"),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return settingsSummaryCard(gtx, u.theme, "PRIVACY PLAN", "Use first-fill approval plus browser/app rules to keep autofill constrained to trusted targets.")
|
return settingsSummaryCard(gtx, u.theme, "PRIVACY PLAN", "Use first-fill approval plus browser/app rules to keep autofill constrained to trusted targets.")
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(14), "First-fill approval")
|
lbl := material.Label(u.theme, unit.Sp(14), "First-fill approval")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
layout.Spacer{Height: unit.Dp(4)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), u.autofillFirstFillApprovalSummary())
|
lbl := material.Label(u.theme, unit.Sp(12), u.autofillFirstFillApprovalSummary())
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Spacer{Height: unit.Dp(6)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return syncChoiceButton(gtx, u.theme, &u.showAutofillApprovalAsk, "Ask First", u.autofillFirstFillApprovalMode == autofillFirstFillApprovalAsk)
|
return syncChoiceButton(gtx, u.theme, &u.showAutofillApprovalAsk, "Ask First", u.autofillFirstFillApprovalMode == autofillFirstFillApprovalAsk)
|
||||||
@@ -3536,21 +3583,21 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return syncChoiceButton(gtx, u.theme, &u.showAutofillApprovalBlock, "Block Until Allowed", u.autofillFirstFillApprovalMode == autofillFirstFillApprovalBlock)
|
return syncChoiceButton(gtx, u.theme, &u.showAutofillApprovalBlock, "Block Until Allowed", u.autofillFirstFillApprovalMode == autofillFirstFillApprovalBlock)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout),
|
layout.Spacer{Height: unit.Dp(10)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(12), fmt.Sprintf("%d autofill rule entries configured across browsers, apps, and package-specific overrides.", u.autofillRuleCount()))
|
lbl := material.Label(u.theme, unit.Sp(12), fmt.Sprintf("%d autofill rule entries configured across browsers, apps, and package-specific overrides.", u.autofillRuleCount()))
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(labeledEditorHelp(u.theme, "Browser allowlist", "One origin or hostname per line for trusted browser surfaces.", &u.autofillBrowserAllowlist, false)),
|
labeledEditorHelp(u.theme, "Browser allowlist", "One origin or hostname per line for trusted browser surfaces.", &u.autofillBrowserAllowlist, false),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(labeledEditorHelp(u.theme, "App and package allowlist", "One Android package name or trusted app identifier per line.", &u.autofillAppAllowlist, false)),
|
labeledEditorHelp(u.theme, "App and package allowlist", "One Android package name or trusted app identifier per line.", &u.autofillAppAllowlist, false),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(labeledEditorHelp(u.theme, "Package rules", "One rule per line, for example `com.android.chrome=hostname` or `org.keepassgo.browser=view-id`.", &u.autofillPackageRules, false)),
|
labeledEditorHelp(u.theme, "Package rules", "One rule per line, for example `com.android.chrome=hostname` or `org.keepassgo.browser=view-id`.", &u.autofillPackageRules, false),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) 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 {
|
||||||
return tonedButton(gtx, u.theme, &u.closeSecuritySettings, "Close")
|
return tonedButton(gtx, u.theme, &u.closeSecuritySettings, "Close")
|
||||||
@@ -3560,44 +3607,50 @@ func (u *ui) securityDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
return tonedButton(gtx, u.theme, &u.saveSecuritySettings, "Save Settings")
|
return tonedButton(gtx, u.theme, &u.saveSecuritySettings, "Save Settings")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
},
|
||||||
)
|
}
|
||||||
|
return material.List(u.theme, &u.securityDialogList).Layout(gtx, len(rows), func(gtx layout.Context, i int) layout.Dimensions {
|
||||||
|
return rows[i](gtx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) remotePrefsDialogContent(gtx layout.Context) layout.Dimensions {
|
func (u *ui) remotePrefsDialogContent(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
rows := []layout.Widget{
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(20), "Remote Connection Settings & Help")
|
lbl := material.Label(u.theme, unit.Sp(20), "Remote Connection Settings & Help")
|
||||||
lbl.Color = accentColor
|
lbl.Color = accentColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
lbl := material.Label(u.theme, unit.Sp(14), "Use Recent Connections to reopen WebDAV-backed vaults quickly without cluttering the main open flow.")
|
lbl := material.Label(u.theme, unit.Sp(14), "Use Recent Connections to reopen WebDAV-backed vaults quickly without cluttering the main open flow.")
|
||||||
lbl.Color = mutedColor
|
lbl.Color = mutedColor
|
||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
|
layout.Spacer{Height: unit.Dp(12)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return approvalFact(u.theme, "Current", u.remotePreferencesCurrentSummary(), "")(gtx)
|
return approvalFact(u.theme, "Current", u.remotePreferencesCurrentSummary(), "")(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return approvalFact(u.theme, "Always Saved", u.remotePreferencesAlwaysSavedSummary(), "")(gtx)
|
return approvalFact(u.theme, "Always Saved", u.remotePreferencesAlwaysSavedSummary(), "")(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return approvalFact(u.theme, "Retention", u.remotePreferencesRetentionSummary(), "")(gtx)
|
return approvalFact(u.theme, "Retention", u.remotePreferencesRetentionSummary(), "")(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Spacer{Height: unit.Dp(8)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return approvalFact(u.theme, "When Sign-in Saves", "Username and password or app token are only stored after a successful remote open when Remember sign-in is enabled.", "")(gtx)
|
return approvalFact(u.theme, "When Sign-in Saves", "Username and password or app token are only stored after a successful remote open when Remember sign-in is enabled.", "")(gtx)
|
||||||
}),
|
},
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
layout.Spacer{Height: unit.Dp(14)}.Layout,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
return tonedButton(gtx, u.theme, &u.closeRemotePrefsHelp, "Done")
|
return tonedButton(gtx, u.theme, &u.closeRemotePrefsHelp, "Done")
|
||||||
}),
|
},
|
||||||
)
|
}
|
||||||
|
return material.List(u.theme, &u.remotePrefsDialogList).Layout(gtx, len(rows), func(gtx layout.Context, i int) layout.Dimensions {
|
||||||
|
return rows[i](gtx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) approvalDialog(gtx layout.Context) layout.Dimensions {
|
func (u *ui) approvalDialog(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -3852,35 +3905,20 @@ func (u *ui) header(gtx layout.Context) layout.Dimensions {
|
|||||||
if u.shouldShowLifecycleSetup() || u.isVaultLocked() {
|
if u.shouldShowLifecycleSetup() || u.isVaultLocked() {
|
||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||||
summary := u.currentVaultSummary()
|
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { return layout.Dimensions{} }),
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
layout.Rigid(u.headerActions),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
)
|
||||||
lbl := material.Label(u.theme, unit.Sp(11), "VAULT")
|
}),
|
||||||
lbl.Color = mutedColor
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
return lbl.Layout(gtx)
|
if !u.mainMenuOpen {
|
||||||
}),
|
return layout.Dimensions{}
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(1)}.Layout),
|
}
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
return layout.Inset{Top: unit.Dp(8)}.Layout(gtx, u.mainMenu)
|
||||||
lbl := material.Label(u.theme, unit.Sp(15), summary.Title)
|
}),
|
||||||
lbl.Color = accentColor
|
)
|
||||||
return lbl.Layout(gtx)
|
|
||||||
}),
|
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|
||||||
if strings.TrimSpace(summary.Detail) == "" || summary.Detail == summary.Title {
|
|
||||||
return layout.Dimensions{}
|
|
||||||
}
|
|
||||||
lbl := material.Label(u.theme, unit.Sp(11), summary.Detail)
|
|
||||||
lbl.Color = mutedColor
|
|
||||||
return lbl.Layout(gtx)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
layout.Rigid(u.headerActions),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if u.shouldShowDesktopWorkingHeader() {
|
if u.shouldShowDesktopWorkingHeader() {
|
||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
@@ -3909,7 +3947,11 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
|||||||
layout.Rigid(u.syncButtonGroup),
|
layout.Rigid(u.syncButtonGroup),
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
btn := material.IconButton(u.theme, &u.openSecuritySettings, u.settingsIcon, "Settings")
|
icon := u.menuIcon
|
||||||
|
if icon == nil {
|
||||||
|
icon = u.settingsIcon
|
||||||
|
}
|
||||||
|
btn := material.IconButton(u.theme, &u.toggleMainMenu, icon, "Menu")
|
||||||
btn.Background = selectedColor
|
btn.Background = selectedColor
|
||||||
btn.Color = accentColor
|
btn.Color = accentColor
|
||||||
btn.Size = unit.Dp(18)
|
btn.Size = unit.Dp(18)
|
||||||
@@ -3924,6 +3966,32 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
||||||
|
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return tonedButton(gtx, u.theme, &u.showEntries, "Entries")
|
||||||
|
}),
|
||||||
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return tonedButton(gtx, u.theme, &u.showRecycle, "Recycle Bin")
|
||||||
|
}),
|
||||||
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return tonedButton(gtx, u.theme, &u.showAPITokens, "API Tokens")
|
||||||
|
}),
|
||||||
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return tonedButton(gtx, u.theme, &u.showAPIAudit, "API Audit")
|
||||||
|
}),
|
||||||
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return tonedButton(gtx, u.theme, &u.openSecuritySettings, "Settings")
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) syncButtonGroup(gtx layout.Context) layout.Dimensions {
|
func (u *ui) syncButtonGroup(gtx layout.Context) layout.Dimensions {
|
||||||
label := "Sync"
|
label := "Sync"
|
||||||
spacing := unit.Dp(4)
|
spacing := unit.Dp(4)
|
||||||
@@ -4033,11 +4101,25 @@ func (u *ui) listPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
if u.mode == "phone" {
|
if u.mode == "phone" {
|
||||||
return panel(gtx, func(gtx layout.Context) layout.Dimensions {
|
return panel(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
rows := make([]layout.Widget, 0, 16+len(u.visible))
|
rows := make([]layout.Widget, 0, 16+len(u.visible))
|
||||||
|
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
||||||
|
gtx.Constraints.Min.X = gtx.Constraints.Max.X
|
||||||
|
return u.outlinedFieldState(gtx, u.isFocused(focusSearch), func(gtx layout.Context) layout.Dimensions {
|
||||||
|
editor := material.Editor(u.theme, &u.search, "Search vault")
|
||||||
|
editor.Color = u.theme.Palette.Fg
|
||||||
|
editor.HintColor = mutedColor
|
||||||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, editor.Layout)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
||||||
|
return layout.Spacer{Height: spacing}.Layout(gtx)
|
||||||
|
})
|
||||||
if !u.isVaultLocked() {
|
if !u.isVaultLocked() {
|
||||||
rows = append(rows, u.navigationHeader)
|
rows = append(rows, u.navigationHeader)
|
||||||
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
if u.state.Section == appstate.SectionEntries {
|
||||||
return layout.Spacer{Height: spacing}.Layout(gtx)
|
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
||||||
})
|
return layout.Spacer{Height: spacing}.Layout(gtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !u.isVaultLocked() && u.state.Section == appstate.SectionRecycleBin {
|
if !u.isVaultLocked() && u.state.Section == appstate.SectionRecycleBin {
|
||||||
rows = append(rows, u.recycleBinSectionNotice)
|
rows = append(rows, u.recycleBinSectionNotice)
|
||||||
@@ -4061,18 +4143,6 @@ func (u *ui) listPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
return layout.Spacer{Height: spacing}.Layout(gtx)
|
return layout.Spacer{Height: spacing}.Layout(gtx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
|
||||||
gtx.Constraints.Min.X = gtx.Constraints.Max.X
|
|
||||||
return u.outlinedFieldState(gtx, u.isFocused(focusSearch), func(gtx layout.Context) layout.Dimensions {
|
|
||||||
editor := material.Editor(u.theme, &u.search, "Search vault")
|
|
||||||
editor.Color = u.theme.Palette.Fg
|
|
||||||
editor.HintColor = mutedColor
|
|
||||||
return layout.UniformInset(unit.Dp(10)).Layout(gtx, editor.Layout)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
|
||||||
return layout.Spacer{Height: spacing}.Layout(gtx)
|
|
||||||
})
|
|
||||||
if !u.isVaultLocked() {
|
if !u.isVaultLocked() {
|
||||||
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
rows = append(rows, func(gtx layout.Context) layout.Dimensions {
|
||||||
switch u.state.Section {
|
switch u.state.Section {
|
||||||
@@ -4206,17 +4276,10 @@ func (u *ui) listPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
|
|
||||||
func (u *ui) navigationHeader(gtx layout.Context) layout.Dimensions {
|
func (u *ui) navigationHeader(gtx layout.Context) layout.Dimensions {
|
||||||
if u.mode == "phone" {
|
if u.mode == "phone" {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
if u.state.Section != appstate.SectionEntries {
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
return layout.Dimensions{}
|
||||||
return u.sectionBar(gtx)
|
}
|
||||||
}),
|
return u.groupControlsDisclosure(gtx)
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|
||||||
if u.state.Section != appstate.SectionEntries {
|
|
||||||
return layout.Dimensions{}
|
|
||||||
}
|
|
||||||
return layout.Inset{Top: unit.Dp(4)}.Layout(gtx, u.groupControlsDisclosure)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -4455,13 +4518,23 @@ func (u *ui) detailPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
layout.Rigid(u.syncButtonGroup),
|
layout.Rigid(u.syncButtonGroup),
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
btn := material.IconButton(u.theme, &u.openSecuritySettings, u.settingsIcon, "Settings")
|
icon := u.menuIcon
|
||||||
|
if icon == nil {
|
||||||
|
icon = u.settingsIcon
|
||||||
|
}
|
||||||
|
btn := material.IconButton(u.theme, &u.toggleMainMenu, icon, "Menu")
|
||||||
btn.Background = selectedColor
|
btn.Background = selectedColor
|
||||||
btn.Color = accentColor
|
btn.Color = accentColor
|
||||||
btn.Size = unit.Dp(18)
|
btn.Size = unit.Dp(18)
|
||||||
btn.Inset = layout.UniformInset(unit.Dp(8))
|
btn.Inset = layout.UniformInset(unit.Dp(8))
|
||||||
return btn.Layout(gtx)
|
return btn.Layout(gtx)
|
||||||
}),
|
}),
|
||||||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
if !u.mainMenuOpen {
|
||||||
|
return layout.Dimensions{}
|
||||||
|
}
|
||||||
|
return layout.Inset{Left: unit.Dp(6)}.Layout(gtx, u.mainMenu)
|
||||||
|
}),
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
btn := material.Button(u.theme, &u.lockVault, "Lock")
|
btn := material.Button(u.theme, &u.lockVault, "Lock")
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"git.julianfamily.org/keepassgo/apiapproval"
|
"git.julianfamily.org/keepassgo/apiapproval"
|
||||||
"git.julianfamily.org/keepassgo/apiaudit"
|
"git.julianfamily.org/keepassgo/apiaudit"
|
||||||
"git.julianfamily.org/keepassgo/apitokens"
|
"git.julianfamily.org/keepassgo/apitokens"
|
||||||
|
"git.julianfamily.org/keepassgo/appstate"
|
||||||
"git.julianfamily.org/keepassgo/clipboard"
|
"git.julianfamily.org/keepassgo/clipboard"
|
||||||
"git.julianfamily.org/keepassgo/passwords"
|
"git.julianfamily.org/keepassgo/passwords"
|
||||||
"git.julianfamily.org/keepassgo/session"
|
"git.julianfamily.org/keepassgo/session"
|
||||||
@@ -806,6 +807,41 @@ func TestUIPhoneListPanelWithExpandedGroupControlsAndEntriesDoesNotPanic(t *test
|
|||||||
_ = u.listPanel(gtx)
|
_ = u.listPanel(gtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIPhoneBackReturnsFromSubscreenToEntries(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := newUIWithModel("phone", vault.Model{
|
||||||
|
Entries: []vault.Entry{{ID: "entry-1", Title: "Git Server", Path: []string{"Root", "Internet"}}},
|
||||||
|
})
|
||||||
|
u.showAPITokensSection()
|
||||||
|
|
||||||
|
if !u.handlePhoneBack() {
|
||||||
|
t.Fatal("handlePhoneBack() = false, want true for phone subsection")
|
||||||
|
}
|
||||||
|
if u.state.Section != appstate.SectionEntries {
|
||||||
|
t.Fatalf("state.Section = %q, want entries", u.state.Section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUISecurityDialogContentDoesNotPanicWithSmallViewport(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := newUIWithModel("phone", vault.Model{})
|
||||||
|
u.securityDialogOpen = true
|
||||||
|
ops := new(op.Ops)
|
||||||
|
gtx := layout.Context{
|
||||||
|
Ops: ops,
|
||||||
|
Constraints: layout.Exact(image.Pt(540, 420)),
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Fatalf("securityDialogContent() panicked in small viewport: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_ = u.securityDialogContent(gtx)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIAPIAuditSectionShowsRecordedEvents(t *testing.T) {
|
func TestUIAPIAuditSectionShowsRecordedEvents(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ func (u *ui) processShortcuts(gtx layout.Context) {
|
|||||||
key.Filter{Name: key.NameUpArrow},
|
key.Filter{Name: key.NameUpArrow},
|
||||||
key.Filter{Name: key.NameDownArrow},
|
key.Filter{Name: key.NameDownArrow},
|
||||||
key.Filter{Name: key.NameReturn},
|
key.Filter{Name: key.NameReturn},
|
||||||
|
key.Filter{Name: key.NameBack},
|
||||||
|
key.Filter{Name: key.NameEscape},
|
||||||
)
|
)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
@@ -48,6 +50,9 @@ func (u *ui) processShortcuts(gtx layout.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.handleKeyPress(ke.Name, ke.Modifiers)
|
u.handleKeyPress(ke.Name, ke.Modifiers)
|
||||||
|
if ke.Name == key.NameBack || ke.Name == key.NameEscape {
|
||||||
|
_ = u.handlePhoneBack()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user