From a34cd1005d987153b10632b952b2ae9c301962c0 Mon Sep 17 00:00:00 2001 From: Lukas Batelka Date: Sun, 28 Sep 2025 22:22:25 +0200 Subject: [PATCH] feature/upgrade-ui - vylepsene ui --- qr_support.go | 47 +------------ ui.go | 190 +++++++++++++++++++------------------------------- 2 files changed, 73 insertions(+), 164 deletions(-) diff --git a/qr_support.go b/qr_support.go index 339d976..9877685 100755 --- a/qr_support.go +++ b/qr_support.go @@ -7,7 +7,6 @@ import ( "image/color" "image/draw" "image/png" - "log" "math" "os/exec" @@ -32,17 +31,12 @@ func GenerateQRPNG(text string, size int) ([]byte, error) { // DecodeQR decodes first QR code text from an image. func DecodeQR(img image.Image) (string, error) { - log.Printf("[qr] start decode: %dx%d %T", img.Bounds().Dx(), img.Bounds().Dy(), img) // Try basic decode first codes, err := goqr.Recognize(img) if err == nil && len(codes) > 0 { - log.Printf("[qr] basic success length=%d", len(codes)) return string(codes[0].Payload), nil } if err != nil { - log.Printf("[qr] basic error: %v", err) - } else { - log.Printf("[qr] basic no codes") } // Convert palette/indexed images to RGBA first to avoid issues with goqr @@ -55,14 +49,8 @@ func DecodeQR(img image.Image) (string, error) { // Try again after conversion codes, err := goqr.Recognize(img) if err == nil && len(codes) > 0 { - log.Printf("[qr] palette-convert success") return string(codes[0].Payload), nil } - if err != nil { - log.Printf("[qr] palette-convert err: %v", err) - } else { - log.Printf("[qr] palette-convert no codes") - } } // Try multiple preprocessing strategies to improve recognition from clipboard screenshots @@ -213,20 +201,17 @@ func DecodeQR(img image.Image) (string, error) { } var firstErr error - for idx, attempt := range attempts { + for _, attempt := range attempts { codes, err := goqr.Recognize(attempt) if err != nil { if firstErr == nil { firstErr = err } - log.Printf("[qr] attempt %d error: %v", idx, err) continue } if len(codes) > 0 { - log.Printf("[qr] attempt %d success (%d codes)", idx, len(codes)) return string(codes[0].Payload), nil } - log.Printf("[qr] attempt %d no codes", idx) } // ZXing helper used across multiple branches @@ -247,25 +232,19 @@ func DecodeQR(img image.Image) (string, error) { } } // Also try ZXing on all preprocessed attempts - for idx, attempt := range attempts { + for _, attempt := range attempts { if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{ gozxing.DecodeHintType_TRY_HARDER: true, gozxing.DecodeHintType_PURE_BARCODE: true, }); err == nil && txt != "" { - log.Printf("[qr] zxing attempt %d success (pure)", idx) return txt, nil } if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{ gozxing.DecodeHintType_TRY_HARDER: true, }); err == nil && txt != "" { - log.Printf("[qr] zxing attempt %d success (try-harder)", idx) return txt, nil } } - if firstErr != nil { - log.Printf("[qr] preprocess firstErr: %v", firstErr) - } - // --- Heuristic fallback: auto-invert + quiet-zone pad + gozxing --- addQuietZone := func(src image.Image) image.Image { b := src.Bounds() @@ -315,7 +294,6 @@ func DecodeQR(img image.Image) (string, error) { modified = append(modified, addQuietZone(invert(base))) for _, m := range modified { if codes, err := goqr.Recognize(m); err == nil && len(codes) > 0 { - log.Printf("[qr] quiet-zone/invert success") return string(codes[0].Payload), nil } // Also try ZXing on these variants before moving on @@ -323,13 +301,11 @@ func DecodeQR(img image.Image) (string, error) { gozxing.DecodeHintType_TRY_HARDER: true, gozxing.DecodeHintType_PURE_BARCODE: true, }); err == nil && txt != "" { - log.Printf("[qr] gozxing success (quiet-zone+pure)") return txt, nil } if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{ gozxing.DecodeHintType_TRY_HARDER: true, }); err == nil && txt != "" { - log.Printf("[qr] gozxing success (quiet-zone+try-harder)") return txt, nil } } @@ -340,27 +316,19 @@ func DecodeQR(img image.Image) (string, error) { gozxing.DecodeHintType_PURE_BARCODE: true, } if txt, err := decodeZX(base, hints); err == nil && txt != "" { - log.Printf("[qr] gozxing success (hints)") return txt, nil - } else if err != nil { - log.Printf("[qr] gozxing(hints) err: %v", err) } // Try again without PURE_BARCODE in case quiet zone is cropped hints2 := map[gozxing.DecodeHintType]interface{}{gozxing.DecodeHintType_TRY_HARDER: true} if txt, err := decodeZX(base, hints2); err == nil && txt != "" { - log.Printf("[qr] gozxing success (try-harder)") return txt, nil - } else if err != nil { - log.Printf("[qr] gozxing(try-harder) err: %v", err) } // Try ZXing on rotations as well for _, r := range rotate(base) { if txt, err := decodeZX(r, hints2); err == nil && txt != "" { - log.Printf("[qr] gozxing success (rotated)") return txt, nil } if txt, err := decodeZX(r, hints); err == nil && txt != "" { - log.Printf("[qr] gozxing success (rotated pure)") return txt, nil } } @@ -392,7 +360,6 @@ func DecodeQR(img image.Image) (string, error) { tryPure := func(src image.Image) (string, bool) { for _, px := range []int{1, 2, 3} { if txt, err := decodeZX(inset(src, px), hints); err == nil && txt != "" { - log.Printf("[qr] gozxing pure+inset %d success", px) return txt, true } } @@ -425,11 +392,9 @@ func DecodeQR(img image.Image) (string, error) { continue } if txt, err := decodeZX(cand, hints); err == nil && txt != "" { - log.Printf("[qr] gozxing pure+asym inset %d,%d,%d,%d success", l, t, r, btm) return txt, nil } if txt, err := decodeZX(cand, hints2); err == nil && txt != "" { - log.Printf("[qr] gozxing asym inset %d,%d,%d,%d success", l, t, r, btm) return txt, nil } } @@ -465,7 +430,6 @@ func DecodeQR(img image.Image) (string, error) { if minX < maxX && minY < maxY { crop := image.NewRGBA(image.Rect(0, 0, maxX-minX+1, maxY-minY+1)) draw.Draw(crop, crop.Bounds(), base, image.Point{X: minX, Y: minY}, draw.Src) - log.Printf("[qr] crop bbox size=%dx%d", crop.Bounds().Dx(), crop.Bounds().Dy()) for _, up := range []int{2, 3, 4} { cw := crop.Bounds().Dx() * up ch := crop.Bounds().Dy() * up @@ -481,10 +445,7 @@ func DecodeQR(img image.Image) (string, error) { } } if txt, err := decodeZX(scaled, hints2); err == nil && txt != "" { - log.Printf("[qr] gozxing crop+scale success up=%d", up) return txt, nil - } else if err != nil { - log.Printf("[qr] crop up=%d fail: %v", up, err) } } } @@ -499,12 +460,8 @@ func DecodeQR(img image.Image) (string, error) { c.Stdin = bytes.NewReader(buf.Bytes()) out, err := c.Output() if err == nil && len(out) > 0 { - log.Printf("[qr] zbarimg success") return string(out), nil } - if err != nil { - log.Printf("[qr] zbarimg error: %v", err) - } } } return "", errors.New("no qr code found") diff --git a/ui.go b/ui.go index 59a12db..a940be7 100755 --- a/ui.go +++ b/ui.go @@ -10,7 +10,6 @@ import ( "image/color" "image/png" "io" - "log" "os" "os/exec" "path/filepath" @@ -55,6 +54,7 @@ func buildEntries() *uiParts { p.pubQR.SetMinSize(fyne.NewSize(200, 200)) p.peerQR.SetMinSize(fyne.NewSize(200, 200)) p.payloadQR.SetMinSize(fyne.NewSize(220, 220)) + p.pubQR.FillMode = canvas.ImageFillContain p.toastLabel.Hide() return p } @@ -74,10 +74,15 @@ func (p *uiParts) showToast(s string) { type simpleTheme struct{} func (simpleTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color { - if n == theme.ColorNameBackground { + switch n { + case theme.ColorNameBackground: return color.NRGBA{24, 27, 31, 255} + case theme.ColorNameDisabled: + // Make disabled text brighter for readability on dark background + return color.NRGBA{230, 233, 238, 255} + default: + return theme.DefaultTheme().Color(n, v) } - return theme.DefaultTheme().Color(n, v) } func (simpleTheme) Font(st fyne.TextStyle) fyne.Resource { return theme.DefaultTheme().Font(st) } func (simpleTheme) Icon(n fyne.ThemeIconName) fyne.Resource { return theme.DefaultTheme().Icon(n) } @@ -172,7 +177,6 @@ func readImageClipboard() (image.Image, error) { if os.Getenv("WAYLAND_DISPLAY") != "" && has("wl-paste") { if types, err := exec.Command("wl-paste", "--list-types").Output(); err == nil { - log.Printf("[clip] wl types: %s", strings.TrimSpace(string(types))) pref := []string{"image/png", "image/jpeg", "image/jpg", "image/webp"} seen := map[string]bool{} for _, p := range pref { @@ -190,23 +194,13 @@ func readImageClipboard() (image.Image, error) { } for _, t := range order { if data, err := exec.Command("wl-paste", "--type", t).Output(); err == nil { - log.Printf("[clip] got type %s size=%d", t, len(data)) if img, ok := tryDecode(data); ok { - // save debug - _ = os.MkdirAll("qr_debug", 0o755) - fn := filepath.Join("qr_debug", fmt.Sprintf("clip_raw_%d.png", time.Now().UnixNano())) - if f, e := os.Create(fn); e == nil { - _ = png.Encode(f, img) - f.Close() - log.Printf("[clip] saved %s", fn) - } return img, nil } } } } if data, err := exec.Command("wl-paste").Output(); err == nil { - log.Printf("[clip] generic wl-paste size=%d", len(data)) if img, ok := tryDecode(data); ok { return img, nil } @@ -215,14 +209,12 @@ func readImageClipboard() (image.Image, error) { if has("xclip") { for _, m := range []string{"image/png", "image/jpeg", "image/jpg", "image/bmp"} { if data, err := exec.Command("xclip", "-selection", "clipboard", "-t", m, "-o").Output(); err == nil { - log.Printf("[clip] xclip type %s size=%d", m, len(data)) if img, ok := tryDecode(data); ok { return img, nil } } } if data, err := exec.Command("xclip", "-selection", "clipboard", "-o").Output(); err == nil { - log.Printf("[clip] xclip raw size=%d", len(data)) if img, ok := tryDecode(data); ok { return img, nil } @@ -234,18 +226,19 @@ func readImageClipboard() (image.Image, error) { // Identity tab func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject { // Toolbar: choose what to encode into the QR to keep density manageable - mode := "combined" // combined, cert, pub - chooser := widget.NewSelect([]string{"Veřejný+Cert", "Jen Cert", "Jen Veřejný"}, func(string) {}) - chooser.Selected = "Veřejný+Cert" + mode := "cert" // cert, pub + options := []string{"Certifikát", "Veřejný klíč"} + chooser := widget.NewSelect(options, func(string) {}) + chooser.Selected = options[0] var update func() chooser.OnChanged = func(v string) { switch v { - case "Jen Cert": + case "Certifikát": mode = "cert" - case "Jen Veřejný": + case "Veřejný klíč": mode = "pub" default: - mode = "combined" + mode = "cert" } update() } @@ -267,6 +260,8 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne. d.Resize(fyne.NewSize(420, 200)) d.Show() }) + // Keep button minimal; align right + deleteRow := container.NewHBox(layout.NewSpacer(), deleteBtn) makeQR := func(data string, target *canvas.Image) { if data == "" { target.Image = nil @@ -284,38 +279,51 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne. if parts.showQR { var text string switch mode { - case "cert": - text = strings.TrimSpace(svc.PublicCert()) case "pub": text = strings.TrimSpace(svc.PublicPEM()) - default: - text = strings.TrimSpace(svc.PublicPEM() + "\n" + svc.PublicCert()) + default: // cert + text = strings.TrimSpace(svc.PublicCert()) } makeQR(text, parts.pubQR) - // debug save for comparison - if parts.pubQR.Image != nil { - _ = os.MkdirAll("qr_debug", 0o755) - if f, e := os.Create(filepath.Join("qr_debug", "identity_current.png")); e == nil { - png.Encode(f, parts.pubQR.Image) - f.Close() - } - } } else { parts.pubQR.Image = nil parts.pubQR.Refresh() } } update() - box := container.NewVBox(container.NewHBox(widget.NewLabel("QR obsah:"), chooser, layout.NewSpacer(), widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) })), parts.pubQR) - return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteBtn)) + saveQR := widget.NewButtonWithIcon("Uložit QR", theme.DocumentSaveIcon(), func() { + if parts.pubQR.Image == nil { + return + } + win := fyne.CurrentApp().Driver().AllWindows()[0] + img := parts.pubQR.Image + fd := dialog.NewFileSave(func(wc fyne.URIWriteCloser, err error) { + if err != nil || wc == nil { + return + } + defer wc.Close() + _ = png.Encode(wc, img) + parts.showToast("QR uložen") + }, win) + fd.SetFileName("identity_qr.png") + fd.Show() + }) + qrRow := container.NewHBox( + widget.NewLabel("Obsah QR"), + chooser, + layout.NewSpacer(), + widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) }), + saveQR, + ) + box := container.NewVBox(qrRow, container.NewCenter(parts.pubQR)) + return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteRow)) } // Decrypt tab func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { parts.plainOut.Disable() - parts.payload.Disable() - parts.payload.SetPlaceHolder("Cyphertext se načte po vložení QR kódu…") - parts.payload.OnChanged = nil + parts.plainOut.Wrapping = fyne.TextWrapWord + parts.plainOut.SetMinRowsVisible(12) parts.payloadQR.FillMode = canvas.ImageFillContain parts.payloadQR.SetMinSize(fyne.NewSize(260, 260)) @@ -339,7 +347,6 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { } setPayload := func(cipher string, img image.Image) { - parts.payload.SetText(cipher) parts.payloadQR.Image = img parts.payloadQR.Refresh() decrypt(cipher) @@ -404,30 +411,26 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { fd.Show() }) - copyPayloadBtn := widget.NewButtonWithIcon("Kopírovat payload", theme.ContentCopyIcon(), func() { - if parts.payload.Text == "" { - return - } - copyClip(parts.payload.Text, parts) - }) - clearBtn := widget.NewButtonWithIcon("Vymazat", theme.ContentClearIcon(), func() { - parts.payload.SetText("") parts.payloadQR.Image = nil parts.payloadQR.Refresh() parts.plainOut.SetText("") parts.showToast("Vymazáno") }) - toolbar := container.NewHBox(pasteQRBtn, openQRBtn, copyPayloadBtn, clearBtn, layout.NewSpacer()) + // Align buttons to the right by placing spacer first + toolbar := container.NewHBox(layout.NewSpacer(), pasteQRBtn, openQRBtn, clearBtn) + copyDecBtn := widget.NewButtonWithIcon("Kopírovat zprávu", theme.ContentCopyIcon(), func() { + if strings.TrimSpace(parts.plainOut.Text) != "" { + copyClip(parts.plainOut.Text, parts) + } + }) return container.NewVBox( widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), toolbar, container.NewHBox(parts.payloadQR, layout.NewSpacer()), - widget.NewLabel("Payload"), - parts.payload, - widget.NewLabel("Výsledek"), + container.NewHBox(widget.NewLabel("Výsledek"), layout.NewSpacer(), copyDecBtn), parts.plainOut, ) } @@ -455,13 +458,11 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) { } } win := fyne.CurrentApp().Driver().AllWindows()[0] - lastCipher := "" doEncrypt := func() { m := strings.TrimSpace(msgEntry.Text) fyne.Do(func() { if m == "" { status.SetText("Zpráva je prázdná") - lastCipher = "" updateQR("") return } @@ -476,7 +477,7 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) { fyne.Do(func() { status.SetText("Chyba: " + err.Error()) }) return } - fyne.Do(func() { lastCipher = res; updateQR(res); status.SetText("Hotovo") }) + fyne.Do(func() { updateQR(res); status.SetText("Hotovo") }) }(m) } var tmr *time.Timer @@ -486,12 +487,6 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) { } tmr = time.AfterFunc(300*time.Millisecond, doEncrypt) } - copyPayloadBtn := widget.NewButton("Kopírovat payload", func() { - if lastCipher != "" { - copyClip(lastCipher, parts) - status.SetText("Zkopírováno") - } - }) copyQRBtn := widget.NewButton("Kopírovat QR", func() { copyImageToClipboard(qrImg.Image, parts) }) saveQRBtn := widget.NewButton("Uložit QR", func() { if qrImg.Image == nil { @@ -509,7 +504,7 @@ func openEncryptPopup(parts *uiParts, svc ServiceFacade, ct Contact) { fd.SetFileName("message_qr.png") fd.Show() }) - content := container.NewVBox(widget.NewLabel("Zpráva"), msgEntry, widget.NewSeparator(), container.NewHBox(widget.NewLabel("QR kód"), layout.NewSpacer(), copyPayloadBtn, copyQRBtn, saveQRBtn), qrImg, status) + content := container.NewVBox(widget.NewLabel("Zpráva"), msgEntry, widget.NewSeparator(), container.NewHBox(widget.NewLabel("QR kód"), layout.NewSpacer(), copyQRBtn, saveQRBtn), qrImg, status) title := ct.Name if title == "" { title = "(bez názvu)" @@ -573,13 +568,6 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { if existing != nil { certValue = existing.Cert } - manualEntry := widget.NewMultiLineEntry() - manualEntry.SetMinRowsVisible(4) - manualEntry.Wrapping = fyne.TextWrapWord - manualEntry.SetPlaceHolder("Sem lze vložit text PEM pokud QR selže…") - if certValue != "" { - manualEntry.SetText(certValue) - } qrImg := canvas.NewImageFromImage(nil) qrImg.FillMode = canvas.ImageFillContain qrImg.SetMinSize(fyne.NewSize(300, 300)) @@ -597,23 +585,7 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { } } updateQR() - // now that updateQR exists, bind manualEntry changes - manualEntry.OnChanged = func(s string) { - certValue = s - updateQR() - } - pasteText := widget.NewToolbarAction(theme.ContentPasteIcon(), func() { - clip := fyne.CurrentApp().Clipboard().Content() - if strings.TrimSpace(clip) == "" { - parts.showToast("Schránka prázdná") - return - } - certValue = clip - manualEntry.SetText(certValue) - updateQR() - parts.showToast("Vloženo") - }) - pasteQR := widget.NewToolbarAction(theme.ComputerIcon(), func() { + pasteQR := widget.NewToolbarAction(theme.ContentPasteIcon(), func() { img, err := readImageClipboard() if err != nil { parts.showToast("Chyba čtení schránky: " + err.Error()) @@ -636,7 +608,6 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { } if txt2, err2 := DecodeQR(inv); err2 == nil { certValue = txt2 - manualEntry.SetText(certValue) updateQR() parts.showToast("Načteno z invert QR") return @@ -654,7 +625,6 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { return } certValue = txt - manualEntry.SetText(certValue) updateQR() parts.showToast("Načteno z QR") }) @@ -678,37 +648,14 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { return } certValue = txt - manualEntry.SetText(certValue) updateQR() parts.showToast("Načteno z QR") }, win) fd.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg"})) fd.Show() }) - clearAct := widget.NewToolbarAction(theme.ContentClearIcon(), func() { certValue = ""; manualEntry.SetText(""); updateQR() }) - copyAct := widget.NewToolbarAction(theme.ContentCopyIcon(), func() { - if certValue != "" { - copyClip(certValue, parts) - } - }) - saveAct := widget.NewToolbarAction(theme.DocumentSaveIcon(), func() { - if qrImg.Image == nil { - return - } - win := fyne.CurrentApp().Driver().AllWindows()[0] - img := qrImg.Image - fd := dialog.NewFileSave(func(wc fyne.URIWriteCloser, err error) { - if err != nil || wc == nil { - return - } - defer wc.Close() - _ = png.Encode(wc, img) - parts.showToast("QR uložen") - }, win) - fd.SetFileName("contact_qr.png") - fd.Show() - }) - toolbar := widget.NewToolbar(pasteText, pasteQR, openImg, widget.NewToolbarSeparator(), copyAct, saveAct, clearAct) + clearAct := widget.NewToolbarAction(theme.ContentClearIcon(), func() { certValue = ""; updateQR() }) + toolbar := widget.NewToolbar(pasteQR, openImg, clearAct) win := fyne.CurrentApp().Driver().AllWindows()[0] var popup dialog.Dialog save := func(useEncrypt bool) { @@ -744,12 +691,19 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { } } if ask { - dialog.NewCustomConfirm("Common Name", "Použít", "Ponechat", widget.NewLabel(fmt.Sprintf("Common Name: %s\nPoužít jako název?", cn)), func(ok bool) { + entry := widget.NewEntry() + entry.SetText(name) + content := container.NewVBox( + widget.NewLabel(fmt.Sprintf("Common Name nalezen v certifikátu: %s", cn)), + widget.NewLabel("Chcete použít CN jako název, nebo jej upravit?"), + entry, + ) + dialog.NewCustomConfirm("Název kontaktu", "Použít CN", "Uložit", content, func(ok bool) { if ok { proceed(cn) - } else { - proceed(name) + return } + proceed(strings.TrimSpace(entry.Text)) }, win).Show() return } @@ -772,9 +726,8 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { parts.showToast("Smazáno") }, win).Show() }) - useBtn := widget.NewButton("Použít ve Šifrování", func() { save(true) }) saveBtn := widget.NewButton("Uložit", func() { save(false) }) - row := container.NewHBox(saveBtn, useBtn, layout.NewSpacer(), delBtn) + row := container.NewHBox(layout.NewSpacer(), saveBtn, delBtn, layout.NewSpacer()) title := "Nový kontakt" if existing != nil { title = "Upravit kontakt" @@ -783,7 +736,6 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { popup = dialog.NewCustom(title, "Zavřít", container.NewVBox( widget.NewLabel("Název"), nameEntry, widget.NewLabel("Certifikát / Public key (QR)"), toolbar, qrImg, - widget.NewLabel("Text PEM"), manualEntry, widget.NewSeparator(), row), win) popup.Resize(fyne.NewSize(640, 520)) popup.Show() -- 2.45.2