prettyview

package module
v0.1.0-alpha Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: BSD-3-Clause Imports: 23 Imported by: 0

README

go-fyne-pretty-view

CI codecov Go Reference Go Report Card Go version License

A memory-efficient, virtualized Fyne widget for viewing structured data — JSON, JSONC, XML, HTML, and raw text — in the style of Bruno's response viewer.

JSON and XML views

Contents

Features

  • Syntax highlighting for JSON / JSONC / XML / HTML, with a dark/light palette you can override.
  • Auto-detection of the input format, with a raw-text fallback for anything else (or malformed input).
  • Expand / fold every container, with a collapse summary on folded nodes ({ 38 items }, [ 3 items ], <tag> 5 children).
  • True character-level free-text selection across rows, with exact-substring copy (Ctrl/Cmd+C) and select-all (Ctrl/Cmd+A).
  • Right-click context menu (Copy / Select all) — the standard Fyne pop-up menu, the same one Fyne's own text widgets use.
  • Copy a whole section (subtree) to the clipboard, regardless of fold state.
  • Search with plain or regular-expression matching, case sensitivity, match navigation, and auto-reveal into folded nodes.
  • Soft word-wrap (toggleable): long lines wrap to the viewport width at word boundaries, or scroll horizontally — selection, search, and copy still operate on whole logical lines.
  • Keyboard navigation: arrows, PageUp/PageDown, Home/End, Esc to clear the selection, Ctrl/Cmd+F to focus search.
  • Optional, à-la-carte controls: a built-in toolbar (Open, format, expand/collapse, wrap, search) you can enable control-by-control — or drive everything from your own widgets via the public API.

Why it stays small

The widget is built around a hard memory bound: only the rows currently visible in the viewport ever exist as live canvas objects. Everything else lives in a compact, pointer-free, struct-of-arrays model, and selection, search and copy all operate on that model rather than on widgets.

Measured on the included fixtures:

Input Visible rows Live row widgets Heap after scrolling the whole file
big.json (7.5 MB) 440,005 31 ~80–90 MB

The parsed model is about 5× the source size (e.g. the ~478 KB openapi.json → ~2.3 MB), and a single multi-megabyte line is horizontally culled so no individual text texture is ever wider than the viewport (without that, Fyne would try to rasterize a ~1 GB bitmap for the line).

Install

go get github.com/ideaconnect/go-fyne-pretty-view

Requires Go 1.26+ and the usual Fyne build dependencies (a C compiler and the OpenGL/X11 headers on Linux).

Quick start

import (
    "fyne.io/fyne/v2/app"
    prettyview "github.com/ideaconnect/go-fyne-pretty-view"
)

func main() {
    a := app.New()
    w := a.NewWindow("viewer")

    pv := prettyview.New()
    pv.SetData(jsonBytes, prettyview.FormatAuto) // or FormatJSON/FormatJSONC/FormatXML/FormatHTML/FormatRaw

    w.SetContent(pv)
    w.ShowAndRun()
}

The widget itself is just the viewer — it has no built-in buttons. Add the optional toolbar (or your own controls) as shown under Controls.

Functionality and how to toggle it

Everything the viewer can do, and how to turn it on or off. The core viewing behaviors are always on; behavior is tuned with construction Options or the matching runtime setters; the on-screen chrome is entirely opt-in.

Viewer behavior
Capability Default How to change it
Syntax highlighting On Always on; recolor with WithTheme / SetTheme (use FormatRaw for plain, unhighlighted text).
Input format Auto-detect WithFormat(f) at build, or SetData(src, f) / Reparse(f) / SetText(s) at runtime.
Expand / fold a node On (click the triangle) Always available; ExpandAll() / CollapseAll() programmatically.
Initial collapse depth Fully expanded (0) WithDefaultCollapseDepth(d) at build, or SetDefaultCollapseDepth(d) at runtime.
Free-text selection & copy On Always on; SelectAll(), SelectedText(), CopySelection(), ClearSelection(), Ctrl/Cmd+A, Ctrl/Cmd+C.
Right-click context menu On Always on (Copy / Select all).
Copy a subtree On demand CopySubtree(byteOffset).
Search On demand Search(SearchQuery{Text, Mode, CaseSensitive}), SearchNext(), SearchPrev(), ClearSearch(), SearchStatus(). Tune with WithSearchConfig(...).
Soft word-wrap Off (WrapNone) WithWrap(WrapWord) at build, or SetWrap(WrapWord) / SetWrap(WrapNone) at runtime; Wrap() reads it.
Tab display width 4 WithTabWidth(n).
Indent step (px/level) 16 WithIndentStep(px).
Theme / colors Track the host Fyne theme WithTheme / WithSyntaxColors at build; SetTheme / SetSyntaxColors at runtime.
Keyboard navigation On Always on (arrows, PageUp/PageDown, Home/End, Esc).

SearchQuery.Mode is SearchPlain (default) or SearchRegex; matches are capped by SearchConfig.MaxMatches (10 000 by default) and revealed even inside folded nodes.

Built-in controls (all opt-in)

The toolbar is assembled from ToolbarConfig — every control is a Show* flag, so you include exactly the ones you want:

Control Flag Notes
Open file ShowOpen Needs Window (built-in file dialog) or OnOpen (your own handler).
Format selector ShowFormat auto / json / jsonc / xml / html / raw; re-parses the current source.
Expand all / Collapse all ShowExpandCollapse
Word-wrap toggle ShowWrap Highlighted while wrapping is on.
Search bar ShowSearch Find box, prev/next, live match counter.
Ctrl/Cmd+F focuses search set Window Registered when a Window is supplied.

Each control is also available à la carte (NewSearchBar, NewFormatSelect, NewFoldButtons, NewWrapToggle) so you can place it anywhere, and host widgets can stay in sync via SetOnSearchChanged and SetOnDataChanged.

Construction options

pv := prettyview.New(
    prettyview.WithFormat(prettyview.FormatJSON),       // skip auto-detect
    prettyview.WithWrap(prettyview.WrapWord),           // soft-wrap long lines (or WrapNone to scroll, default)
    prettyview.WithDefaultCollapseDepth(3),             // auto-collapse below depth 3 on load
    prettyview.WithIndentStep(16),                      // pixels per nesting level
    prettyview.WithTabWidth(4),
    prettyview.WithSearchConfig(prettyview.SearchConfig{MaxMatches: 5000}),
)

NewWithData(src, format, opts...) is the one-shot form that constructs and loads in a single call.

Controls: built-in, your own, or both

The package optionally provides ready-made controls bound to a PrettyView; every control is individually opt-in, so a host app can use the provided ones as-is, disable them and drive the public API from its own widgets, or mix the two.

pv := prettyview.New()

// (a) Drop in the built-in control bar — pick exactly which controls appear.
bar := prettyview.NewToolbar(pv, prettyview.ToolbarConfig{
    ShowOpen:           true,   // "Open…" file dialog (needs Window or OnOpen)
    ShowFormat:         true,   // format selector (re-parses current source)
    ShowExpandCollapse: true,   // Expand all / Collapse all
    ShowWrap:           true,   // soft-wrap toggle
    ShowSearch:         true,   // find box + prev/next + match counter
    Window:             w,      // enables the Open dialog and Ctrl/Cmd+F focus
})
w.SetContent(container.NewBorder(bar, nil, nil, nil, pv))
// (b) Or omit the toolbar and wire your own controls to the public API:
myFind.OnChanged       = func(s string) { pv.Search(prettyview.SearchQuery{Text: s}) }
myExpandButton.OnTapped = pv.ExpandAll

prettyview.DefaultToolbarConfig() returns a config with every control enabled. À-la-carte constructors let you place individual built-ins anywhere: prettyview.NewSearchBar(pv), prettyview.NewFormatSelect(pv), prettyview.NewFoldButtons(pv), prettyview.NewWrapToggle(pv). To keep host controls in sync, register pv.SetOnSearchChanged(fn) (match counter), pv.SetOnDataChanged(fn) (format), and pv.SetOnSearchRequested(fn) (focus the search box, e.g. on Ctrl/Cmd+F).

Note on the file dialog: the built-in Open uses Fyne's own in-canvas file browser, not the OS-native picker (Fyne draws all its UI on the GL canvas). For a platform-native dialog, set ToolbarConfig.OnOpen to your own picker and feed the bytes to pv.SetData.

