Commit Graph
27 Commits
Author SHA1 Message Date
iris-aiandClaude Opus 5 d98969158f Give a slot to the children a container places, and nothing else
A widget's region is now held in the coordinates of the slot it draws in
rather than the window's, and `Painter::place` is how a container asks for a
slot: it draws a child it decides the box of and may decide again. Everything
under that slot is a fraction of its box, so placing the child a second time
is one entry to write whether it moved or changed length. A child drawn any
other way has no slot and shares its nearest ancestor's.

That is what keeps the chain short. `chain_cost` measured depth as the cost
-- free to 8, +42.6% at 16 -- and a slot per widget put a transcript's glyphs
past that for nothing, since almost every slot was zero. `Span`, `Aligned`
and `Scroll` are the containers that re-place a child after drawing it, and
`tests/layout.rs` pins that four widgets between a span and a leaf leave the
leaf's chain one deep.

`UiRegion::stretch`, `UiRegion::stretchable` and `UiScalar::stretch` are
gone. Nothing is inverted any more: a box that changed length is written to
its slot, and the descendants recompose against it in the shader. That also
retires the case the guard existed for, where a fixed length has no fraction
to recover -- `tests/layout.rs` now stretches a 40-tall row on its other
axis, which `stretchable` refused outright.

What still walks the CPU is deciding who must draw again, which no chain can
answer: `mark_resized` descends from the widget whose box changed and marks
anything whose own box changed length and whose drawing reads it. A part of
a box with no relative extent on an axis is a fixed length, and composing
into it leaves none either, so the walk stops where a length did not change
-- an 80-wide child in a widened row is not redrawn though it says `Redraw`.

`Span`, `Pad`, `Stack`, `Offset`, `Aligned`, `SetSize` and `LayerOffset` say
`Scale`: each places in fractions and offsets of its own box and none reads
the box's pixel length. `Scroll` and `MaxSize` do read pixels and stay
`Redraw`.

