feature/ui - zatim ne uplne uhlazena ale celkem pouzitelna appka #1

Merged
luke-20 merged 17 commits from feature/ui into main 2025-09-28 21:05:52 +02:00
2 changed files with 105 additions and 95 deletions
Showing only changes of commit 8c950dd7fc - Show all commits

View File

@ -16,7 +16,7 @@ import (
func NewUI() (stprageDir string, window fyne.Window) { func NewUI() (stprageDir string, window fyne.Window) {
// App + storage dir // App + storage dir
a := app.New() a := app.NewWithID("fckeuspy")
w := a.NewWindow("Encryptor (Fyne)") w := a.NewWindow("Encryptor (Fyne)")
prefs := a.Preferences() prefs := a.Preferences()
width := prefs.IntWithFallback("winW", 1100) width := prefs.IntWithFallback("winW", 1100)

198
ui.go
View File

@ -12,6 +12,7 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
@ -113,12 +114,15 @@ var forceDark = true
// Build key section // Build key section
func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject { func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject {
btnCopyPub := widget.NewButton("Copy public.pem", func() { copyClip(svc.PublicPEM(), parts) }) // Toolbar actions
btnCopyCrt := widget.NewButton("Copy identity.crt", func() { copyClip(svc.PublicCert(), parts) }) identityToggle := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
btnShowPub := widget.NewButton("Show pub", func() { parts.outKey.SetText(svc.PublicPEM()) }) identityToolbar := widget.NewToolbar(
btnShowCrt := widget.NewButton("Show crt", func() { parts.outKey.SetText(svc.PublicCert()) }) widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(svc.PublicPEM()+"\n"+svc.PublicCert(), parts) }),
btnClear := widget.NewButton("Clear", func() { parts.outKey.SetText("") }) widget.NewToolbarAction(theme.ContentPasteIcon(), func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) }),
btnPaste := widget.NewButton("Paste", func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) }) widget.NewToolbarAction(theme.ContentClearIcon(), func() { parts.outKey.SetText("") }),
widget.NewToolbarSeparator(),
identityToggle,
)
deleteBtn := widget.NewButton("Smazat identitu", func() { deleteBtn := widget.NewButton("Smazat identitu", func() {
pwEntry := widget.NewPasswordEntry() pwEntry := widget.NewPasswordEntry()
@ -175,10 +179,7 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
} }
identityContainer := container.NewVBox() identityContainer := container.NewVBox()
rebuild := func() {
toggleBtn := widget.NewButton("", nil)
var rebuild func()
rebuild = func() {
identityContainer.Objects = nil identityContainer.Objects = nil
if parts.showQR { if parts.showQR {
updateQRImages() updateQRImages()
@ -193,26 +194,18 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
} }
identityContainer.Refresh() identityContainer.Refresh()
if parts.showQR { if parts.showQR {
toggleBtn.SetText("Zobrazit plaintext") identityToggle.SetIcon(theme.VisibilityOffIcon())
} else { } else {
toggleBtn.SetText("Zobrazit QR") identityToggle.SetIcon(theme.VisibilityIcon())
} }
} }
toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; rebuild() } identityToggle.OnActivated = func() { parts.showQR = !parts.showQR; rebuild() }
rebuild() rebuild()
// Group buttons by function: data viewing vs clipboard vs destructive // Header with toolbar and content + destructive action
clipboardRow := buttonTile(btnCopyPub, btnCopyCrt, btnPaste, btnClear) header := container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer(), identityToolbar)
viewRow := buttonTile(btnShowPub, btnShowCrt, toggleBtn) content := container.NewVBox(header, identityContainer, buttonTile(deleteBtn))
destroyRow := buttonTile(deleteBtn) return container.NewVScroll(content)
group := container.NewVBox(
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
viewRow,
identityContainer,
clipboardRow,
destroyRow,
)
return container.NewVScroll(group)
} }
// Helper functions separated to avoid circular dependency with encrypt.Service // Helper functions separated to avoid circular dependency with encrypt.Service
@ -240,14 +233,10 @@ func copyClip(s string, parts *uiParts) {
// Tab: Encrypt // Tab: Encrypt
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
parts.cipherOut.Disable() parts.cipherOut.Disable()
peerBtns := buttonTile(
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
widget.NewButton("Clear", func() { parts.peer.SetText("") }),
widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) }),
)
// Peer section with QR/Text toggle
peerContainer := container.NewVBox() peerContainer := container.NewVBox()
peerToggle := widget.NewButton("", nil) peerToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
var updatePeer func() var updatePeer func()
updatePeerQR := func(text string) { updatePeerQR := func(text string) {
if text == "" { if text == "" {
@ -271,20 +260,24 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
if parts.showPeerQR { if parts.showPeerQR {
updatePeerQR(parts.peer.Text) updatePeerQR(parts.peer.Text)
peerContainer.Add(container.NewVBox(parts.peerQR, widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) }))) peerContainer.Add(container.NewVBox(parts.peerQR, widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) })))
peerToggle.SetText("Zobrazit plaintext") peerToggleAction.SetIcon(theme.VisibilityOffIcon())
} else { } else {
peerContainer.Add(parts.peer) peerContainer.Add(parts.peer)
peerToggle.SetText("Zobrazit QR") peerToggleAction.SetIcon(theme.VisibilityIcon())
} }
peerContainer.Refresh() peerContainer.Refresh()
} }
peerToggle.OnTapped = func() { parts.showPeerQR = !parts.showPeerQR; updatePeer() } peerToggleAction.OnActivated = func() { parts.showPeerQR = !parts.showPeerQR; updatePeer() }
parts.peer.OnChanged = func(string) { parts.peer.OnChanged = func(string) {
if parts.showPeerQR { if parts.showPeerQR {
updatePeerQR(parts.peer.Text) updatePeerQR(parts.peer.Text)
} }
} }
updatePeer() updatePeer()
// Output section with QR/Text toggle
outputContainer := container.NewVBox()
outputToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
updateQR := func(text string) { updateQR := func(text string) {
if text == "" { if text == "" {
parts.cipherQR.Image = nil parts.cipherQR.Image = nil
@ -304,6 +297,21 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
parts.cipherQR.Image = img parts.cipherQR.Image = img
parts.cipherQR.Refresh() parts.cipherQR.Refresh()
} }
updateMode := func() {
outputContainer.Objects = nil
if parts.showQR {
updateQR(parts.cipherOut.Text)
outputContainer.Add(container.NewVBox(parts.cipherQR, widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) })))
outputToggleAction.SetIcon(theme.VisibilityOffIcon())
} else {
outputContainer.Add(parts.cipherOut)
outputToggleAction.SetIcon(theme.VisibilityIcon())
}
outputContainer.Refresh()
}
outputToggleAction.OnActivated = func() { parts.showQR = !parts.showQR; updateMode() }
updateMode()
encAction := func() { encAction := func() {
m := parts.msg.Text m := parts.msg.Text
p := parts.peer.Text p := parts.peer.Text
@ -326,6 +334,7 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
}) })
}(m, p) }(m, p)
} }
importPeerQR := func() { importPeerQR := func() {
fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) { fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
if err != nil || r == nil { if err != nil || r == nil {
@ -348,46 +357,37 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"})) fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
fd.Show() fd.Show()
} }
outputContainer := container.NewVBox()
toggleBtn := widget.NewButton("", nil)
var updateMode func()
updateMode = func() {
outputContainer.Objects = nil
if parts.showQR {
updateQR(parts.cipherOut.Text)
outputContainer.Add(container.NewVBox(parts.cipherQR, widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) })))
} else {
outputContainer.Add(parts.cipherOut)
}
if parts.showQR {
toggleBtn.SetText("Zobrazit plaintext")
} else {
toggleBtn.SetText("Zobrazit QR")
}
outputContainer.Refresh()
}
toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; updateMode() }
updateMode()
msgBtns := buttonTile( // Toolbars
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }), peerToolbar := widget.NewToolbar(
widget.NewButton("Encrypt", func() { encAction(); updateMode() }), widget.NewToolbarAction(theme.ContentPasteIcon(), func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) }), widget.NewToolbarAction(theme.ContentClearIcon(), func() { parts.peer.SetText("") }),
widget.NewButton("Import Key QR", importPeerQR), widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.peer.Text, parts) }),
widget.NewToolbarAction(theme.FolderOpenIcon(), importPeerQR),
widget.NewToolbarSeparator(),
peerToggleAction,
)
outputToolbar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.cipherOut.Text, parts) }),
widget.NewToolbarSeparator(),
outputToggleAction,
) )
// Primary CTA
encryptBtn := widget.NewButtonWithIcon("Zašifrovat", theme.ConfirmIcon(), func() { encAction(); updateMode() })
// Sections
peerSection := container.NewVBox( peerSection := container.NewVBox(
container.NewHBox(widget.NewLabel("Veřejný klíč příjemce"), peerToggle), container.NewHBox(widget.NewLabel("Veřejný klíč příjemce"), layout.NewSpacer(), peerToolbar),
peerContainer, peerContainer,
peerBtns,
) )
msgSection := container.NewVBox( msgSection := container.NewVBox(
widget.NewLabel("Zpráva"), widget.NewLabel("Zpráva"),
parts.msg, parts.msg,
msgBtns, container.NewHBox(layout.NewSpacer(), encryptBtn),
) )
outputSection := container.NewVBox( outputSection := container.NewVBox(
container.NewHBox(widget.NewLabel("Výstup"), toggleBtn), container.NewHBox(widget.NewLabel("Výstup"), layout.NewSpacer(), outputToolbar),
outputContainer, outputContainer,
) )
group := container.NewVBox( group := container.NewVBox(
@ -460,8 +460,33 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"})) fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
fd.Show() fd.Show()
} }
payloadBtns := buttonTile( // Toolbar toggle action for payload section
widget.NewButton("Paste+Decrypt", func() { payloadToggleAction := widget.NewToolbarAction(theme.VisibilityOffIcon(), nil)
payloadContainer := container.NewVBox()
updateMode := func() {
payloadContainer.Objects = nil
if parts.showPayloadQR {
updatePayloadQR(parts.payload.Text)
payloadContainer.Add(container.NewVBox(parts.payloadQR, widget.NewButton("Copy", func() { copyClip(parts.payload.Text, parts) })))
payloadToggleAction.SetIcon(theme.VisibilityOffIcon())
} else {
payloadContainer.Add(parts.payload)
payloadToggleAction.SetIcon(theme.VisibilityIcon())
}
payloadContainer.Refresh()
}
payloadToggleAction.OnActivated = func() { parts.showPayloadQR = !parts.showPayloadQR; updateMode() }
parts.payload.OnChanged = func(string) {
if parts.showPayloadQR {
updatePayloadQR(parts.payload.Text)
}
}
updateMode()
// Build payload toolbar
payloadToolbar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
clip := fyne.CurrentApp().Clipboard().Content() clip := fyne.CurrentApp().Clipboard().Content()
parts.payload.SetText(clip) parts.payload.SetText(clip)
if parts.showPayloadQR { if parts.showPayloadQR {
@ -469,48 +494,33 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
} }
decryptAction() decryptAction()
}), }),
widget.NewButton("Clear", func() { widget.NewToolbarAction(theme.ContentClearIcon(), func() {
parts.payload.SetText("") parts.payload.SetText("")
parts.plainOut.SetText("") parts.plainOut.SetText("")
if parts.showPayloadQR { if parts.showPayloadQR {
updatePayloadQR("") updatePayloadQR("")
} }
}), }),
widget.NewButton("QR Import", importPayloadQR), widget.NewToolbarAction(theme.FolderOpenIcon(), importPayloadQR),
widget.NewToolbarSeparator(),
payloadToggleAction,
) )
payloadContainer := container.NewVBox() // Build result toolbar
payloadToggle := widget.NewButton("", nil) resultToolbar := widget.NewToolbar(
var updateMode func() widget.NewToolbarAction(theme.ContentCopyIcon(), func() { copyClip(parts.plainOut.Text, parts) }),
updateMode = func() { )
payloadContainer.Objects = nil
if parts.showPayloadQR { // Primary CTA for decryption
updatePayloadQR(parts.payload.Text) decryptBtn := widget.NewButtonWithIcon("Dešifrovat", theme.ConfirmIcon(), func() { decryptAction() })
payloadContainer.Add(container.NewVBox(parts.payloadQR, widget.NewButton("Copy", func() { copyClip(parts.payload.Text, parts) })))
payloadToggle.SetText("Zobrazit plaintext")
} else {
payloadContainer.Add(parts.payload)
payloadToggle.SetText("Zobrazit QR")
}
payloadContainer.Refresh()
}
payloadToggle.OnTapped = func() { parts.showPayloadQR = !parts.showPayloadQR; updateMode() }
parts.payload.OnChanged = func(string) {
if parts.showPayloadQR {
updatePayloadQR(parts.payload.Text)
}
}
updateMode()
plainBtns := buttonTile(widget.NewButton("Copy", func() { copyClip(parts.plainOut.Text, parts) }))
payloadSection := container.NewVBox( payloadSection := container.NewVBox(
container.NewHBox(widget.NewLabel("Payload"), payloadToggle), container.NewHBox(widget.NewLabel("Payload"), layout.NewSpacer(), payloadToolbar),
payloadContainer, payloadContainer,
payloadBtns, container.NewHBox(layout.NewSpacer(), decryptBtn),
) )
resultSection := container.NewVBox( resultSection := container.NewVBox(
widget.NewLabel("Výsledek"), container.NewHBox(widget.NewLabel("Výsledek"), layout.NewSpacer(), resultToolbar),
parts.plainOut, parts.plainOut,
plainBtns,
) )
group := container.NewVBox( group := container.NewVBox(
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),