refactor(ui): move private implementation out of the root package
- commit
- 84c1f246107febed0d595f21fc0901d2285ee23c
- author
- brice · · 2026-09-30 03:50 UTC
- parent
- 068ba85
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
D doc.go +0 -13
| @@ -1,13 +0,0 @@ | |||
| 1 | - | // Package nefergui provides a pure-Go immediate-mode GUI for Wayland, rendered with | |
| 2 | - | // Vulkan and styled with a bounded CSS dialect (see docs/css.md). | |
| 3 | - | // | |
| 4 | - | // Run opens a window and calls the view function to build each frame. The view | |
| 5 | - | // receives a Frame; Frame and its Node handles are valid only during that call. | |
| 6 | - | // Key identifies a child among its siblings across frames; without a key, | |
| 7 | - | // position and type determine identity. ID is CSS metadata, not frame identity. | |
| 8 | - | // Options such as Title, Size, Styles and Transparent configure the window. | |
| 9 | - | // | |
| 10 | - | // Builds support CGO_ENABLED=0. At runtime, NeferGUI requires libvulkan.so.1, | |
| 11 | - | // libxkbcommon.so.0 and a Wayland compositor supporting linux-dmabuf and | |
| 12 | - | // linux-drm-syncobj. See docs/runtime.md and docs/troubleshooting.md. | |
| 13 | - | package nefergui | |
| @@ -1,13 +0,0 @@ | |||||
| 1 | - | // Package nefergui provides a pure-Go immediate-mode GUI for Wayland, rendered with | |||
| 2 | - | // Vulkan and styled with a bounded CSS dialect (see docs/css.md). | |||
| 3 | - | // | |||
| 4 | - | // Run opens a window and calls the view function to build each frame. The view | |||
| 5 | - | // receives a Frame; Frame and its Node handles are valid only during that call. | |||
| 6 | - | // Key identifies a child among its siblings across frames; without a key, | |||
| 7 | - | // position and type determine identity. ID is CSS metadata, not frame identity. | |||
| 8 | - | // Options such as Title, Size, Styles and Transparent configure the window. | |||
| 9 | - | // | |||
| 10 | - | // Builds support CGO_ENABLED=0. At runtime, NeferGUI requires libvulkan.so.1, | |||
| 11 | - | // libxkbcommon.so.0 and a Wayland compositor supporting linux-dmabuf and | |||
| 12 | - | // linux-drm-syncobj. See docs/runtime.md and docs/troubleshooting.md. | |||
| 13 | - | package nefergui | |||
M docs/controls.md +1 -1
| @@ -12,7 +12,7 @@ The pointer cursor follows the CSS `cursor` of the hovered element (`pointer`, ` | |||
| 12 | 12 | ||
| 13 | 13 | `internal/edit` supplies the headless clipboard (`ReadText(limit)`, `WriteText`) and IME (`Enable`, `Disable`, `Surrounding`, `CursorRect`, `ContentType`) ports. A read is limited to 1 MiB and rejected unless UTF-8. IME batches represent preedit, commit and delete-surrounding relative to the UTF-8 cursor, processed at done. The preedit is shaped inline at the model cursor with an underline and its own cursor commands, without changing the model or its normal text run until commit. Password preedit is masked per grapheme before shaping or entering the display list. Ordinary keyboard text is inserted while an IME port is active, except while a preedit is shown; an input method that grabs the keyboard receives its keys instead of the editor. The ports do not negotiate MIME types, own FDs, or provide a Wayland adapter. | |
| 14 | 14 | ||
| 15 | - | Display-list JSON snapshots under `testdata/control-display` cover every control's normal and hover states, interactive focus-visible and disabled states, checkbox/radio checked states, and Input LTR/Arabic selections and preedit. They are deterministic command snapshots, not raster screenshots. Platform `Run` uses the same control tree and display list. | |
| 15 | + | Controls are implemented in `internal/ui`; the root package re-exports their types. Display-list JSON snapshots under `internal/ui/testdata/control-display` cover every control's normal and hover states, interactive focus-visible and disabled states, checkbox/radio checked states, and Input LTR/Arabic selections and preedit. They are deterministic command snapshots, not raster screenshots. Platform `Run` uses the same control tree and display list. | |
| 16 | 16 | ||
| 17 | 17 | The additive UA rules for `checkbox`, `radio`, `slider`, `separator`, `spacer`, and `icon` live in `internal/css/ua.css`. Their dimensions and colors are regular computed CSS and can be overridden by author styles. The control helpers hard-code no paint values; only the indicator painter fixes the shape details listed above. | |
| 18 | 18 | ||
| @@ -12,7 +12,7 @@ The pointer cursor follows the CSS `cursor` of the hovered element (`pointer`, ` | |||||
| 12 | 12 | ||||
| 13 | `internal/edit` supplies the headless clipboard (`ReadText(limit)`, `WriteText`) and IME (`Enable`, `Disable`, `Surrounding`, `CursorRect`, `ContentType`) ports. A read is limited to 1 MiB and rejected unless UTF-8. IME batches represent preedit, commit and delete-surrounding relative to the UTF-8 cursor, processed at done. The preedit is shaped inline at the model cursor with an underline and its own cursor commands, without changing the model or its normal text run until commit. Password preedit is masked per grapheme before shaping or entering the display list. Ordinary keyboard text is inserted while an IME port is active, except while a preedit is shown; an input method that grabs the keyboard receives its keys instead of the editor. The ports do not negotiate MIME types, own FDs, or provide a Wayland adapter. | 13 | `internal/edit` supplies the headless clipboard (`ReadText(limit)`, `WriteText`) and IME (`Enable`, `Disable`, `Surrounding`, `CursorRect`, `ContentType`) ports. A read is limited to 1 MiB and rejected unless UTF-8. IME batches represent preedit, commit and delete-surrounding relative to the UTF-8 cursor, processed at done. The preedit is shaped inline at the model cursor with an underline and its own cursor commands, without changing the model or its normal text run until commit. Password preedit is masked per grapheme before shaping or entering the display list. Ordinary keyboard text is inserted while an IME port is active, except while a preedit is shown; an input method that grabs the keyboard receives its keys instead of the editor. The ports do not negotiate MIME types, own FDs, or provide a Wayland adapter. | ||
| 14 | 14 | ||||
| 15 | - | Display-list JSON snapshots under `testdata/control-display` cover every control's normal and hover states, interactive focus-visible and disabled states, checkbox/radio checked states, and Input LTR/Arabic selections and preedit. They are deterministic command snapshots, not raster screenshots. Platform `Run` uses the same control tree and display list. | 15 | + | Controls are implemented in `internal/ui`; the root package re-exports their types. Display-list JSON snapshots under `internal/ui/testdata/control-display` cover every control's normal and hover states, interactive focus-visible and disabled states, checkbox/radio checked states, and Input LTR/Arabic selections and preedit. They are deterministic command snapshots, not raster screenshots. Platform `Run` uses the same control tree and display list. |
| 16 | 16 | ||||
| 17 | The additive UA rules for `checkbox`, `radio`, `slider`, `separator`, `spacer`, and `icon` live in `internal/css/ua.css`. Their dimensions and colors are regular computed CSS and can be overridden by author styles. The control helpers hard-code no paint values; only the indicator painter fixes the shape details listed above. | 17 | The additive UA rules for `checkbox`, `radio`, `slider`, `separator`, `spacer`, and `icon` live in `internal/css/ua.css`. Their dimensions and colors are regular computed CSS and can be overridden by author styles. The control helpers hard-code no paint values; only the indicator painter fixes the shape details listed above. | ||
| 18 | 18 | ||||
M docs/performance.md +23 -3
| @@ -1,4 +1,6 @@ | |||
| 1 | - | # CPU performance budgets | |
| 1 | + | # Performance | |
| 2 | + | ||
| 3 | + | ## CPU budgets | |
| 2 | 4 | ||
| 3 | 5 | AMD Ryzen 9 7900X3D 12-Core Processor; `CGO_ENABLED=0 GOWORK=off go test -run '^$' -bench . -benchmem -count=5` in the indicated packages. Entries are medians of five runs. Budgets are approximately twice the measured medians (zero allocations remain zero). CPU numbers vary with machine load. The demo-frame benchmark builds a headless home-view tree, computes layout and produces a display list after warmup; Prepare uses a representative warm text/box command mix and excludes GPU submission. | |
| 4 | 6 | ||
| @@ -24,11 +26,29 @@ Frame construction stays low-allocation because: | |||
| 24 | 26 | - `text.Engine.Measure` caches results by text, request and width; entries unused for two frames are evicted and the cache holds at most 4,096 entries (callers treat results as immutable and use `Layout.Clone` before adjusting positions); | |
| 25 | 27 | - identity path keys are computed once per element. | |
| 26 | 28 | ||
| 27 | - | `Run` builds a frame only on input or a redraw request, so an idle window allocates nothing. Profile with: | |
| 29 | + | `Run` builds a frame only on input or a redraw request. Idle frame construction stops, but platform release-wait polling can still allocate small amounts. Profile with: | |
| 28 | 30 | ||
| 29 | 31 | ```sh | |
| 30 | - | go test -run '^$' -bench BenchmarkDemoFrame -benchmem -memprofile mem.out -memprofilerate=1 . | |
| 32 | + | go test -run '^$' -bench BenchmarkDemoFrame -benchmem -memprofile mem.out -memprofilerate=1 ./internal/ui | |
| 31 | 33 | go tool pprof -sample_index=alloc_objects -top -cum mem.out | |
| 32 | 34 | ``` | |
| 33 | 35 | ||
| 34 | 36 | A 400-frame demo harness run (960×640, scale 1, input: name Ada then continue) recorded `submit_to_commit_ns` median **184,545 ns** and nearest-rank p95 **481,550 ns** in `frame-timings.jsonl`. This is host-side submission-to-Wayland-commit latency, not GPU execution time. `atlas_pages` stayed at **1** allocated 1024×1024 grayscale page (maximum 4) for all 400 frames. These figures depend on the compositor, driver and machine load. | |
| 37 | + | ||
| 38 | + | ## Memory as a dependency | |
| 39 | + | ||
| 40 | + | An external Go module with a minimal counter view, a 960×640 window, and a private headless NeferWL compositor was profiled on AMD/RADV. Debug readbacks were disabled. Live Go heap was sampled after forced GC; RSS includes shared driver libraries, while PSS apportions shared pages. | |
| 41 | + | ||
| 42 | + | | Measurement | Minimal view | Demo view | | |
| 43 | + | |---|---:|---:| | |
| 44 | + | | Live Go heap after 1,000 frames | 5.3 MiB | 5.4 MiB | | |
| 45 | + | | Process RSS / PSS after 1,000 frames | 60 / 43 MiB | 64 / 47 MiB | | |
| 46 | + | | Estimated temporary allocations per rendered frame, sampler excluded | about 65 KiB | about 230 KiB | | |
| 47 | + | ||
| 48 | + | The minimal view starts at about 0.75 MiB live Go heap, reaches 5.2 MiB after the first frame, and returns to about 0.9 MiB after closing. No sustained live-heap growth was observed over 1,000–3,000 frames. Driver mappings and Go heap capacity can remain resident after closing; RSS is not a leak measurement on its own. | |
| 49 | + | ||
| 50 | + | DRM counters reported about 41 MiB VRAM and 4 MiB GTT once all three presentation buffers were active. Window dimensions, scale, fonts and driver affect these figures. These are measurements on one stack, not portable limits. | |
| 51 | + | ||
| 52 | + | The largest frame-allocation sources are prepared quads, render batches, and layout painting. Release-wait polling allocated about 150 KiB over a 38-second idle interval. `NEFERGUI_DEBUG_DIR` enables readbacks and PNG work and raised observed peak RSS to about 113 MiB; leave it unset when measuring ordinary application cost. | |
| 53 | + | ||
| 54 | + | Use `pprof`'s `alloc_space` to find cumulative allocation churn and `inuse_space` after GC to inspect retained Go memory. Use `/proc/<pid>/smaps_rollup` for RSS/PSS and DRM fdinfo for GPU counters; heap profiles do not include driver or GPU memory. | |
| @@ -1,4 +1,6 @@ | |||||
| 1 | - | # CPU performance budgets | 1 | + | # Performance |
| 2 | + | ||||
| 3 | + | ## CPU budgets | |||
| 2 | 4 | ||||
| 3 | AMD Ryzen 9 7900X3D 12-Core Processor; `CGO_ENABLED=0 GOWORK=off go test -run '^$' -bench . -benchmem -count=5` in the indicated packages. Entries are medians of five runs. Budgets are approximately twice the measured medians (zero allocations remain zero). CPU numbers vary with machine load. The demo-frame benchmark builds a headless home-view tree, computes layout and produces a display list after warmup; Prepare uses a representative warm text/box command mix and excludes GPU submission. | 5 | AMD Ryzen 9 7900X3D 12-Core Processor; `CGO_ENABLED=0 GOWORK=off go test -run '^$' -bench . -benchmem -count=5` in the indicated packages. Entries are medians of five runs. Budgets are approximately twice the measured medians (zero allocations remain zero). CPU numbers vary with machine load. The demo-frame benchmark builds a headless home-view tree, computes layout and produces a display list after warmup; Prepare uses a representative warm text/box command mix and excludes GPU submission. | ||
| 4 | 6 | ||||
| @@ -24,11 +26,29 @@ Frame construction stays low-allocation because: | |||||
| 24 | - `text.Engine.Measure` caches results by text, request and width; entries unused for two frames are evicted and the cache holds at most 4,096 entries (callers treat results as immutable and use `Layout.Clone` before adjusting positions); | 26 | - `text.Engine.Measure` caches results by text, request and width; entries unused for two frames are evicted and the cache holds at most 4,096 entries (callers treat results as immutable and use `Layout.Clone` before adjusting positions); | ||
| 25 | - identity path keys are computed once per element. | 27 | - identity path keys are computed once per element. | ||
| 26 | 28 | ||||
| 27 | - | `Run` builds a frame only on input or a redraw request, so an idle window allocates nothing. Profile with: | 29 | + | `Run` builds a frame only on input or a redraw request. Idle frame construction stops, but platform release-wait polling can still allocate small amounts. Profile with: |
| 28 | 30 | ||||
| 29 | ```sh | 31 | ```sh | ||
| 30 | - | go test -run '^$' -bench BenchmarkDemoFrame -benchmem -memprofile mem.out -memprofilerate=1 . | 32 | + | go test -run '^$' -bench BenchmarkDemoFrame -benchmem -memprofile mem.out -memprofilerate=1 ./internal/ui |
| 31 | go tool pprof -sample_index=alloc_objects -top -cum mem.out | 33 | go tool pprof -sample_index=alloc_objects -top -cum mem.out | ||
| 32 | ``` | 34 | ``` | ||
| 33 | 35 | ||||
| 34 | A 400-frame demo harness run (960×640, scale 1, input: name Ada then continue) recorded `submit_to_commit_ns` median **184,545 ns** and nearest-rank p95 **481,550 ns** in `frame-timings.jsonl`. This is host-side submission-to-Wayland-commit latency, not GPU execution time. `atlas_pages` stayed at **1** allocated 1024×1024 grayscale page (maximum 4) for all 400 frames. These figures depend on the compositor, driver and machine load. | 36 | A 400-frame demo harness run (960×640, scale 1, input: name Ada then continue) recorded `submit_to_commit_ns` median **184,545 ns** and nearest-rank p95 **481,550 ns** in `frame-timings.jsonl`. This is host-side submission-to-Wayland-commit latency, not GPU execution time. `atlas_pages` stayed at **1** allocated 1024×1024 grayscale page (maximum 4) for all 400 frames. These figures depend on the compositor, driver and machine load. | ||
| 37 | + | ||||
| 38 | + | ## Memory as a dependency | |||
| 39 | + | ||||
| 40 | + | An external Go module with a minimal counter view, a 960×640 window, and a private headless NeferWL compositor was profiled on AMD/RADV. Debug readbacks were disabled. Live Go heap was sampled after forced GC; RSS includes shared driver libraries, while PSS apportions shared pages. | |||
| 41 | + | ||||
| 42 | + | | Measurement | Minimal view | Demo view | | |||
| 43 | + | |---|---:|---:| | |||
| 44 | + | | Live Go heap after 1,000 frames | 5.3 MiB | 5.4 MiB | | |||
| 45 | + | | Process RSS / PSS after 1,000 frames | 60 / 43 MiB | 64 / 47 MiB | | |||
| 46 | + | | Estimated temporary allocations per rendered frame, sampler excluded | about 65 KiB | about 230 KiB | | |||
| 47 | + | ||||
| 48 | + | The minimal view starts at about 0.75 MiB live Go heap, reaches 5.2 MiB after the first frame, and returns to about 0.9 MiB after closing. No sustained live-heap growth was observed over 1,000–3,000 frames. Driver mappings and Go heap capacity can remain resident after closing; RSS is not a leak measurement on its own. | |||
| 49 | + | ||||
| 50 | + | DRM counters reported about 41 MiB VRAM and 4 MiB GTT once all three presentation buffers were active. Window dimensions, scale, fonts and driver affect these figures. These are measurements on one stack, not portable limits. | |||
| 51 | + | ||||
| 52 | + | The largest frame-allocation sources are prepared quads, render batches, and layout painting. Release-wait polling allocated about 150 KiB over a 38-second idle interval. `NEFERGUI_DEBUG_DIR` enables readbacks and PNG work and raised observed peak RSS to about 113 MiB; leave it unset when measuring ordinary application cost. | |||
| 53 | + | ||||
| 54 | + | Use `pprof`'s `alloc_space` to find cumulative allocation churn and `inuse_space` after GC to inspect retained Go memory. Use `/proc/<pid>/smaps_rollup` for RSS/PSS and DRM fdinfo for GPU counters; heap profiles do not include driver or GPU memory. | |||
M example_test.go +20 -0
| @@ -2,10 +2,30 @@ package nefergui_test | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | + | "fmt" | |
| 6 | + | "log" | |
| 5 | 7 | ||
| 6 | 8 | "github.com/bnema/nefergui" | |
| 7 | 9 | ) | |
| 8 | 10 | ||
| 11 | + | // The README counter. Run needs a Wayland session, so this example is | |
| 12 | + | // compiled but not executed by go test. | |
| 13 | + | func Example_counter() { | |
| 14 | + | count := 0 | |
| 15 | + | view := func(f *nefergui.Frame, count *int) { | |
| 16 | + | root := f.Root().Column() | |
| 17 | + | root.Text("Hello, Wayland!") | |
| 18 | + | if root.Button("Count", nefergui.Key("count")).Activated() { | |
| 19 | + | *count++ | |
| 20 | + | } | |
| 21 | + | root.Text(fmt.Sprintf("Clicked %d times", *count)) | |
| 22 | + | } | |
| 23 | + | if err := nefergui.Run(context.Background(), &count, view, | |
| 24 | + | nefergui.Title("Hello"), nefergui.Size(320, 200)); err != nil { | |
| 25 | + | log.Fatal(err) | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | ||
| 9 | 29 | // The product-design example. Run needs a Wayland session, so this example is | |
| 10 | 30 | // compiled but not executed by go test; examples/demo runs it. | |
| 11 | 31 | func Example_compileOnly() { | |
| @@ -2,10 +2,30 @@ package nefergui_test | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | + | "fmt" | |||
| 6 | + | "log" | |||
| 5 | 7 | ||||
| 6 | "github.com/bnema/nefergui" | 8 | "github.com/bnema/nefergui" | ||
| 7 | ) | 9 | ) | ||
| 8 | 10 | ||||
| 11 | + | // The README counter. Run needs a Wayland session, so this example is | |||
| 12 | + | // compiled but not executed by go test. | |||
| 13 | + | func Example_counter() { | |||
| 14 | + | count := 0 | |||
| 15 | + | view := func(f *nefergui.Frame, count *int) { | |||
| 16 | + | root := f.Root().Column() | |||
| 17 | + | root.Text("Hello, Wayland!") | |||
| 18 | + | if root.Button("Count", nefergui.Key("count")).Activated() { | |||
| 19 | + | *count++ | |||
| 20 | + | } | |||
| 21 | + | root.Text(fmt.Sprintf("Clicked %d times", *count)) | |||
| 22 | + | } | |||
| 23 | + | if err := nefergui.Run(context.Background(), &count, view, | |||
| 24 | + | nefergui.Title("Hello"), nefergui.Size(320, 200)); err != nil { | |||
| 25 | + | log.Fatal(err) | |||
| 26 | + | } | |||
| 27 | + | } | |||
| 28 | + | ||||
| 9 | // The product-design example. Run needs a Wayland session, so this example is | 29 | // The product-design example. Run needs a Wayland session, so this example is | ||
| 10 | // compiled but not executed by go test; examples/demo runs it. | 30 | // compiled but not executed by go test; examples/demo runs it. | ||
| 11 | func Example_compileOnly() { | 31 | func Example_compileOnly() { | ||
R controls.go → internal/ui/controls.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
R controls_extra.go → internal/ui/controls_extra.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | vi "github.com/bnema/nefergui/internal/image" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | vi "github.com/bnema/nefergui/internal/image" | 4 | vi "github.com/bnema/nefergui/internal/image" | ||
R controls_extra_test.go → internal/ui/controls_extra_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
R controls_label_test.go → internal/ui/controls_label_test.go +3 -3 renamed, 96% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| @@ -12,7 +12,7 @@ import ( | |||
| 12 | 12 | // glyphs. Snapshots alone once froze labelless controls; this invariant does not | |
| 13 | 13 | // depend on a golden file. | |
| 14 | 14 | func TestDeclaredTextIsPainted(t *testing.T) { | |
| 15 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 15 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 16 | 16 | if err != nil { | |
| 17 | 17 | t.Fatal(err) | |
| 18 | 18 | } | |
| @@ -75,7 +75,7 @@ func TestDeclaredTextIsPainted(t *testing.T) { | |||
| 75 | 75 | // Placeholders follow editor text geometry: text-align and RTL position, | |
| 76 | 76 | // letter-spacing, and textarea wrapping. | |
| 77 | 77 | func TestPlaceholderGeometry(t *testing.T) { | |
| 78 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 78 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 79 | 79 | if err != nil { | |
| 80 | 80 | t.Fatal(err) | |
| 81 | 81 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "testing" | 4 | "testing" | ||
| @@ -12,7 +12,7 @@ import ( | |||||
| 12 | // glyphs. Snapshots alone once froze labelless controls; this invariant does not | 12 | // glyphs. Snapshots alone once froze labelless controls; this invariant does not | ||
| 13 | // depend on a golden file. | 13 | // depend on a golden file. | ||
| 14 | func TestDeclaredTextIsPainted(t *testing.T) { | 14 | func TestDeclaredTextIsPainted(t *testing.T) { | ||
| 15 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 15 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 16 | if err != nil { | 16 | if err != nil { | ||
| 17 | t.Fatal(err) | 17 | t.Fatal(err) | ||
| 18 | } | 18 | } | ||
| @@ -75,7 +75,7 @@ func TestDeclaredTextIsPainted(t *testing.T) { | |||||
| 75 | // Placeholders follow editor text geometry: text-align and RTL position, | 75 | // Placeholders follow editor text geometry: text-align and RTL position, | ||
| 76 | // letter-spacing, and textarea wrapping. | 76 | // letter-spacing, and textarea wrapping. | ||
| 77 | func TestPlaceholderGeometry(t *testing.T) { | 77 | func TestPlaceholderGeometry(t *testing.T) { | ||
| 78 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 78 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 79 | if err != nil { | 79 | if err != nil { | ||
| 80 | t.Fatal(err) | 80 | t.Fatal(err) | ||
| 81 | } | 81 | } | ||
R controls_snapshot_test.go → internal/ui/controls_snapshot_test.go +2 -2 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "bytes" | |
| @@ -17,7 +17,7 @@ import ( | |||
| 17 | 17 | // The golden is the serialized display list, not a rendered image; each case | |
| 18 | 18 | // still checks the intended pseudo-state/event before comparing exact JSON. | |
| 19 | 19 | func TestControlDisplaySnapshots(t *testing.T) { | |
| 20 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 20 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 21 | 21 | if err != nil { | |
| 22 | 22 | t.Fatal(err) | |
| 23 | 23 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "bytes" | 4 | "bytes" | ||
| @@ -17,7 +17,7 @@ import ( | |||||
| 17 | // The golden is the serialized display list, not a rendered image; each case | 17 | // The golden is the serialized display list, not a rendered image; each case | ||
| 18 | // still checks the intended pseudo-state/event before comparing exact JSON. | 18 | // still checks the intended pseudo-state/event before comparing exact JSON. | ||
| 19 | func TestControlDisplaySnapshots(t *testing.T) { | 19 | func TestControlDisplaySnapshots(t *testing.T) { | ||
| 20 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 20 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 21 | if err != nil { | 21 | if err != nil { | ||
| 22 | t.Fatal(err) | 22 | t.Fatal(err) | ||
| 23 | } | 23 | } | ||
R controls_visual_test.go → internal/ui/controls_visual_test.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "encoding/json" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
R debug_off.go → internal/ui/debug_off.go +1 -1 renamed, 77% similar
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | //go:build !nefergui_debug | |
| 2 | 2 | ||
| 3 | - | package nefergui | |
| 3 | + | package ui | |
| 4 | 4 | ||
| 5 | 5 | const debugDiagnostics = false | |
| @@ -1,5 +1,5 @@ | |||||
| 1 | //go:build !nefergui_debug | 1 | //go:build !nefergui_debug | ||
| 2 | 2 | ||||
| 3 | - | package nefergui | 3 | + | package ui |
| 4 | 4 | ||||
| 5 | const debugDiagnostics = false | 5 | const debugDiagnostics = false | ||
R debug_on.go → internal/ui/debug_on.go +1 -1 renamed, 77% similar
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | //go:build nefergui_debug | |
| 2 | 2 | ||
| 3 | - | package nefergui | |
| 3 | + | package ui | |
| 4 | 4 | ||
| 5 | 5 | const debugDiagnostics = true | |
| @@ -1,5 +1,5 @@ | |||||
| 1 | //go:build nefergui_debug | 1 | //go:build nefergui_debug | ||
| 2 | 2 | ||||
| 3 | - | package nefergui | 3 | + | package ui |
| 4 | 4 | ||||
| 5 | const debugDiagnostics = true | 5 | const debugDiagnostics = true | ||
R edit_geometry.go → internal/ui/edit_geometry.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R edit_geometry_review_test.go → internal/ui/edit_geometry_review_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R edit_geometry_test.go → internal/ui/edit_geometry_test.go +2 -2 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "bytes" | |
| @@ -17,7 +17,7 @@ import ( | |||
| 17 | 17 | ||
| 18 | 18 | func geometryRuntime(t *testing.T, sheet string) *runtime { | |
| 19 | 19 | t.Helper() | |
| 20 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 20 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 21 | 21 | if err != nil { | |
| 22 | 22 | t.Fatal(err) | |
| 23 | 23 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "bytes" | 4 | "bytes" | ||
| @@ -17,7 +17,7 @@ import ( | |||||
| 17 | 17 | ||||
| 18 | func geometryRuntime(t *testing.T, sheet string) *runtime { | 18 | func geometryRuntime(t *testing.T, sheet string) *runtime { | ||
| 19 | t.Helper() | 19 | t.Helper() | ||
| 20 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 20 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 21 | if err != nil { | 21 | if err != nil { | ||
| 22 | t.Fatal(err) | 22 | t.Fatal(err) | ||
| 23 | } | 23 | } | ||
R edit_mixed_direction_test.go → internal/ui/edit_mixed_direction_test.go +2 -2 renamed, 96% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -11,7 +11,7 @@ import ( | |||
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestMixedDirectionSelectionVisualRects(t *testing.T) { | |
| 14 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 14 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 15 | 15 | if err != nil { | |
| 16 | 16 | t.Fatal(err) | |
| 17 | 17 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
| @@ -11,7 +11,7 @@ import ( | |||||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | func TestMixedDirectionSelectionVisualRects(t *testing.T) { | 13 | func TestMixedDirectionSelectionVisualRects(t *testing.T) { | ||
| 14 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 14 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 15 | if err != nil { | 15 | if err != nil { | ||
| 16 | t.Fatal(err) | 16 | t.Fatal(err) | ||
| 17 | } | 17 | } | ||
R edit_navigation.go → internal/ui/edit_navigation.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R edit_navigation_test.go → internal/ui/edit_navigation_test.go +2 -2 renamed, 95% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -7,7 +7,7 @@ import ( | |||
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | 9 | func TestWrappedNavigationAndScroll(t *testing.T) { | |
| 10 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 10 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 11 | 11 | if err != nil { | |
| 12 | 12 | t.Fatal(err) | |
| 13 | 13 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
| @@ -7,7 +7,7 @@ import ( | |||||
| 7 | ) | 7 | ) | ||
| 8 | 8 | ||||
| 9 | func TestWrappedNavigationAndScroll(t *testing.T) { | 9 | func TestWrappedNavigationAndScroll(t *testing.T) { | ||
| 10 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 10 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 11 | if err != nil { | 11 | if err != nil { | ||
| 12 | t.Fatal(err) | 12 | t.Fatal(err) | ||
| 13 | } | 13 | } | ||
R edit_paint.go → internal/ui/edit_paint.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R edit_paint_test.go → internal/ui/edit_paint_test.go +2 -2 renamed, 95% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestEditorVisualSelectionAndCaret(t *testing.T) { | |
| 11 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 11 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 12 | 12 | if err != nil { | |
| 13 | 13 | t.Fatal(err) | |
| 14 | 14 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
| @@ -8,7 +8,7 @@ import ( | |||||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | func TestEditorVisualSelectionAndCaret(t *testing.T) { | 10 | func TestEditorVisualSelectionAndCaret(t *testing.T) { | ||
| 11 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 11 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 12 | if err != nil { | 12 | if err != nil { | ||
| 13 | t.Fatal(err) | 13 | t.Fatal(err) | ||
| 14 | } | 14 | } | ||
R edit_pointer_test.go → internal/ui/edit_pointer_test.go +2 -2 renamed, 96% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| @@ -9,7 +9,7 @@ import ( | |||
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | func TestPointerTextSelection(t *testing.T) { | |
| 12 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 12 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 13 | 13 | if err != nil { | |
| 14 | 14 | t.Fatal(err) | |
| 15 | 15 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "testing" | 4 | "testing" | ||
| @@ -9,7 +9,7 @@ import ( | |||||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | func TestPointerTextSelection(t *testing.T) { | 11 | func TestPointerTextSelection(t *testing.T) { | ||
| 12 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 12 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 13 | if err != nil { | 13 | if err != nil { | ||
| 14 | t.Fatal(err) | 14 | t.Fatal(err) | ||
| 15 | } | 15 | } | ||
R edit_preedit.go → internal/ui/edit_preedit.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R edit_preedit_password_test.go → internal/ui/edit_preedit_password_test.go +2 -2 renamed, 93% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "bytes" | |
| @@ -11,7 +11,7 @@ import ( | |||
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | 13 | func TestPasswordPreeditNeverLeaksIntoDisplay(t *testing.T) { | |
| 14 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 14 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 15 | 15 | if err != nil { | |
| 16 | 16 | t.Fatal(err) | |
| 17 | 17 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "bytes" | 4 | "bytes" | ||
| @@ -11,7 +11,7 @@ import ( | |||||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | func TestPasswordPreeditNeverLeaksIntoDisplay(t *testing.T) { | 13 | func TestPasswordPreeditNeverLeaksIntoDisplay(t *testing.T) { | ||
| 14 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 14 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 15 | if err != nil { | 15 | if err != nil { | ||
| 16 | t.Fatal(err) | 16 | t.Fatal(err) | ||
| 17 | } | 17 | } | ||
R edit_preedit_test.go → internal/ui/edit_preedit_test.go +2 -2 renamed, 94% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestPreeditInlineWithoutModelMutation(t *testing.T) { | |
| 11 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 11 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 12 | 12 | if err != nil { | |
| 13 | 13 | t.Fatal(err) | |
| 14 | 14 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
| @@ -8,7 +8,7 @@ import ( | |||||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | func TestPreeditInlineWithoutModelMutation(t *testing.T) { | 10 | func TestPreeditInlineWithoutModelMutation(t *testing.T) { | ||
| 11 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 11 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 12 | if err != nil { | 12 | if err != nil { | ||
| 13 | t.Fatal(err) | 13 | t.Fatal(err) | ||
| 14 | } | 14 | } | ||
R edit_scroll.go → internal/ui/edit_scroll.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R ime_focus_test.go → internal/ui/ime_focus_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "reflect" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "reflect" | 4 | "reflect" | ||
R ime_return_test.go → internal/ui/ime_return_test.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "testing" | 4 | "testing" | ||
R indicator_paint.go → internal/ui/indicator_paint.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R indicator_paint_test.go → internal/ui/indicator_paint_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R interaction.go → internal/ui/interaction.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R interaction_test.go → internal/ui/interaction_test.go +2 -2 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "encoding/json" | |
| @@ -27,7 +27,7 @@ func shell(r *runtime, activated *int) func(*Frame) { | |||
| 27 | 27 | } | |
| 28 | 28 | func TestShellStyleAndFocus(t *testing.T) { | |
| 29 | 29 | r := newRuntime() | |
| 30 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 30 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 31 | 31 | if err != nil { | |
| 32 | 32 | t.Fatal(err) | |
| 33 | 33 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
| @@ -27,7 +27,7 @@ func shell(r *runtime, activated *int) func(*Frame) { | |||||
| 27 | } | 27 | } | ||
| 28 | func TestShellStyleAndFocus(t *testing.T) { | 28 | func TestShellStyleAndFocus(t *testing.T) { | ||
| 29 | r := newRuntime() | 29 | r := newRuntime() | ||
| 30 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 30 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 31 | if err != nil { | 31 | if err != nil { | ||
| 32 | t.Fatal(err) | 32 | t.Fatal(err) | ||
| 33 | } | 33 | } | ||
R options.go → internal/ui/options.go +26 -22 renamed, 51% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import "github.com/bnema/nefergui/internal/css" | |
| 4 | 4 | ||
| @@ -9,21 +9,24 @@ type EditOption interface{ edit(*element) } | |||
| 9 | 9 | ||
| 10 | 10 | // ValueOption accepts CSS options and Disabled for pointer-backed controls. | |
| 11 | 11 | type ValueOption interface{ valueOption(*element) } | |
| 12 | - | type commonOption struct{ apply func(*element) } | |
| 13 | - | ||
| 14 | - | func (o commonOption) container(e *element) { o.apply(e) } | |
| 15 | - | func (o commonOption) button(e *element) { o.apply(e) } | |
| 16 | - | func (o commonOption) edit(e *element) { o.apply(e) } | |
| 17 | - | func (o commonOption) valueOption(e *element) { o.apply(e) } | |
| 18 | - | func Key(key string) commonOption { | |
| 19 | - | return commonOption{func(e *element) { e.identity = &identity{key: key} }} | |
| 12 | + | ||
| 13 | + | // CommonOption is the concrete type of Key, ID, Class and Inline; it belongs | |
| 14 | + | // to every option family. It is exported only so the root facade can return it. | |
| 15 | + | type CommonOption struct{ apply func(*element) } | |
| 16 | + | ||
| 17 | + | func (o CommonOption) container(e *element) { o.apply(e) } | |
| 18 | + | func (o CommonOption) button(e *element) { o.apply(e) } | |
| 19 | + | func (o CommonOption) edit(e *element) { o.apply(e) } | |
| 20 | + | func (o CommonOption) valueOption(e *element) { o.apply(e) } | |
| 21 | + | func Key(key string) CommonOption { | |
| 22 | + | return CommonOption{func(e *element) { e.identity = &identity{key: key} }} | |
| 20 | 23 | } | |
| 21 | - | func ID(id string) commonOption { return commonOption{func(e *element) { e.id = id }} } | |
| 22 | - | func Class(class string) commonOption { | |
| 23 | - | return commonOption{func(e *element) { e.classes = append(e.classes, class) }} | |
| 24 | + | func ID(id string) CommonOption { return CommonOption{func(e *element) { e.id = id }} } | |
| 25 | + | func Class(class string) CommonOption { | |
| 26 | + | return CommonOption{func(e *element) { e.classes = append(e.classes, class) }} | |
| 24 | 27 | } | |
| 25 | - | func Inline(src string) commonOption { | |
| 26 | - | return commonOption{func(e *element) { e.inline, _ = css.ParseInline(src) }} | |
| 28 | + | func Inline(src string) CommonOption { | |
| 29 | + | return CommonOption{func(e *element) { e.inline, _ = css.ParseInline(src) }} | |
| 27 | 30 | } | |
| 28 | 31 | func applyContainer(e *element, opts []ContainerOption) { | |
| 29 | 32 | for _, o := range opts { | |
| @@ -31,21 +34,22 @@ func applyContainer(e *element, opts []ContainerOption) { | |||
| 31 | 34 | } | |
| 32 | 35 | } | |
| 33 | 36 | ||
| 34 | - | // disabledOption applies to every interactive control (buttons, value controls | |
| 35 | - | // and editors) but not to containers or headings. | |
| 36 | - | type disabledOption struct{ disabled bool } | |
| 37 | + | // DisabledOption is the concrete type of Disabled. It applies to every | |
| 38 | + | // interactive control (buttons, value controls and editors) but not to | |
| 39 | + | // containers or headings. Exported only so the root facade can return it. | |
| 40 | + | type DisabledOption struct{ disabled bool } | |
| 37 | 41 | ||
| 38 | - | func (o disabledOption) button(e *element) { e.disabled = o.disabled } | |
| 39 | - | func (o disabledOption) edit(e *element) { e.disabled = o.disabled } | |
| 40 | - | func (o disabledOption) valueOption(e *element) { e.disabled = o.disabled } | |
| 42 | + | func (o DisabledOption) button(e *element) { e.disabled = o.disabled } | |
| 43 | + | func (o DisabledOption) edit(e *element) { e.disabled = o.disabled } | |
| 44 | + | func (o DisabledOption) valueOption(e *element) { e.disabled = o.disabled } | |
| 41 | 45 | ||
| 42 | 46 | // Disabled blocks focus and interaction on an interactive control. | |
| 43 | - | func Disabled(v bool) disabledOption { return disabledOption{v} } | |
| 47 | + | func Disabled(v bool) DisabledOption { return DisabledOption{v} } | |
| 44 | 48 | ||
| 45 | 49 | // HeadingOption accepts common CSS options and Level, but not control options. | |
| 46 | 50 | type HeadingOption interface{ heading(*element) } | |
| 47 | 51 | ||
| 48 | - | func (o commonOption) heading(e *element) { o.apply(e) } | |
| 52 | + | func (o CommonOption) heading(e *element) { o.apply(e) } | |
| 49 | 53 | ||
| 50 | 54 | type headingLevel int | |
| 51 | 55 | ||
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import "github.com/bnema/nefergui/internal/css" | 3 | import "github.com/bnema/nefergui/internal/css" | ||
| 4 | 4 | ||||
| @@ -9,21 +9,24 @@ type EditOption interface{ edit(*element) } | |||||
| 9 | 9 | ||||
| 10 | // ValueOption accepts CSS options and Disabled for pointer-backed controls. | 10 | // ValueOption accepts CSS options and Disabled for pointer-backed controls. | ||
| 11 | type ValueOption interface{ valueOption(*element) } | 11 | type ValueOption interface{ valueOption(*element) } | ||
| 12 | - | type commonOption struct{ apply func(*element) } | 12 | + | |
| 13 | - | 13 | + | // CommonOption is the concrete type of Key, ID, Class and Inline; it belongs | |
| 14 | - | func (o commonOption) container(e *element) { o.apply(e) } | 14 | + | // to every option family. It is exported only so the root facade can return it. |
| 15 | - | func (o commonOption) button(e *element) { o.apply(e) } | 15 | + | type CommonOption struct{ apply func(*element) } |
| 16 | - | func (o commonOption) edit(e *element) { o.apply(e) } | 16 | + | |
| 17 | - | func (o commonOption) valueOption(e *element) { o.apply(e) } | 17 | + | func (o CommonOption) container(e *element) { o.apply(e) } |
| 18 | - | func Key(key string) commonOption { | 18 | + | func (o CommonOption) button(e *element) { o.apply(e) } |
| 19 | - | return commonOption{func(e *element) { e.identity = &identity{key: key} }} | 19 | + | func (o CommonOption) edit(e *element) { o.apply(e) } |
| 20 | + | func (o CommonOption) valueOption(e *element) { o.apply(e) } | |||
| 21 | + | func Key(key string) CommonOption { | |||
| 22 | + | return CommonOption{func(e *element) { e.identity = &identity{key: key} }} | |||
| 20 | } | 23 | } | ||
| 21 | - | func ID(id string) commonOption { return commonOption{func(e *element) { e.id = id }} } | 24 | + | func ID(id string) CommonOption { return CommonOption{func(e *element) { e.id = id }} } |
| 22 | - | func Class(class string) commonOption { | 25 | + | func Class(class string) CommonOption { |
| 23 | - | return commonOption{func(e *element) { e.classes = append(e.classes, class) }} | 26 | + | return CommonOption{func(e *element) { e.classes = append(e.classes, class) }} |
| 24 | } | 27 | } | ||
| 25 | - | func Inline(src string) commonOption { | 28 | + | func Inline(src string) CommonOption { |
| 26 | - | return commonOption{func(e *element) { e.inline, _ = css.ParseInline(src) }} | 29 | + | return CommonOption{func(e *element) { e.inline, _ = css.ParseInline(src) }} |
| 27 | } | 30 | } | ||
| 28 | func applyContainer(e *element, opts []ContainerOption) { | 31 | func applyContainer(e *element, opts []ContainerOption) { | ||
| 29 | for _, o := range opts { | 32 | for _, o := range opts { | ||
| @@ -31,21 +34,22 @@ func applyContainer(e *element, opts []ContainerOption) { | |||||
| 31 | } | 34 | } | ||
| 32 | } | 35 | } | ||
| 33 | 36 | ||||
| 34 | - | // disabledOption applies to every interactive control (buttons, value controls | 37 | + | // DisabledOption is the concrete type of Disabled. It applies to every |
| 35 | - | // and editors) but not to containers or headings. | 38 | + | // interactive control (buttons, value controls and editors) but not to |
| 36 | - | type disabledOption struct{ disabled bool } | 39 | + | // containers or headings. Exported only so the root facade can return it. |
| 40 | + | type DisabledOption struct{ disabled bool } | |||
| 37 | 41 | ||||
| 38 | - | func (o disabledOption) button(e *element) { e.disabled = o.disabled } | 42 | + | func (o DisabledOption) button(e *element) { e.disabled = o.disabled } |
| 39 | - | func (o disabledOption) edit(e *element) { e.disabled = o.disabled } | 43 | + | func (o DisabledOption) edit(e *element) { e.disabled = o.disabled } |
| 40 | - | func (o disabledOption) valueOption(e *element) { e.disabled = o.disabled } | 44 | + | func (o DisabledOption) valueOption(e *element) { e.disabled = o.disabled } |
| 41 | 45 | ||||
| 42 | // Disabled blocks focus and interaction on an interactive control. | 46 | // Disabled blocks focus and interaction on an interactive control. | ||
| 43 | - | func Disabled(v bool) disabledOption { return disabledOption{v} } | 47 | + | func Disabled(v bool) DisabledOption { return DisabledOption{v} } |
| 44 | 48 | ||||
| 45 | // HeadingOption accepts common CSS options and Level, but not control options. | 49 | // HeadingOption accepts common CSS options and Level, but not control options. | ||
| 46 | type HeadingOption interface{ heading(*element) } | 50 | type HeadingOption interface{ heading(*element) } | ||
| 47 | 51 | ||||
| 48 | - | func (o commonOption) heading(e *element) { o.apply(e) } | 52 | + | func (o CommonOption) heading(e *element) { o.apply(e) } |
| 49 | 53 | ||||
| 50 | type headingLevel int | 54 | type headingLevel int | ||
| 51 | 55 | ||||
R paste.go → internal/ui/paste.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
R paste_test.go → internal/ui/paste_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
R performance_test.go → internal/ui/performance_test.go +2 -2 renamed, 95% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| @@ -28,7 +28,7 @@ func demoFrame(f *Frame) { | |||
| 28 | 28 | ||
| 29 | 29 | func demoRuntime(tb testing.TB) *runtime { | |
| 30 | 30 | tb.Helper() | |
| 31 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | |
| 31 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) | |
| 32 | 32 | if err != nil { | |
| 33 | 33 | tb.Fatal(err) | |
| 34 | 34 | } | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "testing" | 4 | "testing" | ||
| @@ -28,7 +28,7 @@ func demoFrame(f *Frame) { | |||||
| 28 | 28 | ||||
| 29 | func demoRuntime(tb testing.TB) *runtime { | 29 | func demoRuntime(tb testing.TB) *runtime { | ||
| 30 | tb.Helper() | 30 | tb.Helper() | ||
| 31 | - | catalog, err := text.Load(text.DirectorySource("testdata/fonts")) | 31 | + | catalog, err := text.Load(text.DirectorySource("../../testdata/fonts")) |
| 32 | if err != nil { | 32 | if err != nil { | ||
| 33 | tb.Fatal(err) | 33 | tb.Fatal(err) | ||
| 34 | } | 34 | } | ||
R platform.go → internal/ui/platform.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
R runtime.go → internal/ui/runtime.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
R runtime_test.go → internal/ui/runtime_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
R slider_grid_test.go → internal/ui/slider_grid_test.go +1 -1 renamed, 99% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "math" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "math" | 4 | "math" | ||
R slider_pointer_test.go → internal/ui/slider_pointer_test.go +1 -1 renamed, 98% similar
| @@ -1,4 +1,4 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | package ui | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/nefergui/internal/css" | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | package nefergui | 1 | + | package ui |
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/nefergui/internal/css" | 4 | "github.com/bnema/nefergui/internal/css" | ||
R testdata/control-display/button_disabled.json → internal/ui/testdata/control-display/button_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/button_focus-visible.json → internal/ui/testdata/control-display/button_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/button_hover.json → internal/ui/testdata/control-display/button_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/button_normal.json → internal/ui/testdata/control-display/button_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/checkbox_checked.json → internal/ui/testdata/control-display/checkbox_checked.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/checkbox_disabled.json → internal/ui/testdata/control-display/checkbox_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/checkbox_focus-visible.json → internal/ui/testdata/control-display/checkbox_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/checkbox_hover.json → internal/ui/testdata/control-display/checkbox_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/checkbox_normal.json → internal/ui/testdata/control-display/checkbox_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/heading_hover.json → internal/ui/testdata/control-display/heading_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/heading_normal.json → internal/ui/testdata/control-display/heading_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/icon_hover.json → internal/ui/testdata/control-display/icon_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/icon_normal.json → internal/ui/testdata/control-display/icon_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/image_hover.json → internal/ui/testdata/control-display/image_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/image_normal.json → internal/ui/testdata/control-display/image_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_disabled.json → internal/ui/testdata/control-display/input_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_focus-visible.json → internal/ui/testdata/control-display/input_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_hover.json → internal/ui/testdata/control-display/input_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_normal.json → internal/ui/testdata/control-display/input_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_preedit.json → internal/ui/testdata/control-display/input_preedit.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_selection-arabic.json → internal/ui/testdata/control-display/input_selection-arabic.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/input_selection-ltr.json → internal/ui/testdata/control-display/input_selection-ltr.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/radio_checked.json → internal/ui/testdata/control-display/radio_checked.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/radio_disabled.json → internal/ui/testdata/control-display/radio_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/radio_focus-visible.json → internal/ui/testdata/control-display/radio_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/radio_hover.json → internal/ui/testdata/control-display/radio_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/radio_normal.json → internal/ui/testdata/control-display/radio_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/separator_hover.json → internal/ui/testdata/control-display/separator_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/separator_normal.json → internal/ui/testdata/control-display/separator_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/slider_disabled.json → internal/ui/testdata/control-display/slider_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/slider_focus-visible.json → internal/ui/testdata/control-display/slider_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/slider_hover.json → internal/ui/testdata/control-display/slider_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/slider_normal.json → internal/ui/testdata/control-display/slider_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/spacer_hover.json → internal/ui/testdata/control-display/spacer_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/spacer_normal.json → internal/ui/testdata/control-display/spacer_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/text_hover.json → internal/ui/testdata/control-display/text_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/text_normal.json → internal/ui/testdata/control-display/text_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/textarea_disabled.json → internal/ui/testdata/control-display/textarea_disabled.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/textarea_focus-visible.json → internal/ui/testdata/control-display/textarea_focus-visible.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/textarea_hover.json → internal/ui/testdata/control-display/textarea_hover.json +0 -0 renamed, 100% similar
renamed, 100% similar
R testdata/control-display/textarea_normal.json → internal/ui/testdata/control-display/textarea_normal.json +0 -0 renamed, 100% similar
renamed, 100% similar
R tree.go → internal/ui/tree.go +3 -1 renamed, 97% similar
| @@ -1,4 +1,6 @@ | |||
| 1 | - | package nefergui | |
| 1 | + | // Package ui owns the immediate-mode frame tree, controls, editors, input | |
| 2 | + | // routing and the Wayland run loop behind the public nefergui facade. | |
| 3 | + | package ui | |
| 2 | 4 | ||
| 3 | 5 | import ( | |
| 4 | 6 | "fmt" | |
| @@ -1,4 +1,6 @@ | |||||
| 1 | - | package nefergui | 1 | + | // Package ui owns the immediate-mode frame tree, controls, editors, input |
| 2 | + | // routing and the Wayland run loop behind the public nefergui facade. | |||
| 3 | + | package ui | |||
| 2 | 4 | ||||
| 3 | import ( | 5 | import ( | ||
| 4 | "fmt" | 6 | "fmt" | ||
A nefergui.go +108 -0
| @@ -0,0 +1,108 @@ | |||
| 1 | + | // Package nefergui provides a pure-Go immediate-mode GUI for Wayland, rendered with | |
| 2 | + | // Vulkan and styled with a bounded CSS dialect (see docs/css.md). | |
| 3 | + | // | |
| 4 | + | // Run opens a window and calls the view function to build each frame. The view | |
| 5 | + | // receives a Frame; Frame and its Node handles are valid only during that call. | |
| 6 | + | // Key identifies a child among its siblings across frames; without a key, | |
| 7 | + | // position and type determine identity. ID is CSS metadata, not frame identity. | |
| 8 | + | // Options such as Title, Size, Styles and Transparent configure the window. | |
| 9 | + | // | |
| 10 | + | // The frame tree, controls, editors and input routing are implemented in | |
| 11 | + | // internal/ui; this package is the public facade and re-exports those types. | |
| 12 | + | // | |
| 13 | + | // Builds support CGO_ENABLED=0. At runtime, NeferGUI requires libvulkan.so.1, | |
| 14 | + | // libxkbcommon.so.0 and a Wayland compositor supporting linux-dmabuf and | |
| 15 | + | // linux-drm-syncobj. See docs/runtime.md and docs/troubleshooting.md. | |
| 16 | + | package nefergui | |
| 17 | + | ||
| 18 | + | import ( | |
| 19 | + | "context" | |
| 20 | + | ||
| 21 | + | "github.com/bnema/nefergui/internal/ui" | |
| 22 | + | ) | |
| 23 | + | ||
| 24 | + | // Frame is valid only during one call to the view function. Root builds the | |
| 25 | + | // application element; Diagnostics reports identity problems in debug builds. | |
| 26 | + | type Frame = ui.Frame | |
| 27 | + | ||
| 28 | + | // Node is an ephemeral handle into the current frame. Do not retain it between | |
| 29 | + | // frames. Container methods (Box, Row, Column, Stack, Scroll, Header, Nav, | |
| 30 | + | // Main, Section, Aside, Footer, Element) return child nodes; control methods | |
| 31 | + | // (Button, Checkbox, Radio, Slider, Input, Textarea, Text, Heading, Image, | |
| 32 | + | // Icon, Separator, Spacer) declare controls. Interactive controls return event | |
| 33 | + | // snapshots. | |
| 34 | + | type Node = ui.Node | |
| 35 | + | ||
| 36 | + | // Control event values are immutable snapshots; querying them never consumes events. | |
| 37 | + | type ( | |
| 38 | + | ButtonEvent = ui.ButtonEvent | |
| 39 | + | EditEvent = ui.EditEvent | |
| 40 | + | ChangeEvent = ui.ChangeEvent | |
| 41 | + | ) | |
| 42 | + | ||
| 43 | + | // Option families are sealed: ContainerOption, ButtonOption, EditOption, | |
| 44 | + | // ValueOption and HeadingOption accept the common CSS options (Key, ID, | |
| 45 | + | // Class, Inline) plus the control-specific options listed on each function. | |
| 46 | + | type ( | |
| 47 | + | ContainerOption = ui.ContainerOption | |
| 48 | + | ButtonOption = ui.ButtonOption | |
| 49 | + | EditOption = ui.EditOption | |
| 50 | + | ValueOption = ui.ValueOption | |
| 51 | + | HeadingOption = ui.HeadingOption | |
| 52 | + | WindowOption = ui.WindowOption | |
| 53 | + | ) | |
| 54 | + | ||
| 55 | + | // commonOption belongs to every option family; disabledOption to every | |
| 56 | + | // interactive control family. Both are concrete internal types, aliased so | |
| 57 | + | // the constructors below can return them without a second implementation. | |
| 58 | + | type ( | |
| 59 | + | commonOption = ui.CommonOption | |
| 60 | + | disabledOption = ui.DisabledOption | |
| 61 | + | ) | |
| 62 | + | ||
| 63 | + | // Key identifies an element among its siblings across frames. | |
| 64 | + | func Key(key string) commonOption { return ui.Key(key) } | |
| 65 | + | ||
| 66 | + | // ID sets the CSS ID; it does not affect frame identity. | |
| 67 | + | func ID(id string) commonOption { return ui.ID(id) } | |
| 68 | + | ||
| 69 | + | // Class adds a CSS class. | |
| 70 | + | func Class(class string) commonOption { return ui.Class(class) } | |
| 71 | + | ||
| 72 | + | // Inline applies inline CSS declarations. | |
| 73 | + | func Inline(src string) commonOption { return ui.Inline(src) } | |
| 74 | + | ||
| 75 | + | // Disabled blocks focus and interaction on an interactive control. | |
| 76 | + | func Disabled(v bool) disabledOption { return ui.Disabled(v) } | |
| 77 | + | ||
| 78 | + | // Level sets the accessibility heading level (1 through 6). | |
| 79 | + | func Level(level int) HeadingOption { return ui.Level(level) } | |
| 80 | + | ||
| 81 | + | // Placeholder sets the text shown in an empty editor. | |
| 82 | + | func Placeholder(v string) EditOption { return ui.Placeholder(v) } | |
| 83 | + | ||
| 84 | + | // Password masks displayed editor text and blocks copy and cut. | |
| 85 | + | func Password(v bool) EditOption { return ui.Password(v) } | |
| 86 | + | ||
| 87 | + | // Title sets the window title. | |
| 88 | + | func Title(s string) WindowOption { return ui.Title(s) } | |
| 89 | + | ||
| 90 | + | // Size sets the initial logical window size. | |
| 91 | + | func Size(w, h int) WindowOption { return ui.Size(w, h) } | |
| 92 | + | ||
| 93 | + | // Styles loads an author stylesheet from a file path. | |
| 94 | + | func Styles(s string) WindowOption { return ui.Styles(s) } | |
| 95 | + | ||
| 96 | + | // Transparent requests an alpha-capable Wayland surface (opaque by default). | |
| 97 | + | func Transparent() WindowOption { return ui.Transparent() } | |
| 98 | + | ||
| 99 | + | // Run builds and presents an immediate view on the Wayland session owner loop. | |
| 100 | + | func Run[T any](ctx context.Context, model *T, view func(*Frame, *T), options ...WindowOption) error { | |
| 101 | + | return ui.Run(ctx, model, view, options...) | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | // RunFrames is a deterministic harness entry: each commit requests a redraw | |
| 105 | + | // and the loop stops after the given number of committed frames. | |
| 106 | + | func RunFrames[T any](ctx context.Context, frames int, model *T, view func(*Frame, *T), committed func(uint64) error, options ...WindowOption) error { | |
| 107 | + | return ui.RunFrames(ctx, frames, model, view, committed, options...) | |
| 108 | + | } | |
| @@ -0,0 +1,108 @@ | |||||
| 1 | + | // Package nefergui provides a pure-Go immediate-mode GUI for Wayland, rendered with | |||
| 2 | + | // Vulkan and styled with a bounded CSS dialect (see docs/css.md). | |||
| 3 | + | // | |||
| 4 | + | // Run opens a window and calls the view function to build each frame. The view | |||
| 5 | + | // receives a Frame; Frame and its Node handles are valid only during that call. | |||
| 6 | + | // Key identifies a child among its siblings across frames; without a key, | |||
| 7 | + | // position and type determine identity. ID is CSS metadata, not frame identity. | |||
| 8 | + | // Options such as Title, Size, Styles and Transparent configure the window. | |||
| 9 | + | // | |||
| 10 | + | // The frame tree, controls, editors and input routing are implemented in | |||
| 11 | + | // internal/ui; this package is the public facade and re-exports those types. | |||
| 12 | + | // | |||
| 13 | + | // Builds support CGO_ENABLED=0. At runtime, NeferGUI requires libvulkan.so.1, | |||
| 14 | + | // libxkbcommon.so.0 and a Wayland compositor supporting linux-dmabuf and | |||
| 15 | + | // linux-drm-syncobj. See docs/runtime.md and docs/troubleshooting.md. | |||
| 16 | + | package nefergui | |||
| 17 | + | ||||
| 18 | + | import ( | |||
| 19 | + | "context" | |||
| 20 | + | ||||
| 21 | + | "github.com/bnema/nefergui/internal/ui" | |||
| 22 | + | ) | |||
| 23 | + | ||||
| 24 | + | // Frame is valid only during one call to the view function. Root builds the | |||
| 25 | + | // application element; Diagnostics reports identity problems in debug builds. | |||
| 26 | + | type Frame = ui.Frame | |||
| 27 | + | ||||
| 28 | + | // Node is an ephemeral handle into the current frame. Do not retain it between | |||
| 29 | + | // frames. Container methods (Box, Row, Column, Stack, Scroll, Header, Nav, | |||
| 30 | + | // Main, Section, Aside, Footer, Element) return child nodes; control methods | |||
| 31 | + | // (Button, Checkbox, Radio, Slider, Input, Textarea, Text, Heading, Image, | |||
| 32 | + | // Icon, Separator, Spacer) declare controls. Interactive controls return event | |||
| 33 | + | // snapshots. | |||
| 34 | + | type Node = ui.Node | |||
| 35 | + | ||||
| 36 | + | // Control event values are immutable snapshots; querying them never consumes events. | |||
| 37 | + | type ( | |||
| 38 | + | ButtonEvent = ui.ButtonEvent | |||
| 39 | + | EditEvent = ui.EditEvent | |||
| 40 | + | ChangeEvent = ui.ChangeEvent | |||
| 41 | + | ) | |||
| 42 | + | ||||
| 43 | + | // Option families are sealed: ContainerOption, ButtonOption, EditOption, | |||
| 44 | + | // ValueOption and HeadingOption accept the common CSS options (Key, ID, | |||
| 45 | + | // Class, Inline) plus the control-specific options listed on each function. | |||
| 46 | + | type ( | |||
| 47 | + | ContainerOption = ui.ContainerOption | |||
| 48 | + | ButtonOption = ui.ButtonOption | |||
| 49 | + | EditOption = ui.EditOption | |||
| 50 | + | ValueOption = ui.ValueOption | |||
| 51 | + | HeadingOption = ui.HeadingOption | |||
| 52 | + | WindowOption = ui.WindowOption | |||
| 53 | + | ) | |||
| 54 | + | ||||
| 55 | + | // commonOption belongs to every option family; disabledOption to every | |||
| 56 | + | // interactive control family. Both are concrete internal types, aliased so | |||
| 57 | + | // the constructors below can return them without a second implementation. | |||
| 58 | + | type ( | |||
| 59 | + | commonOption = ui.CommonOption | |||
| 60 | + | disabledOption = ui.DisabledOption | |||
| 61 | + | ) | |||
| 62 | + | ||||
| 63 | + | // Key identifies an element among its siblings across frames. | |||
| 64 | + | func Key(key string) commonOption { return ui.Key(key) } | |||
| 65 | + | ||||
| 66 | + | // ID sets the CSS ID; it does not affect frame identity. | |||
| 67 | + | func ID(id string) commonOption { return ui.ID(id) } | |||
| 68 | + | ||||
| 69 | + | // Class adds a CSS class. | |||
| 70 | + | func Class(class string) commonOption { return ui.Class(class) } | |||
| 71 | + | ||||
| 72 | + | // Inline applies inline CSS declarations. | |||
| 73 | + | func Inline(src string) commonOption { return ui.Inline(src) } | |||
| 74 | + | ||||
| 75 | + | // Disabled blocks focus and interaction on an interactive control. | |||
| 76 | + | func Disabled(v bool) disabledOption { return ui.Disabled(v) } | |||
| 77 | + | ||||
| 78 | + | // Level sets the accessibility heading level (1 through 6). | |||
| 79 | + | func Level(level int) HeadingOption { return ui.Level(level) } | |||
| 80 | + | ||||
| 81 | + | // Placeholder sets the text shown in an empty editor. | |||
| 82 | + | func Placeholder(v string) EditOption { return ui.Placeholder(v) } | |||
| 83 | + | ||||
| 84 | + | // Password masks displayed editor text and blocks copy and cut. | |||
| 85 | + | func Password(v bool) EditOption { return ui.Password(v) } | |||
| 86 | + | ||||
| 87 | + | // Title sets the window title. | |||
| 88 | + | func Title(s string) WindowOption { return ui.Title(s) } | |||
| 89 | + | ||||
| 90 | + | // Size sets the initial logical window size. | |||
| 91 | + | func Size(w, h int) WindowOption { return ui.Size(w, h) } | |||
| 92 | + | ||||
| 93 | + | // Styles loads an author stylesheet from a file path. | |||
| 94 | + | func Styles(s string) WindowOption { return ui.Styles(s) } | |||
| 95 | + | ||||
| 96 | + | // Transparent requests an alpha-capable Wayland surface (opaque by default). | |||
| 97 | + | func Transparent() WindowOption { return ui.Transparent() } | |||
| 98 | + | ||||
| 99 | + | // Run builds and presents an immediate view on the Wayland session owner loop. | |||
| 100 | + | func Run[T any](ctx context.Context, model *T, view func(*Frame, *T), options ...WindowOption) error { | |||
| 101 | + | return ui.Run(ctx, model, view, options...) | |||
| 102 | + | } | |||
| 103 | + | ||||
| 104 | + | // RunFrames is a deterministic harness entry: each commit requests a redraw | |||
| 105 | + | // and the loop stops after the given number of committed frames. | |||
| 106 | + | func RunFrames[T any](ctx context.Context, frames int, model *T, view func(*Frame, *T), committed func(uint64) error, options ...WindowOption) error { | |||
| 107 | + | return ui.RunFrames(ctx, frames, model, view, committed, options...) | |||
| 108 | + | } | |||
D readme_example_test.go +0 -26
| @@ -1,26 +0,0 @@ | |||
| 1 | - | package nefergui_test | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "context" | |
| 5 | - | "fmt" | |
| 6 | - | "log" | |
| 7 | - | ||
| 8 | - | "github.com/bnema/nefergui" | |
| 9 | - | ) | |
| 10 | - | ||
| 11 | - | // Compile the README example without opening a window during tests. | |
| 12 | - | func Example_counter() { | |
| 13 | - | count := 0 | |
| 14 | - | view := func(f *nefergui.Frame, count *int) { | |
| 15 | - | root := f.Root().Column() | |
| 16 | - | root.Text("Hello, Wayland!") | |
| 17 | - | if root.Button("Count", nefergui.Key("count")).Activated() { | |
| 18 | - | *count++ | |
| 19 | - | } | |
| 20 | - | root.Text(fmt.Sprintf("Clicked %d times", *count)) | |
| 21 | - | } | |
| 22 | - | if err := nefergui.Run(context.Background(), &count, view, | |
| 23 | - | nefergui.Title("Hello"), nefergui.Size(320, 200)); err != nil { | |
| 24 | - | log.Fatal(err) | |
| 25 | - | } | |
| 26 | - | } | |
| @@ -1,26 +0,0 @@ | |||||
| 1 | - | package nefergui_test | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "context" | |||
| 5 | - | "fmt" | |||
| 6 | - | "log" | |||
| 7 | - | ||||
| 8 | - | "github.com/bnema/nefergui" | |||
| 9 | - | ) | |||
| 10 | - | ||||
| 11 | - | // Compile the README example without opening a window during tests. | |||
| 12 | - | func Example_counter() { | |||
| 13 | - | count := 0 | |||
| 14 | - | view := func(f *nefergui.Frame, count *int) { | |||
| 15 | - | root := f.Root().Column() | |||
| 16 | - | root.Text("Hello, Wayland!") | |||
| 17 | - | if root.Button("Count", nefergui.Key("count")).Activated() { | |||
| 18 | - | *count++ | |||
| 19 | - | } | |||
| 20 | - | root.Text(fmt.Sprintf("Clicked %d times", *count)) | |||
| 21 | - | } | |||
| 22 | - | if err := nefergui.Run(context.Background(), &count, view, | |||
| 23 | - | nefergui.Title("Hello"), nefergui.Size(320, 200)); err != nil { | |||
| 24 | - | log.Fatal(err) | |||
| 25 | - | } | |||
| 26 | - | } | |||