Key methods

Method Purpose
SetData(src, format) / SetText(s) load content
Reparse(format) / Source() / Format() re-parse the current bytes / read them back / current format
ExpandAll() / CollapseAll() / SetDefaultCollapseDepth(d) fold control
ExpandTo(byteOffset) reveal & scroll to a node
SelectAll() / ClearSelection() / SelectedText() selection
CopySelection() / CopySubtree(byteOffset) clipboard
Search(SearchQuery{...}) / SearchNext() / SearchPrev() / ClearSearch() / SearchStatus() search
SetWrap(WrapWord/WrapNone) / Wrap() soft-wrap long lines to the viewport, or scroll
SetTheme(variant, Theme{...}) / SetSyntaxColors(variant, SyntaxColors{...}) theming (all colors / syntax-only)
SetOnSearchRequested(fn) / SetOnSearchChanged(fn) / SetOnDataChanged(fn) host hooks (focus search, sync counter, sync format)

Theming

The viewer ships a built-in dark/light palette (theme.go), but every color is overridable. The structural colors (foreground, selection, indent guides) default to tracking the host Fyne theme, so an un-themed viewer blends into your app.

import "fyne.io/fyne/v2/theme"

pv := prettyview.New(
    // Override any subset of colors for a variant; nil fields keep the default.
    prettyview.WithTheme(theme.VariantDark, prettyview.Theme{
        Key:         myKeyColor,
        String:      myStringColor,
        Selection:   mySelectionFill,   // free-text selection fill
        Match:       myMatchFill,        // search highlight
        ActiveMatch: myActiveMatchFill,
        IndentGuide: myGuideColor,
    }),
)

// …or just the syntax tokens, or change it at runtime (both compose):
pv.SetSyntaxColors(theme.VariantDark, prettyview.SyntaxColors{Number: myNumberColor})
pv.SetTheme(theme.VariantLight, prettyview.Theme{Selection: myLightSelection})

Theme covers the syntax tokens (Key, String, Number, Bool, Null, Punct, Tag, Attr, Comment) and the structural colors (Foreground, Summary, IndentGuide, Selection, Match, ActiveMatch). SyntaxColors is the token-only shorthand. Overrides merge, so repeated calls accumulate.

Threading

PrettyView follows the usual Fyne widget rule: it is not safe for concurrent use — call its methods (SetData, Search, ExpandAll, the selection and theme mutators, …) on the goroutine that runs the Fyne event loop. To drive it from another goroutine (e.g. after a network fetch), marshal the call with fyne.Do:

go func() {
    data := fetch()
    fyne.Do(func() { pv.SetData(data, prettyview.FormatAuto) })
}()

The widget holds no locks by design; its one internal background task — the search debounce — already marshals back onto the Fyne goroutine.

Demo

go run ./cmd/prettyview-demo               # loads testdata/openapi.json
go run ./cmd/prettyview-demo path/to/file  # or any file

The demo shows both control styles at once: the built-in NewToolbar (Open, format, expand/collapse, wrap, search) used as-is, plus an app-supplied fixture dropdown that drives the public API directly.

Prebuilt binaries are produced by CI for Linux, Windows, and macOS — each is a zip containing the executable alongside the testdata/ fixtures, so the fixture dropdown works as soon as you extract and run it. Tagged versions publish these zips to the project's GitHub Releases page; CI runs also keep them as build artifacts.

Design and documentation

The full, source-grounded architecture (the virtualization invariant, the struct-of-arrays model, the Fenwick fold index, the char-level selection math, and the adversarial risk analysis) lives in docs/DESIGN.md.

File For whom / what
README.md This overview: features, install, usage, API.
STRUCTURE.md The codebase map — every file, the layering, the mental model.
WORKFLOWS.md How to build, run, test, benchmark, and extend (parsers, colors).
docs/DESIGN.md The authoritative architecture + adversarial risk analysis.
docs/PERFORMANCE.md Performance review: hot paths, benchmarks, and the measured deltas.
HUMANS.md Onboarding and contribution guide for people.
AGENTS.md Brief for AI coding agents: invariants to preserve, conventions.
CLAUDE.md Claude Code entry point (points at AGENTS.md).

