aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/ui/plan.go
diff options
context:
space:
mode:
Diffstat (limited to 'gui/internal/ui/plan.go')
-rw-r--r--gui/internal/ui/plan.go128
1 files changed, 124 insertions, 4 deletions
diff --git a/gui/internal/ui/plan.go b/gui/internal/ui/plan.go
index f6f2952..97d8599 100644
--- a/gui/internal/ui/plan.go
+++ b/gui/internal/ui/plan.go
@@ -5,6 +5,8 @@ package ui
import (
"context"
"fmt"
+ "os"
+ "path/filepath"
"strings"
"github.com/diamondburned/gotk4/pkg/gdk/v4"
@@ -35,8 +37,18 @@ type planView struct {
list *gtk.ListBox
details *gtk.TextView
- menu *gtk.Popover
- menuRow int
+
+ previewNote *gtk.Label
+ picture *gtk.Picture
+ pictureFrame *gtk.ScrolledWindow
+ previewText *gtk.TextView
+ previewScroll *gtk.ScrolledWindow
+ previewTmp string
+ previewFor string
+ previewOff bool
+ startSelected bool
+ menu *gtk.Popover
+ menuRow int
tab *model.PlanTab
cancelOp context.CancelFunc
@@ -102,13 +114,53 @@ func newPlanView(w *Window) *planView {
p.details.SetBottomMargin(6)
detailScroll := gtk.NewScrolledWindow()
detailScroll.SetChild(p.details)
- detailScroll.SetSizeRequest(320, -1)
+ detailScroll.SetVExpand(true)
+ detailScroll.SetSizeRequest(-1, 160)
+
+ // Under the explanation, a look at the file itself: a picture for an
+ // image, the first page for a PDF, the first lines for anything that is
+ // text (his request, 2026-09-16).
+ p.previewNote = gtk.NewLabel("")
+ p.previewNote.SetXAlign(0)
+ p.previewNote.SetMarginStart(8)
+ p.previewNote.SetMarginEnd(8)
+ p.previewNote.SetEllipsize(pango.EllipsizeEnd)
+ p.previewNote.SetMaxWidthChars(20)
+ p.previewNote.AddCSSClass("dim-label")
+ p.picture = gtk.NewPicture()
+ p.picture.SetCanShrink(true)
+ p.picture.SetContentFit(gtk.ContentFitContain)
+ p.picture.SetVisible(false)
+ pictureFrame := gtk.NewScrolledWindow()
+ pictureFrame.SetChild(p.picture)
+ pictureFrame.SetSizeRequest(-1, 280)
+ pictureFrame.SetVisible(false)
+ p.pictureFrame = pictureFrame
+ p.previewText = gtk.NewTextView()
+ p.previewText.SetEditable(false)
+ p.previewText.SetMonospace(true)
+ p.previewText.SetLeftMargin(8)
+ p.previewText.SetRightMargin(8)
+ p.previewText.SetTopMargin(6)
+ previewScroll := gtk.NewScrolledWindow()
+ previewScroll.SetChild(p.previewText)
+ previewScroll.SetSizeRequest(-1, 260)
+ previewScroll.SetVisible(false)
+ p.previewScroll = previewScroll
+
+ detailBox := gtk.NewBox(gtk.OrientationVertical, 0)
+ detailBox.Append(detailScroll)
+ detailBox.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
+ detailBox.Append(p.previewNote)
+ detailBox.Append(pictureFrame)
+ detailBox.Append(previewScroll)
+ detailBox.SetSizeRequest(340, -1)
// A pane the user can drag: on a narrow window the list needs the room,
// on a wide one the explanation does.
panes := gtk.NewPaned(gtk.OrientationHorizontal)
panes.SetStartChild(listScroll)
- panes.SetEndChild(detailScroll)
+ panes.SetEndChild(detailBox)
panes.SetResizeStartChild(true)
panes.SetResizeEndChild(false)
panes.SetShrinkEndChild(false)
@@ -272,6 +324,9 @@ func (p *planView) onScan() {
return
}
p.tab = tab
+ if !p.startSelected {
+ tab.SelectNone()
+ }
p.fillList()
c := tab.Counts
p.w.setStatus("%d scanned, %d to act on, %d excluded, %d skipped, %d with warnings",
@@ -479,4 +534,69 @@ func (p *planView) showDetails(i int) {
b.WriteString("\n" + escape(r.Outcome) + "\n")
}
p.details.Buffer().SetText(b.String())
+ p.showPreview(r.Rel)
+}
+
+// showPreview looks at the file behind row rel, off the main loop: reading
+// it, and rendering a PDF page, takes long enough to stutter the window.
+func (p *planView) showPreview(rel string) {
+ if p.tab == nil || p.previewOff {
+ return
+ }
+ path := filepath.Join(p.tab.Dir.Root, filepath.FromSlash(rel))
+ if p.previewFor == path {
+ return
+ }
+ p.previewFor = path
+ p.pictureFrame.SetVisible(false)
+ p.previewScroll.SetVisible(false)
+ p.previewNote.SetText("looking at " + escape(rel) + "...")
+ if p.previewTmp == "" {
+ dir, err := os.MkdirTemp("", "krino-preview-")
+ if err != nil {
+ p.previewNote.SetText(escape(err.Error()))
+ return
+ }
+ p.previewTmp = dir
+ }
+ var pv model.Preview
+ runInBackground(func(ctx context.Context) error {
+ pv = model.MakePreview(ctx, path, p.previewTmp)
+ return nil
+ }, func(error) {
+ if p.previewFor != path {
+ return // another row was chosen while this one was read
+ }
+ p.previewNote.SetText(escape(pv.Note))
+ switch pv.Kind {
+ case model.PreviewImage:
+ p.picture.SetFilename(pv.Image)
+ p.picture.SetVisible(true)
+ p.pictureFrame.SetVisible(true)
+ case model.PreviewText:
+ p.previewText.Buffer().SetText(escapeText(pv.Text))
+ p.previewScroll.SetVisible(true)
+ }
+ })
+}
+
+// applyPrefs turns the file preview on or off, and says how a fresh plan
+// starts.
+func (p *planView) applyPrefs(prefs model.Prefs) {
+ p.previewOff = !prefs.Preview
+ p.startSelected = prefs.SelectAll
+ if p.previewOff {
+ p.previewFor = ""
+ p.pictureFrame.SetVisible(false)
+ p.previewScroll.SetVisible(false)
+ p.previewNote.SetText("")
+ }
+}
+
+// closePreview removes what the previews left behind.
+func (p *planView) closePreview() {
+ if p.previewTmp != "" {
+ os.RemoveAll(p.previewTmp)
+ p.previewTmp = ""
+ }
}