45 tests pass, five of them new. Render verification comes after the CPU
side, per the owner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 12:25:37 -04:00
iris-ai f9423855e1 Size a widget while drawing it, not in a pass of its own (#16)
Reviewed-on: iris/iris#16
Reviewed-by: iris <2+iris@noreply.localhost>
Co-authored-by: iris-ai <4+iris-ai@noreply.localhost>
2026-09-14 02:48:02 -04:00
iris-aiandiris 32b10383d8 Rename the Sized widget to SetSize (#14)
`Sized` shadowed the marker trait, so a `?Sized` bound in any crate that imports the prelude failed to resolve -- a compile error in someone else's code that nothing here would have caught. It was already biting inside iris: `default/mod.rs`, `widget/ptr.rs` and `widget/text/build.rs` all imported `std::marker::Sized` explicitly to get out from under it, which they no longer need.

`SetSize` rather than `FixedSize` because the size it sets need not be fixed -- `width(rest(2))` (a flex weight) and `width(rel(0.5))` (half the parent) build the same widget, and both are more common than `sized((100, 100))`. It also pairs with the `MaxSize` beside it in that module: one sets a length, the other caps it. The builders are unchanged.

`tests/prelude_bounds.rs` is a compile-level guard -- it fails to build if the prelude shadows `Sized` again, which I checked by reverting `src/` under it:

```
error[E0404]: expected trait, found struct `Sized`
 --> tests/prelude_bounds.rs:8:22
  |
8 | fn takes_unsized<T: ?Sized>(_: &T) {}
  |                      ^^^^^ not a trait
```

The pad tab of the tabs example -- the one built out of `sized` and the flexible widths -- renders pixel-identical to before the rename.

---------

Co-authored-by: iris <2+iris@noreply.localhost>
Reviewed-on: iris/iris#14
Co-authored-by: AIris <4+iris-ai@noreply.localhost>
2026-09-13 20:16:17 -04:00
iris-aiandiris b234497d21 Draw the glyph atlas as an array texture and images with their own bind groups + primitive rendering overhaul
Replaces the bindless `binding_array<texture_2d<f32>>` the renderer bound every texture through. That array needs `VK_EXT_descriptor_indexing`, which a real share of Android GPUs lack, so the old shape did not run there at all.

The two things being bound want opposite treatment, so they are now split:

- **Glyph atlas pages become layers of one `texture_2d_array`.** A glyph primitive carries a `layer` instead of a view/sampler index pair. A layer index is an ordinary sampling operand, so this needs nothing beyond plain Vulkan 1.0 / GLES. Growing the atlas recreates the array with headroom and `copy_texture_to_texture`s the old layers across, no readback.
- **A standalone image gets its own texture and its own bind group,** and draws in its own call. It no longer needs a per-instance entry in `PrimitiveData`: the bind group has already picked the texture.

`Primitives` keeps images in a list of their own as a result, with `PrimitiveChange::is_image` naming which list a renumbering belongs to -- the two have independent index spaces, so `(layer, inst_idx)` alone would collide between them.

Two notes on judgement calls, since this slice was rebuilt on top of `main` rather than transplanted:

- The source version renamed `GlyphEntry::is_colored` to `is_color` and added a second `IS_COLOR` flag constant beside the existing `GlyphEntry::IS_COLORED`. Both dropped: #10's naming and its `flags()` are kept, and UVs stay `Vec2` rather than going back to `[f32; 2]`.
- `ImageGpu` no longer holds the `Texture` behind its view, which removes an `#[allow(dead_code)]`. A `TextureView` keeps its own reference to the texture, checked by rendering rather than assumed -- see below.

### Verification

```
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
```

All clean; the 4 text-edit tests pass. The only clippy output is the pre-existing future-incompatibility notice about `naga`/`wgpu`/`winit`.

Because this is a rendering change, it was also run for real rather than only compiled. The `tabs` example was rendered on this machine's GPU -- Venus onto an RX 7900 XT, confirmed from the loaded ICD (`libvulkan_virtio.so` on `/dev/dri/renderD128`) rather than assumed, since a failed Vulkan init here silently falls back to llvmpipe and would make the screenshots meaningless.

Screenshots before and after the change are **byte-identical** (same md5) in two scenes: the default tab, which exercises text (the atlas path) and rects, and the image tab with a standalone image pushed at startup, which exercises the per-image bind group. The image-tab scene needed a temporary local edit to the example to push the image without a click; that edit is not part of this branch. The same comparison, re-run after dropping the `Texture` field, is still byte-identical -- which is the check that the view alone keeps it alive.

---------

Co-authored-by: iris <2+iris@noreply.localhost>
Reviewed-on: iris/iris#11
Reviewed-by: iris <2+iris@noreply.localhost>
Co-authored-by: AIris <4+iris-ai@noreply.localhost>
2026-09-13 18:56:59 -04:00
iris-aiandiris 0f6a28b4dd Move text layout and rendering to Parley (#10)
Replace the cosmic-text path with Parley layout and Swash rasterization, backed by shared glyph-atlas pages. Shaping, editing, rasterization, and glyph rendering move together because they share the text buffer and rendered-glyph types; splitting them further would require a temporary renderer that is immediately removed.

This is reconstructed rather than replayed from the extraction history. It also fixes issues found during review:

- texture binding changes remain set when an atlas patch follows a new page
- pressing an empty field places a caret and accepts input
- selection motion delegates collapse behavior to Parley
- character deletion follows logical clusters rather than visual neighbors
- the unused root-level Swash dependency is omitted

Four public-behavior integration tests live in `tests/text_edit.rs`: empty-field input, multibyte IME preedit replacement, UTF-8-safe backspace, and selection replacement. The old twelve-test inline block and implementation-restating cases are omitted.

Every added source comment was manually reviewed. Comments that narrated implementation or history were removed; retained comments document cache/rasterization keys, GPU upload constraints, focus representation, bidi geometry, or IME semantics.

Known limitation: atlas pages currently grow without eviction. Each page is 4 MiB on CPU and GPU. An arbitrary cap would leave cached rendered-text UVs pointing at reused glyph slots, so bounding this safely needs a later generation/invalidation change.

This changes public text types and signatures. GPU glyph rendering is covered by compilation rather than a live-surface test.

Verified with:

- `cargo fmt --all --check`
- `cargo clippy --workspace --all-targets -- -D warnings`
- `cargo test --workspace` (four integration tests pass)

Cargo still reports inherited future-incompatibility notices for existing wgpu/winit dependencies; there are no current clippy warnings.

---------

Co-authored-by: iris <2+iris@noreply.localhost>
Reviewed-on: iris/iris#10
Reviewed-by: iris <2+iris@noreply.localhost>
Co-authored-by: AIris <4+iris-ai@noreply.localhost>
2026-09-13 03:39:23 -04:00
iris 06dd015092 finished moving out render_state 2026-01-19 18:00:24 -05:00
iris 79813db3ba work 2026-01-12 18:40:27 -05:00
iris f2ac6f195f remove typed stuff / just specify rsc if needed 2026-01-03 18:06:05 -05:00
iris 59901b6580 tasks initial impl (still working on task_on trait method) 2026-01-03 16:26:23 -05:00
iris 5da1e9e767 separate state from rsc 2026-01-01 22:18:08 -05:00
iris 1ccf947220 convenience methods for span 2025-12-20 01:38:56 -05:00
iris c00ded78c0 switch away from handles to refs that must be upgraded once 2025-12-20 01:36:07 -05:00
iris fabc7d0b90 abuse macros.. 2025-12-20 00:26:08 -05:00
iris bae17235c6 better global state structure? 2025-12-19 21:54:48 -05:00
iris 30bc55c78e remove state generic from a lot of things 2025-12-17 21:37:55 -05:00
iris 70ac0fbcb2 typed stuff 2025-12-17 00:55:36 -05:00
iris ecbb9e56e2 small QOL 2025-12-16 20:24:21 -05:00
iris 2183fbd3cb make painter not stupid (size ctx is kinda tho) 2025-12-16 00:26:25 -05:00
iris 0b8a93c5ce RE ADD CONTEXT 2025-12-15 21:50:53 -05:00
iris 37b1987aa8 remove modules and have single event manager (atomics feature parity + preparation for local state) 2025-12-12 01:46:24 -05:00
iris a708813ce7 idk work (r + h) 2025-12-11 23:05:27 -05:00
iris 966b6a2ac2 proper widgetid + slot vec instead of map 2025-12-11 16:23:14 -05:00
iris 2dad409300 handles (tuple) 2025-12-11 07:30:59 -05:00
iris 36668c82f4 strong & weak widgets 2025-12-11 07:16:06 -05:00
iris a85e129026 move everything out of layout 2025-12-11 05:48:29 -05:00
iris 174c447706 switch to macro 2025-12-11 05:31:34 -05:00
iris 2dc5b0f62c refactor project structure (start of redoing atomic branch without atomics) 2025-12-11 05:25:58 -05:00