openpencil/crates
Kayshen-X 8457f4d629 feat(shell): LayerPanel drag-to-reorder — cross-parent reparenting
Drag a layer row in the LayerPanel; a 2 px primary-tint drop
indicator paints between rows; release commits the move. Supports
both same-parent reordering and cross-parent reparenting.

Core (`document/`):
- `Document::reorder_before(source, anchor)` / `reorder_after`
  with editability + cycle + existence pre-checks. The mutation
  phase calls `extract_node` + `insert_before/after_in_children`
  via shared walkers — guarded by the pre-check pass so the
  Err(node) arm of the insert helpers stays unreachable.
- New walkers: `extract_node`, `insert_before/after_in_children`
  (`Result<(), Node>` so the node bounces back on miss),
  `children_contain_descendant`. All `pub(in crate::document)`
  so sibling submodules (`mutators`) can call them via
  `super::walkers::*`.

Widget (`widgets/layer_panel.rs`):
- `DropPosition::{Before, After}` + `DropTarget { anchor,
  position, indicator_y }` re-exported from `widgets::`.
- `LayerPanel::drop_target_at(rect, point) -> Option<DropTarget>`
  walks the layer rows; upper half → Before, lower half → After.
- `LayerPanel.drop_target` field + `from_document_with_drop`
  ctor so the host can thread the active drop target through
  to paint without touching the existing API. Drop-indicator
  paints last so it sits above row chrome.
- Layout constants promoted to `pub(crate)` and tests moved to
  a sibling `widgets/layer_panel_tests.rs` to honor the 800-line
  cap (mirrors the `document/` split pattern).

Host wiring (native + web parity):
- `LayerDragState { source, start_y, current_x/y, active }` on
  both `WidgetHostNative` and web `WidgetHost`.
- `apply_press` peeks for a Layer row hit and seeds the drag
  candidate (selection still happens immediately on press).
- `apply_cursor_move` promotes candidate → active once the
  cursor moves past a 4 px screen-space threshold (mirrors the
  marquee activation heuristic).
- `apply_release_with_viewport` calls `commit_layer_drag`,
  which runs `drop_target_at` against the live cursor pos and
  dispatches to `reorder_before` / `reorder_after`. Viewport-
  less `apply_release` drops the candidate silently (no rect
  to compute against).
- Paint paths build the panel via `from_document_with_drop`
  when `layer_drag.active` is true.

Tests (8 new, 137 total in shell-core):
- 6 mutator tests in `document/tests_mutators.rs`:
  same-parent before/after move, cross-parent reparenting,
  cycle rejection, locked/hidden source rejection,
  same-id/missing-id rejection.
- 1 drop-target hit-test in `widgets/layer_panel_tests.rs`
  covering both halves of a row (Before / After) and the
  `indicator_y` contract.

All checks clean: cargo fmt + cargo test + cargo check
--workspace + tools/check-widget-boundary.sh. Every file
under the 800-line ceiling.
2026-05-11 22:06:02 +08:00
..
openpencil-app feat(openpencil-app): skeleton crate (Stage F entry placeholder) 2026-05-03 22:05:00 +08:00
openpencil-desktop feat(shell): selection handles + drag-create + per-node flags + LayerPanel polish 2026-05-11 21:30:06 +08:00
openpencil-shell-core feat(shell): LayerPanel drag-to-reorder — cross-parent reparenting 2026-05-11 22:06:02 +08:00
openpencil-shell-native feat(shell): LayerPanel drag-to-reorder — cross-parent reparenting 2026-05-11 22:06:02 +08:00
openpencil-shell-web feat(shell): LayerPanel drag-to-reorder — cross-parent reparenting 2026-05-11 22:06:02 +08:00
pen-codegen chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-core chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-engine chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-figma chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-types feat(pen-types): skeleton crate (bucket A) 2026-05-03 22:35:00 +08:00
wasm-libc-shim style(shell): cargo fmt --all (rustfmt-clean) 2026-05-10 18:47:33 +08:00
CLAUDE.md docs(shell): note document.rs directory split in crates/CLAUDE.md 2026-05-11 21:42:44 +08:00