Simplify KeePassGO desktop vault workflow
This commit is contained in:
+82
-98
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
surface := u.sessionSurface()
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
@@ -26,34 +25,9 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(12), "MASTER KEY MODE")
|
||||
if surface.Locked {
|
||||
lbl.Text += " • " + strings.ToUpper(surface.Message)
|
||||
}
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Master Password", "Leave blank if this vault is protected by key file only.", &u.masterPassword, true)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.masterKeyPasswordOnly, "Password Only")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.masterKeyKeyFileOnly, "Key File Only")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.masterKeyComposite, "Password + Key File")
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Master Password", "Used alone or together with a key file to unlock the vault.", &u.masterPassword, true)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, false)),
|
||||
layout.Rigid(selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleMode == "remote" {
|
||||
@@ -68,39 +42,22 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
)
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Vault Path", "Local path to an existing .kdbx file to open.", &u.vaultPath, false)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Save As Path", "Local target path used when creating a new vault or saving a copy.", &u.saveAsPath, false)),
|
||||
layout.Rigid(selectorEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, &u.pickVaultPath, "Choose File", false)),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleMode == "remote" {
|
||||
return tonedButton(gtx, u.theme, &u.openRemote, "Open Remote Vault")
|
||||
}
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.createVault, "New Vault")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleMode == "remote" {
|
||||
return tonedButton(gtx, u.theme, &u.openRemote, "Open Remote")
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.openVault, "Open Vault")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.saveVault, "Save") }),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleMode == "remote" {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.saveAsVault, "Save As")
|
||||
return tonedButton(gtx, u.theme, &u.createVault, "New Vault")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.changeMasterKey, "Change Master Key")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.unlockVault, "Unlock") }),
|
||||
)
|
||||
}),
|
||||
)
|
||||
@@ -135,7 +92,7 @@ func (u *ui) attachmentList(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
|
||||
func (u *ui) groupControls(gtx layout.Context) layout.Dimensions {
|
||||
if u.state.Section == appstate.SectionRecycleBin {
|
||||
if u.state.Section != appstate.SectionEntries {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
@@ -146,13 +103,20 @@ func (u *ui) groupControls(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.createGroup, "Create Group")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.renameGroup, "Rename Group")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.deleteGroup, "Delete Group")
|
||||
if len(u.currentPath) == 0 {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.renameGroup, "Rename Group")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.deleteGroup, "Delete Group")
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
@@ -161,8 +125,6 @@ func (u *ui) groupControls(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
func (u *ui) entryEditorPanel(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "ID", &u.entryID, false, u.isFocused(detailFocusID(detailFieldID)))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "Title", &u.entryTitle, false, u.isFocused(detailFocusID(detailFieldTitle)))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "Username", &u.entryUsername, false, u.isFocused(detailFocusID(detailFieldUsername)))),
|
||||
@@ -185,49 +147,33 @@ func (u *ui) entryEditorPanel(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "Notes", &u.entryNotes, false, u.isFocused(detailFocusID(detailFieldNotes)))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "Custom Fields (key=value)", &u.entryFields, false, u.isFocused(detailFocusID(detailFieldFields)))),
|
||||
layout.Rigid(labeledEditorHelpFocus(u.theme, "Custom Fields", "One key=value pair per line. These fields are only saved when you save the entry.", &u.entryFields, false, u.isFocused(detailFocusID(detailFieldFields)))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(labeledEditorWithFocus(u.theme, "History Index", &u.historyIndex, false, u.isFocused(detailFocusID(detailFieldHistoryIndex)))),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
switch u.state.Section {
|
||||
case appstate.SectionTemplates:
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.generatePassword, "Generate Password")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.cancelEdit, "Cancel")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.state.Section == appstate.SectionTemplates {
|
||||
return tonedButton(gtx, u.theme, &u.saveTemplate, "Save Template")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.deleteTemplate, "Delete Template")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.instantiateTemplate, "Instantiate")
|
||||
}),
|
||||
)
|
||||
case appstate.SectionRecycleBin:
|
||||
return tonedButton(gtx, u.theme, &u.restoreEntry, "Restore Entry")
|
||||
default:
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.saveEntry, "Save Entry")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.duplicateEntry, "Duplicate")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.deleteEntry, "Delete") }),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.generatePassword, "Generate Password")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.restoreHistory, "Restore History")
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.saveEntry, "Save Entry")
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(11), "Generate Password only updates the form. Nothing is persisted until you save.")
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
@@ -283,9 +229,13 @@ func labeledEditor(th *material.Theme, label string, editor *widget.Editor, sens
|
||||
}
|
||||
|
||||
func labeledEditorHelp(th *material.Theme, label, help string, editor *widget.Editor, sensitive bool) layout.Widget {
|
||||
return labeledEditorHelpFocus(th, label, help, editor, sensitive, false)
|
||||
}
|
||||
|
||||
func labeledEditorHelpFocus(th *material.Theme, label, help string, editor *widget.Editor, sensitive bool, focused bool) layout.Widget {
|
||||
return func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(labeledEditor(th, label, editor, sensitive)),
|
||||
layout.Rigid(labeledEditorWithFocus(th, label, editor, sensitive, focused)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(th, unit.Sp(11), help)
|
||||
@@ -296,6 +246,40 @@ func labeledEditorHelp(th *material.Theme, label, help string, editor *widget.Ed
|
||||
}
|
||||
}
|
||||
|
||||
func selectorEditorHelp(th *material.Theme, label, help string, editor *widget.Editor, click *widget.Clickable, buttonLabel string, sensitive bool) layout.Widget {
|
||||
return func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, labeledEditor(th, label, editor, sensitive)),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, th, click, buttonLabel)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(th, unit.Sp(11), help)
|
||||
lbl.Color = mutedColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) unlockPanel(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(labeledEditorHelp(u.theme, "Master Password", "Used alone or together with a key file to unlock the vault.", &u.masterPassword, true)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.unlockVault, "Unlock")
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func labeledEditorWithFocus(
|
||||
th *material.Theme,
|
||||
label string,
|
||||
|
||||
Reference in New Issue
Block a user