Contributing

Contributions are welcome — issues and pull requests both. A few things keep the project healthy:

  • Read the briefs first. HUMANS.md is the human onboarding guide; WORKFLOWS.md covers build/run/test/bench and how to add a parser or a color; AGENTS.md lists the non-negotiable invariants.
  • make check must pass. It runs gofmt, go vet (which also forbids internal/ Fyne imports), and go test -race ./.... CI additionally enforces > 90 % coverage, so ship a regression test with each change.
  • Respect the memory invariants. Only viewport-many rows are ever live widgets; selection/search/copy operate on the model, not on widgets; per-row text is horizontally culled. The arena sizes (Node=32 B, Line=24 B, Segment=12 B) are locked by internal/model/sizes_test.go. If a change regresses renderer_test.go or memory_test.go, it's the change that's wrong.
  • See the UI without a display. make shots renders the fixtures to PNGs via Fyne's software painter, so you can verify layout/colors/highlight z-order headlessly.
  • Keep changes milestone-sized and ship the test in the same change.

Sponsorship

This project is maintained on the side and looking for sponsors to keep the modernization moving forward. If your team relies on it, please consider chipping in ❤️ — every contribution helps keep this library alive:

Sponsor on GitHub Buy Me a Coffee

Thank you to everyone who already supports the project! 🙏

Credits

Toolbar glyphs (open, expand/collapse, wrap-text, search, up/down) are from Iconoir by Luca Burgio, used under the MIT License (© 2021 Luca Burgio). The icon files and their license are vendored under icons/iconoir/ (see icons/iconoir/LICENSE). They are recolored to the active theme foreground at build time.

License

Licensed under the BSD 3-Clause License (© 2026 IDCT, Bartosz Pachołek).

Documentation

Overview

Package prettyview provides a memory-efficient, virtualized Fyne widget for viewing structured data — JSON, JSONC, XML, HTML, and raw text — in the style of Bruno's response viewer: syntax highlighting, per-node expand/fold with collapse summaries, copy-subtree, true character-level free-text selection, and incremental search.

The widget is built around a hard memory bound: only the rows currently visible in the viewport ever exist as live fyne.CanvasObjects. Everything else lives in a compact struct-of-arrays document model, and selection, search, and copy all operate on that model rather than on widgets. As a result a multi-megabyte document occupies a small, predictable number of canvas objects regardless of its size.

Threading

PrettyView follows the standard Fyne widget threading model: it is NOT safe for concurrent use. Every method — including the data, fold, selection, and search mutators (SetData, SetText, Reparse, ExpandAll, CollapseAll, ExpandTo, Search, SearchNext/Prev, ClearSearch, SelectAll, ClearSelection, SetTheme, …) — must be called on the goroutine that runs the Fyne event loop (the "main"/Fyne goroutine), exactly like any other Fyne widget. To drive the widget from another goroutine (e.g. after a network fetch), marshal the call with fyne.Do:

go func() {
    data := fetch()
    fyne.Do(func() { pv.SetData(data, prettyview.FormatAuto) })
}()

The widget intentionally holds no locks: all of its state (the document, the fold index, and the selection/search state) is owned by the Fyne goroutine, so single-threaded access is a precondition rather than something guarded at runtime. The only work that runs off that goroutine is the search-debounce timer (time.AfterFunc); it marshals its scan back via fyne.Do and drops superseded scans via a generation counter, so it never touches widget state concurrently.

Index

Constants

View Source
const (
	FormatAuto  = model.FormatAuto  // run AutoDetect heuristics
	FormatRaw   = model.FormatRaw   // plain text, split into physical lines
	FormatJSON  = model.FormatJSON  // strict JSON
	FormatJSONC = model.FormatJSONC // JSON with // and /* */ comments
	FormatXML   = model.FormatXML   // XML
	FormatHTML  = model.FormatHTML  // HTML (tolerant)
)

Variables

This section is empty.

Functions

func NewFoldButtons

func NewFoldButtons(pv *PrettyView) fyne.CanvasObject

NewFoldButtons returns an expand-all / collapse-all icon pair bound to pv.

func NewFormatSelect

