diff options
Diffstat (limited to 'gui/internal/ui/plan.go')
| -rw-r--r-- | gui/internal/ui/plan.go | 128 |
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 = "" + } } |
