Skip to content

M3: group, layer and type appearance with Contents and Characters rows (M3.68) - #26

Merged
bflatastic merged 1 commit into
mainfrom
pr/m3-container-appearance
Oct 4, 2026
Merged

bflatastic merged 1 commit into
mainfrom
pr/m3-container-appearance

Conversation

@bflatastic

Copy link
Copy Markdown
Member

Part of M3 (Paint & appearance). Groups, layers and type objects get an appearance of their own, as in Illustrator, with Contents and Characters rows in the Appearance panel.

  • M3.68: container appearance.
    • Groups and layers carry their own fills, strokes and effects:
      • Their fills and strokes paint every member's geometry.
      • Geometry effects reshape the members as one piece.
      • Raster effects apply to the composite, so a group gets one combined shadow.
      • Knockout groups still knock their members out, and layer clipping is respected.
      • The renderer, SVG/PDF export (through baking) and Expand Appearance share one evaluator, effects::evaluate_container.
    • Type:
      • Its own fills and strokes paint under or over the characters, depending on whether they sit below or above the Characters row. This works on screen and in PDF.
      • Its object strokes are drawn on the glyph outlines.
    • Model: Appearance.contents_index: Option<usize> (None = every item above the contents). It is only written when set, and there is a native round-trip test.
    • Commands:
      • appearance.*, effect.* and graphicStyle.apply take target: "object"|"contents" and accept layer ids. They work without a selection when given ids, and error when there is no target.
      • appearance.moveItem {from: "contents"} moves the Contents slot, and contents? lets an item cross the slot in one undo step.
      • New appearance.targetContents selects a group's members.
      • appearance.addFill/addStroke on type copies the characters' paint.
    • Panel:
      • Groups and layers show a Contents row; type shows a Characters row with chips of the characters' paint.
      • Rows can be dragged across the slot.
      • Double-clicking Contents targets the members; double-clicking Characters opens the Color panel.
    • Graphic styles: a style applied to a group now sets the group's own appearance; target: "contents" styles the members as before. A group with no appearance of its own still counts as linked when all its members match.

docs/mcp.md covers targeting, the Contents slot and the commands.

Known limits:

  • SVG export of live type still ignores the type object's own fills and strokes. That was already the case before this PR.
  • The Layers panel has no click-to-target for layers yet; layers are reached through ids.

Checks

Rebased onto 25c8f32 (#23). cargo xtask ci passes all 7 steps on this commit stacked with the swatch libraries PR that follows it: 1,530 tests, 0 failed. It also passed on its own on e21ff60 with 1,505 tests.

New tests:

  • render: one combined shadow; a group fill paints both members; a fill below Characters is hidden by the glyphs; knockout and layer fills;
  • SVG bake;
  • engine commands and undo;
  • native round-trip;
  • a headless Appearance panel test with the drop commands.

🤖 Generated with Claude Code

…cts, with Contents and Characters rows

A group's or layer's own fills and strokes paint every member's geometry, below or above the
members as the new contents slot (Appearance.contents_index) says; its geometry effects reshape
the members as one piece and its raster effects apply to the composite (one combined shadow),
respecting knockout groups. Type's own fills and strokes paint under or over its characters.
Render, SVG/PDF (via bake) and Expand Appearance evaluate them alike.

Commands: appearance.* / effect.* take target: "object"|"contents" and layer ids;
appearance.moveItem moves the Contents slot (from: "contents") or an item across it
(contents); appearance.targetContents selects a group's members. Graphic styles applied to a
group now style the group itself. The Appearance panel shows Contents/Characters rows that
drag, and double-click to target the members or edit the characters' paint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bflatastic
bflatastic merged commit 1ea13b5 into main Oct 4, 2026
@bflatastic
bflatastic deleted the pr/m3-container-appearance branch October 4, 2026 18:10
apexmiguel9-hub pushed a commit to apexmiguel9-hub/vectorcraft-android that referenced this pull request Oct 6, 2026
MEDIDO en el movil. La app entra en `android_main` —el punto de entrada funciona— y
muere asi:

    Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 24081 (android_main)
    storytold#26 pc ... base.apk (offset 0xb0000) (android_main+320)

**Y no hay ningun mensaje de panic en el logcat.** Ni una linea.

La razon es la misma que ya se pago en el otro repo: **el gancho por defecto de
Rust escribe en `stderr`, y en Android `stderr` no llega a logcat** si el proceso
no tiene stdout asociado. Ahi un `eprintln!` en un bucle de diez mil llamadas no
aparecio ni una vez y se tomo como evidencia de que el codigo no se llamaba.

Aqui el sintoma es peor: se sabe que hay panic y que esta dentro de `android_main`,
y no se sabe que linea ni por que. `android_main+320` son 320 bytes de codigo sin
nombre.

## El arreglo

Un hook de panic que manda el mensaje al log de Android, que si llega, y tambien a
`stderr` por si hay un debugger enganchado:

    std::panic::set_hook(Box::new(|info| {
        log::error!("PANIC de Rust: {info}");
        eprintln!("PANIC de Rust: {info}");
    }));

Se pone **antes de nada mas**, y antes del `log::info!` de arranque, para que el
siguiente run diga en que punto se llega.

**Instrumentacion que no funciona es peor que no tenerla**, y esta vez costaba el
siguiente paso entero: sin el mensaje, todo lo que se puede decir del fallo es que
ocurre 320 bytes dentro de una funcion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant