Clarify lifecycle loading and target selection
This commit is contained in:
@@ -61,8 +61,10 @@ const (
|
||||
)
|
||||
|
||||
type uiBanner struct {
|
||||
Kind bannerKind
|
||||
Message string
|
||||
Kind bannerKind
|
||||
Message string
|
||||
Detail string
|
||||
Dismissable bool
|
||||
}
|
||||
|
||||
type uiSurface struct {
|
||||
@@ -200,6 +202,9 @@ type ui struct {
|
||||
pickVaultPath widget.Clickable
|
||||
pickKeyFile widget.Clickable
|
||||
pickSyncLocalPath widget.Clickable
|
||||
clearVaultSelection widget.Clickable
|
||||
clearRemoteSelection widget.Clickable
|
||||
dismissBanner widget.Clickable
|
||||
addEntry widget.Clickable
|
||||
saveEntry widget.Clickable
|
||||
duplicateEntry widget.Clickable
|
||||
@@ -1688,21 +1693,60 @@ func (u *ui) describeActionError(label string, err error) string {
|
||||
func (u *ui) bannerSurface() uiBanner {
|
||||
switch {
|
||||
case strings.TrimSpace(u.loadingMessage) != "":
|
||||
return uiBanner{Kind: bannerLoading, Message: strings.TrimSpace(u.loadingMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerLoading,
|
||||
Message: strings.TrimSpace(u.loadingMessage),
|
||||
Detail: u.loadingDetailMessage(),
|
||||
}
|
||||
case strings.TrimSpace(u.state.ErrorMessage) != "":
|
||||
return uiBanner{Kind: bannerError, Message: strings.TrimSpace(u.state.ErrorMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerError,
|
||||
Message: strings.TrimSpace(u.state.ErrorMessage),
|
||||
Dismissable: true,
|
||||
}
|
||||
case strings.TrimSpace(u.state.StatusMessage) != "":
|
||||
if !u.statusExpiresAt.IsZero() && !u.now().Before(u.statusExpiresAt) {
|
||||
u.state.StatusMessage = ""
|
||||
u.statusExpiresAt = time.Time{}
|
||||
return uiBanner{}
|
||||
}
|
||||
return uiBanner{Kind: bannerStatus, Message: strings.TrimSpace(u.state.StatusMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerStatus,
|
||||
Message: strings.TrimSpace(u.state.StatusMessage),
|
||||
Dismissable: true,
|
||||
}
|
||||
default:
|
||||
return uiBanner{}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) loadingDetailMessage() string {
|
||||
if !u.shouldShowLifecycleSetup() {
|
||||
return ""
|
||||
}
|
||||
if u.lifecycleMode == "remote" {
|
||||
baseURL := strings.TrimSpace(u.remoteBaseURL.Text())
|
||||
path := strings.TrimSpace(u.remotePath.Text())
|
||||
switch {
|
||||
case baseURL != "" && path != "":
|
||||
return fmt.Sprintf(
|
||||
"Target: %s (%s)",
|
||||
friendlyRecentRemoteLabel(recentRemoteRecord{BaseURL: baseURL, Path: path}),
|
||||
path,
|
||||
)
|
||||
case baseURL != "":
|
||||
return "Target: " + baseURL
|
||||
default:
|
||||
return "Preparing remote vault access"
|
||||
}
|
||||
}
|
||||
path := strings.TrimSpace(u.vaultPath.Text())
|
||||
if path == "" {
|
||||
return "Preparing local vault access"
|
||||
}
|
||||
return "Target: " + path
|
||||
}
|
||||
|
||||
func (u *ui) sessionSurface() uiSurface {
|
||||
if u.state.Session == nil {
|
||||
return uiSurface{}
|
||||
@@ -1739,6 +1783,10 @@ func (u *ui) shouldShowLifecycleSetup() bool {
|
||||
return !u.hasOpenVault()
|
||||
}
|
||||
|
||||
func (u *ui) lifecycleBusy() bool {
|
||||
return u.shouldShowLifecycleSetup() && strings.TrimSpace(u.loadingMessage) != ""
|
||||
}
|
||||
|
||||
func (u *ui) shouldUseLockedSinglePane() bool {
|
||||
return u.isVaultLocked() && !u.shouldShowLifecycleSetup()
|
||||
}
|
||||
@@ -1958,12 +2006,21 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
u.showAPIAuditSection()
|
||||
}
|
||||
for u.showLocalLifecycle.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleMode = "local"
|
||||
}
|
||||
for u.showRemoteLifecycle.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleMode = "remote"
|
||||
}
|
||||
for u.toggleLifecycleAdvanced.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleAdvancedHidden = !u.lifecycleAdvancedHidden
|
||||
u.saveUIPreferences()
|
||||
}
|
||||
@@ -2059,9 +2116,15 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
u.loadSelectedEntryIntoEditor()
|
||||
}
|
||||
for u.pickVaultPath.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.runAction("choose vault path", func() error { return u.chooseExistingFileAction(&u.vaultPath) })
|
||||
}
|
||||
for u.pickKeyFile.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.runAction("choose key file", func() error { return u.chooseExistingFileAction(&u.keyFilePath) })
|
||||
}
|
||||
for u.pickSyncLocalPath.Clicked(gtx) {
|
||||
@@ -2069,18 +2132,50 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
for i := range u.recentVaultClicks {
|
||||
for u.recentVaultClicks[i].Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
if i < len(u.recentVaults) {
|
||||
u.lifecycleMode = "local"
|
||||
u.vaultPath.SetText(u.recentVaults[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range u.recentRemoteClicks {
|
||||
for u.recentRemoteClicks[i].Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
if i < len(u.recentRemotes) {
|
||||
u.lifecycleMode = "remote"
|
||||
u.applyRecentRemoteRecord(u.recentRemotes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for u.clearVaultSelection.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.vaultPath.SetText("")
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
}
|
||||
for u.clearRemoteSelection.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.remoteBaseURL.SetText("")
|
||||
u.remotePath.SetText("")
|
||||
u.remoteUsername.SetText("")
|
||||
u.remotePassword.SetText("")
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
}
|
||||
for u.dismissBanner.Clicked(gtx) {
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
u.statusExpiresAt = time.Time{}
|
||||
}
|
||||
for u.addEntry.Clicked(gtx) {
|
||||
u.state.BeginNewEntry()
|
||||
u.loadSelectedEntryIntoEditor()
|
||||
@@ -3304,9 +3399,35 @@ func (u *ui) banner(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
return layout.Background{}.Layout(gtx, fill(bg), func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.UniformInset(unit.Dp(12)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(14), banner.Message)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(14), banner.Message)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if strings.TrimSpace(banner.Detail) == "" {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Inset{Top: unit.Dp(2)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(12), banner.Detail)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if !banner.Dismissable {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Inset{Left: unit.Dp(10)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.dismissBanner, "Dismiss")
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2905,6 +2905,89 @@ func TestSelectingRecentRemoteConnectionKeepsPasswordMasked(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectingRecentVaultSwitchesToLocalMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.lifecycleMode = "remote"
|
||||
u.recentVaults = []string{"/tmp/example.kdbx"}
|
||||
u.recentVaultClicks = make([]widget.Clickable, 1)
|
||||
u.recentVaultClicks[0].Click()
|
||||
|
||||
gtx := layout.Context{}
|
||||
for u.recentVaultClicks[0].Clicked(gtx) {
|
||||
if 0 < len(u.recentVaults) {
|
||||
u.lifecycleMode = "local"
|
||||
u.vaultPath.SetText(u.recentVaults[0])
|
||||
}
|
||||
}
|
||||
|
||||
if got := u.lifecycleMode; got != "local" {
|
||||
t.Fatalf("lifecycleMode after recent vault click = %q, want local", got)
|
||||
}
|
||||
if got := u.vaultPath.Text(); got != "/tmp/example.kdbx" {
|
||||
t.Fatalf("vaultPath after recent vault click = %q, want /tmp/example.kdbx", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectingRecentRemoteSwitchesToRemoteMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.lifecycleMode = "local"
|
||||
u.recentRemotes = []recentRemoteRecord{{
|
||||
BaseURL: "https://dav.example.com",
|
||||
Path: "vaults/home.kdbx",
|
||||
}}
|
||||
u.recentRemoteClicks = make([]widget.Clickable, 1)
|
||||
u.recentRemoteClicks[0].Click()
|
||||
|
||||
gtx := layout.Context{}
|
||||
for u.recentRemoteClicks[0].Clicked(gtx) {
|
||||
if 0 < len(u.recentRemotes) {
|
||||
u.lifecycleMode = "remote"
|
||||
u.applyRecentRemoteRecord(u.recentRemotes[0])
|
||||
}
|
||||
}
|
||||
|
||||
if got := u.lifecycleMode; got != "remote" {
|
||||
t.Fatalf("lifecycleMode after recent remote click = %q, want remote", got)
|
||||
}
|
||||
if got := u.remoteBaseURL.Text(); got != "https://dav.example.com" {
|
||||
t.Fatalf("remoteBaseURL after recent remote click = %q, want https://dav.example.com", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUILoadingDetailMessageUsesSelectedVault(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.vaultPath.SetText("/home/julian/vaults/main.kdbx")
|
||||
u.loadingMessage = "Open vault..."
|
||||
|
||||
got := u.loadingDetailMessage()
|
||||
want := "Target: /home/julian/vaults/main.kdbx"
|
||||
if got != want {
|
||||
t.Fatalf("loadingDetailMessage() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUILoadingDetailMessageUsesSelectedRemote(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.lifecycleMode = "remote"
|
||||
u.remoteBaseURL.SetText("https://dav.example.com")
|
||||
u.remotePath.SetText("vaults/home.kdbx")
|
||||
u.loadingMessage = "Open remote vault..."
|
||||
|
||||
got := u.loadingDetailMessage()
|
||||
want := "Target: dav.example.com · vaults/home.kdbx (vaults/home.kdbx)"
|
||||
if got != want {
|
||||
t.Fatalf("loadingDetailMessage() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIOpenRemoteVaultRestoresLastOpenedGroupForThatConnection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
+93
-10
@@ -17,6 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
busy := u.lifecycleBusy()
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(12), "OPEN OR CREATE VAULT")
|
||||
@@ -27,10 +28,16 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return passiveSectionTab(gtx, u.theme, "Local Vault", u.lifecycleMode == "local")
|
||||
}
|
||||
return sectionTabButton(gtx, u.theme, &u.showLocalLifecycle, "Local Vault", u.lifecycleMode == "local")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return passiveSectionTab(gtx, u.theme, "Remote Vault", u.lifecycleMode == "remote")
|
||||
}
|
||||
return sectionTabButton(gtx, u.theme, &u.showRemoteLifecycle, "Remote Vault", u.lifecycleMode == "remote")
|
||||
}),
|
||||
)
|
||||
@@ -40,7 +47,12 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
return u.masterPasswordField(gtx, "Leave blank if this vault is protected by key file only.")
|
||||
}),
|
||||
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(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return labeledEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, false)(gtx)
|
||||
}
|
||||
return selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleMode == "remote" {
|
||||
@@ -91,6 +103,13 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
lbl.Color = accentColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.clearRemoteSelection, "Change...")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(11), record.BaseURL)
|
||||
@@ -116,7 +135,12 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
return box.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(u.recentRemoteList),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return u.recentRemoteList(gtx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
@@ -126,7 +150,12 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(selectorEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, &u.pickVaultPath, "Choose File", false)),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return labeledEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, false)(gtx)
|
||||
}
|
||||
return selectorEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, &u.pickVaultPath, "Choose File", false)(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if strings.TrimSpace(u.vaultPath.Text()) == "" {
|
||||
@@ -146,6 +175,13 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
lbl.Color = accentColor
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.clearVaultSelection, "Change...")
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(11), u.vaultPath.Text())
|
||||
@@ -157,14 +193,34 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
||||
})
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(u.recentVaultList),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return u.recentVaultList(gtx)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||
layout.Rigid(u.lifecycleAdvancedDisclosure),
|
||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if u.lifecycleAdvancedHidden {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Spacer{Height: unit.Dp(8)}.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return u.lifecycleAdvancedDisclosure(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Spacer{Height: unit.Dp(6)}.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy || u.lifecycleAdvancedHidden {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
@@ -176,14 +232,31 @@ 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 {
|
||||
if u.lifecycleMode == "remote" {
|
||||
return tonedButton(gtx, u.theme, &u.openRemote, "Open Remote Vault")
|
||||
label := "Open Remote Vault"
|
||||
if busy {
|
||||
label = "Opening Remote Vault..."
|
||||
}
|
||||
if busy {
|
||||
return passiveTonedButton(gtx, u.theme, label)
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.openRemote, label)
|
||||
}
|
||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.openVault, "Open Vault")
|
||||
label := "Open Vault"
|
||||
if busy {
|
||||
label = "Opening Vault..."
|
||||
}
|
||||
if busy {
|
||||
return passiveTonedButton(gtx, u.theme, label)
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.openVault, label)
|
||||
}),
|
||||
layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if busy {
|
||||
return passiveTonedButton(gtx, u.theme, "New Vault")
|
||||
}
|
||||
return tonedButton(gtx, u.theme, &u.createVault, "New Vault")
|
||||
}),
|
||||
)
|
||||
@@ -368,6 +441,16 @@ func recentSelectionCard(gtx layout.Context, selected bool, w layout.Widget) lay
|
||||
)
|
||||
}
|
||||
|
||||
func passiveSectionTab(gtx layout.Context, th *material.Theme, label string, active bool) layout.Dimensions {
|
||||
click := new(widget.Clickable)
|
||||
return sectionTabButton(gtx, th, click, label, active)
|
||||
}
|
||||
|
||||
func passiveTonedButton(gtx layout.Context, th *material.Theme, label string) layout.Dimensions {
|
||||
click := new(widget.Clickable)
|
||||
return tonedButton(gtx, th, click, label)
|
||||
}
|
||||
|
||||
func friendlyRecentVaultLabel(path string) string {
|
||||
value := strings.TrimSpace(path)
|
||||
if value == "" {
|
||||
|
||||
Reference in New Issue
Block a user