func NewFormatSelect(pv *PrettyView) fyne.CanvasObject

NewFormatSelect returns a format selector bound to pv. Choosing a format re-parses the current source; the selection follows the document when content is loaded elsewhere (it registers PrettyView's data-changed hook).

func NewSearchBar

func NewSearchBar(pv *PrettyView) fyne.CanvasObject

NewSearchBar returns a find box (with prev/next and a self-updating match counter) bound to pv. It registers PrettyView's search-changed and search-requested hooks so the counter stays in sync and Ctrl/Cmd+F focuses it.

func NewToolbar

func NewToolbar(pv *PrettyView, cfg ToolbarConfig) fyne.CanvasObject

NewToolbar builds an optional control bar bound to pv from cfg. Disabled controls are omitted. The result is a plain fyne.CanvasObject; place it where you like (typically the top of a container.NewBorder around the PrettyView).

func NewWrapToggle

func NewWrapToggle(pv *PrettyView) fyne.CanvasObject

NewWrapToggle returns a wrap-text icon toggle bound to pv: it flips between soft-wrap (WrapWord) and horizontal scroll (WrapNone), and is highlighted (HighImportance) while wrapping is on so the state is visible.

func ShowOpenDialog

func ShowOpenDialog(pv *PrettyView, win fyne.Window)

ShowOpenDialog opens Fyne's built-in file-open dialog (an in-canvas widget, not the OS-native picker — Fyne draws its own UI) and loads the chosen file into pv, auto-detecting the format. Exposed so hosts can trigger it from their own menu/button. A host that wants the platform-native dialog should instead set ToolbarConfig.OnOpen (or call its own picker) and feed the bytes to pv.SetData.

Types

type Format

type Format = model.Format

Format selects (or, with FormatAuto, detects) the input grammar. It is an alias of the model package's type so the document model and the public API share one type while keeping the model internal.

type Match

type Match struct {
	Line     int32
	ColStart int
	ColEnd   int
}

Match is one search hit, in model coordinates: a stable display-line index and a rune column range into that line's expanded text. Keying by line (not visible row) makes matches survive folding; the visible row is an O(log n) lookup.

type Option

type Option func(*config)

Option customizes a PrettyView at construction time.

func WithDefaultCollapseDepth

func WithDefaultCollapseDepth(d int) Option

WithDefaultCollapseDepth auto-collapses every container deeper than d on load (d <= 0 disables auto-collapse).

func WithFormat

func WithFormat(f Format) Option

WithFormat forces a specific input format, skipping auto-detection.

func WithIndentStep

func WithIndentStep(px float32) Option

WithIndentStep sets the pixels of indentation per nesting level.

func WithSearchConfig

func WithSearchConfig(s SearchConfig) Option

WithSearchConfig overrides the search tuning parameters. The struct is used as given — it replaces the defaults wholesale, not field-by-field. A zero MaxMatches or MinQueryLen still falls back to its default at scan time, but a zero DebounceFor means "no debounce" (scan on every keystroke), NOT 150 ms; set it explicitly to keep keystroke coalescing.

func WithSyntaxColors

func WithSyntaxColors(v fyne.ThemeVariant, s SyntaxColors) Option

WithSyntaxColors overrides just the syntax token colors for a theme variant (shorthand for WithTheme with only the token fields set).

func WithTabWidth

func WithTabWidth(n int) Option

WithTabWidth sets the display width of a tab character (default 4).

func WithTheme

func WithTheme(v fyne.ThemeVariant, t Theme) Option

WithTheme overrides any of the viewer's colors for a theme variant. Nil fields keep their (Fyne-theme-tracking) defaults; calls compose. Pass the variant your app uses (theme.VariantDark / theme.VariantLight), or set both.

func WithWrap

func WithWrap(m WrapMode) Option

WithWrap selects the long-line handling mode (default WrapNone): WrapNone lets long lines overflow and scroll horizontally (matching Bruno), WrapWord soft-wraps them to the viewport width. The mode can also be changed at runtime with SetWrap.

type PrettyView

type PrettyView struct {
	widget.BaseWidget
	// contains filtered or unexported fields
}

PrettyView is the virtualized structured-data viewer widget.

All state is unexported. Construct one with New or NewWithData and feed it content with SetData / SetText.

PrettyView is not safe for concurrent use: call its methods on the Fyne goroutine (see the package doc's Threading section).

func New

func New(opts ...Option) *PrettyView

New constructs an empty PrettyView, applying zero or more Options.

func NewWithData

func NewWithData(src []byte, format Format, opts ...Option) *PrettyView

NewWithData constructs a PrettyView and immediately parses src under format.

func (*PrettyView) ClearSearch

func (pv *PrettyView) ClearSearch()

ClearSearch clears search state and highlights. It also cancels any pending debounced scan so a stale query can't repopulate matches after a clear (this is the path SetData uses, so loading new data drops the old document's pending search too).

func (*PrettyView) ClearSelection

func (pv *PrettyView) ClearSelection()

ClearSelection drops any selection.

func (*PrettyView) CollapseAll

func (pv *PrettyView) CollapseAll()

CollapseAll collapses every node below the top level.

func (*PrettyView) CopySelection

func (pv *PrettyView) CopySelection()

CopySelection copies SelectedText to the clipboard (no-op if empty).

func (*PrettyView) CopySubtree

func (pv *PrettyView) CopySubtree(byteOffset int)

CopySubtree copies the serialized text of the node owning byteOffset (the whole {…}/[…]/<tag>…</tag> span), regardless of fold state.

func (*PrettyView) CreateRenderer

func (pv *PrettyView) CreateRenderer() fyne.WidgetRenderer

CreateRenderer implements fyne.Widget. It builds the scroll + layered content and wires scrolling to the visible-window reflow.

func (*PrettyView) Cursor

func (pv *PrettyView) Cursor() desktop.Cursor

Cursor reports the pointer shape: a pointer over a fold triangle, the text I-beam elsewhere. The over-triangle flag is updated by hover tracking.

func (*PrettyView) DragEnd

func (pv *PrettyView) DragEnd()

func (*PrettyView) Dragged

func (pv *PrettyView) Dragged(ev *fyne.DragEvent)

func (*PrettyView) ExpandAll

func (pv *PrettyView) ExpandAll()

ExpandAll expands every node.

func (*PrettyView) ExpandTo

func (pv *PrettyView) ExpandTo(off int)

ExpandTo expands every collapsed ancestor of the node owning byte offset off (JSON only; XML/HTML lack source offsets) and scrolls it into view.

func (*PrettyView) FocusGained

func (pv *PrettyView) FocusGained()

func (*PrettyView) FocusLost

func (pv *PrettyView) FocusLost()

func (*PrettyView) Format

func (pv *PrettyView) Format() Format

Format reports the format actually used for the current document.

func (*PrettyView) MouseDown

func (pv *PrettyView) MouseDown(ev *desktop.MouseEvent)

func (*PrettyView) MouseIn

func (pv *PrettyView) MouseIn(*desktop.MouseEvent)

func (*PrettyView) MouseMoved

func (pv *PrettyView) MouseMoved(ev *desktop.MouseEvent)

func (*PrettyView) MouseOut

func (pv *PrettyView) MouseOut()

func (*PrettyView) MouseUp

func (pv *PrettyView) MouseUp(*desktop.MouseEvent)

func (*PrettyView) Reparse

func (pv *PrettyView) Reparse(format Format)

Reparse re-parses the current source under a different format (e.g. when a UI lets the user override auto-detection). No-op if no document is loaded.

func (*PrettyView) Search

func (pv *PrettyView) Search(q SearchQuery)

Search starts or replaces the active search and reveals the first match. It runs synchronously and returns once matches are computed.

func (*PrettyView) SearchNext

func (pv *PrettyView) SearchNext()

SearchNext moves to the next match (wrapping) and reveals it.

func (*PrettyView) SearchPrev

func (pv *PrettyView) SearchPrev()

SearchPrev moves to the previous match (wrapping) and reveals it.

func (*PrettyView) SearchStatus

func (pv *PrettyView) SearchStatus() (active, total int, capped bool)

SearchStatus returns the active 1-based index, the total match count, and whether the count was capped.

func (*PrettyView) SelectAll

func (pv *PrettyView) SelectAll()

SelectAll selects the entire (currently visible) document.

func (*PrettyView) SelectedText

func (pv *PrettyView) SelectedText() string

SelectedText returns the exact text currently selected, or "".

func (*PrettyView) SetData

func (pv *PrettyView) SetData(src []byte, format Format)

SetData parses src under format (FormatAuto detects) and refreshes the view. Parsing is synchronous; the model it builds is compact (~5x the source) so this is fast even for multi-megabyte input.

The src slice is retained: the model holds zero-copy byte ranges into it, so callers must not mutate src after this call (copy it first if it may change).

func (*PrettyView) SetDefaultCollapseDepth

func (pv *PrettyView) SetDefaultCollapseDepth(depth int)

SetDefaultCollapseDepth sets the auto-collapse depth applied on subsequent SetData calls (0 disables).

func (*PrettyView) SetOnDataChanged

func (pv *PrettyView) SetOnDataChanged(fn func())

SetOnDataChanged registers a callback invoked whenever the document is replaced (SetData/SetText/Reparse). Use it to keep host controls (such as a format selector) in sync. Setting it replaces any previous callback.

func (*PrettyView) SetOnSearchChanged

func (pv *PrettyView) SetOnSearchChanged(fn func())

SetOnSearchChanged registers a callback invoked whenever the search match set or active match changes. Use it to keep a host match counter in sync. Setting it replaces any previous callback.

func (*PrettyView) SetOnSearchRequested

func (pv *PrettyView) SetOnSearchRequested(fn func())

SetOnSearchRequested registers a callback invoked when the user presses the search shortcut (Ctrl/Cmd+F), so a host can focus its search field.

func (*PrettyView) SetSyntaxColors

func (pv *PrettyView) SetSyntaxColors(variant fyne.ThemeVariant, c SyntaxColors)

SetSyntaxColors overrides just the syntax token colors for a theme variant and refreshes (shorthand for SetTheme with only the token fields set).

func (*PrettyView) SetText

func (pv *PrettyView) SetText(s string)

SetText is shorthand for SetData([]byte(s), FormatAuto).

func (*PrettyView) SetTheme

func (pv *PrettyView) SetTheme(variant fyne.ThemeVariant, t Theme)

SetTheme overrides any of the viewer's colors for a theme variant and refreshes. Nil fields keep their defaults; calls compose with earlier WithTheme/WithSyntaxColors/SetTheme overrides for that variant.

func (*PrettyView) SetWrap

func (pv *PrettyView) SetWrap(mode WrapMode)

SetWrap switches long-line handling between WrapNone (horizontal scroll) and WrapWord (soft-wrap to the viewport width) and refreshes. Wrapping is purely presentational: the model, selection, search, and copy are unchanged — a wrapped line still copies as one logical line.

func (*PrettyView) Source

func (pv *PrettyView) Source() []byte

Source returns the bytes of the current document (the originally supplied input), or nil.

func (*PrettyView) Tapped

func (pv *PrettyView) Tapped(e *fyne.PointEvent)

Tapped toggles a fold when the tap lands on a fold triangle. Other taps are left to the selection layer (M8).

func (*PrettyView) TappedSecondary

func (pv *PrettyView) TappedSecondary(e *fyne.PointEvent)

TappedSecondary shows the context menu at the click. This is the standard Fyne pop-up menu — the same themed overlay Entry and read-only selectable text use for their right-click menus (Fyne draws its own UI on the GL canvas, so there is no native OS menu to invoke). A right-click never disturbs the selection (MouseDown returns early for the secondary button), so "Copy" acts on whatever the user had already highlighted.

func (*PrettyView) TypedKey

func (pv *PrettyView) TypedKey(ev *fyne.KeyEvent)

TypedKey handles Escape (clear selection) and keyboard scrolling/navigation: Up/Down scroll one row, PageUp/PageDown one viewport, Home/End jump to the top/ bottom. A multi-megabyte viewer should be navigable without the mouse.

func (*PrettyView) TypedRune

func (pv *PrettyView) TypedRune(rune)

func (*PrettyView) TypedShortcut

func (pv *PrettyView) TypedShortcut(s fyne.Shortcut)

func (*PrettyView) Wrap

func (pv *PrettyView) Wrap() WrapMode

Wrap reports the current long-line handling mode.

type SearchConfig

type SearchConfig struct {
	MaxMatches  int           // cap on stored matches (default 10_000)
	DebounceFor time.Duration // keystroke debounce (default 150ms)
	MinQueryLen int           // shortest query that triggers a scan (default 1)
}

SearchConfig tunes the incremental search behavior. The scan is synchronous on the Fyne goroutine (debounced, and bounded by MaxMatches); see runSearch.

type SearchMode

type SearchMode uint8

SearchMode selects plain-substring or regular-expression matching.

const (
	SearchPlain SearchMode = iota
	SearchRegex
)

type SearchQuery

type SearchQuery struct {
	Text          string
	Mode          SearchMode
	CaseSensitive bool
}

SearchQuery describes an incremental search request.

type SyntaxColors

type SyntaxColors struct {
	Key     color.Color
	String  color.Color
	Number  color.Color
	Bool    color.Color
	Null    color.Color
	Punct   color.Color
	Tag     color.Color
	Attr    color.Color
	Comment color.Color
}

SyntaxColors is the token-color subset of Theme, kept for the common case of recoloring only the syntax. A nil field keeps the default.

type Theme

type Theme struct {
	// Syntax token colors.
	Key     color.Color
	String  color.Color
	Number  color.Color
	Bool    color.Color
	Null    color.Color
	Punct   color.Color
	Tag     color.Color
	Attr    color.Color
	Comment color.Color

	// Structural / UI colors.
	Foreground  color.Color // default ("plain") text and punctuation fallback
	Summary     color.Color // fold-summary text, e.g. "{ 6 items }"
	IndentGuide color.Color // the vertical indent guide lines
	Selection   color.Color // free-text selection highlight fill
	Match       color.Color // inactive search-match highlight fill
	ActiveMatch color.Color // active search-match highlight fill
}

Theme is the full set of colors the viewer draws. Any nil field falls back to the built-in default for the active theme variant — and the structural defaults (Foreground, Summary, IndentGuide, Selection) themselves follow the host Fyne app theme, so an un-themed PrettyView blends into its surroundings.

Override per variant with WithTheme (construction) or SetTheme (at runtime). Overrides compose: setting only a few fields leaves the rest at their default, and repeated calls / WithSyntaxColors merge rather than replace.

type ToolbarConfig

type ToolbarConfig struct {
	ShowOpen           bool        // an "Open…" button (needs Window or OnOpen)
	ShowFormat         bool        // a format selector (auto/json/xml/html/raw)
	ShowExpandCollapse bool        // Expand all / Collapse all buttons
	ShowWrap           bool        // a wrap-text icon toggle (soft-wrap on/off)
	ShowSearch         bool        // a find box with prev/next and a match counter
	Window             fyne.Window // enables the built-in Open dialog and Ctrl/Cmd+F focus
	OnOpen             func()      // overrides the built-in Open behavior, if set
}

ToolbarConfig selects which built-in controls NewToolbar includes. Each is optional; leave a field false to omit that control and provide your own.

func DefaultToolbarConfig

func DefaultToolbarConfig() ToolbarConfig

DefaultToolbarConfig enables every control.

type WrapMode

type WrapMode uint8

WrapMode controls long-line handling. WrapNone lets long lines overflow and be reached by horizontal scrolling (matching Bruno); WrapWord soft-wraps them to the viewport width (breaking at word boundaries, with a char-break fallback for an unbreakable run). Wrapping is presentational only — selection, search, and copy still operate on whole logical lines.

const (
	WrapNone WrapMode = iota // long lines overflow; horizontal scroll (default)
	WrapWord                 // soft-wrap to the viewport width
)

Directories

Path Synopsis
cmd
prettyview-demo command
Command prettyview-demo exercises the prettyview widget.
Command prettyview-demo exercises the prettyview widget.
internal
geometry
Package geometry holds the integer-rounded layout math that maps between model positions (line, rune column) and content-space pixels.
Package geometry holds the integer-rounded layout math that maps between model positions (line, rune column) and content-space pixels.
parse
Package parse turns raw bytes into a model.Document.
Package parse turns raw bytes into a model.Document.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL