pinToTop: element-level pin-to-top overlays - #26
Merged
Conversation
pinToTop reparents an object onto a designated pin container and re-syncs its transform from its original anchor each paint, so it renders on top while following the anchor's position/scale/animation. State lives on the stage object so separate easelbone bundles on one stage cooperate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pin() previously re-derived the anchor from obj.parent, which is wrong for an already-pinned object (parent is by then the pin container), causing a leaked never-torn-down record plus redundant per-frame matrix work on every re-pin of the same instance. Guard pin() to no-op when the object is already tracked. Also mark RootView dirty on successful pin/unpin via DirtyFlag, since reparenting/removing a child doesn't itself trigger a repaint under dirtyRendering: true. Extends the pin-baseline fixture/test with re-pin-replace, idempotent-re-pin, and anchor-removed auto-teardown coverage; the idempotent case reproduces FAIL against the pre-fix pin() (2 tracked records instead of 1) and passes after the fix.
…l size
pinToTop reparented the pinned obj directly into the shared pin
container and rewrote its transform. That broke Fill/Background
objects (e.g. QR codes), which size themselves via
parent.getBounds() -- the plain pin container has no bounds, so the
Fill fell back to a 100x100 default and rendered tiny in the
top-left of its placeholder.
Fix: reparent obj into a per-pin wrapper Container instead. The
wrapper carries the anchor's bounds and is transformed to the
anchor's global position; obj itself (and its own transform/regX/
regY) is left untouched. This reproduces the anchor as obj's parent
exactly (same bounds + same global transform), so Fills size
correctly and non-Fill objects render identically to before.
Record shape is now { obj, anchor, wrapper }. _restore detaches the
wrapper and reattaches obj to its original anchor (if still on
stage), which also makes explicit unpin() put the object back where
it came from instead of leaving it stranded with regX/regY reset.
Added a regression assertion in tools/pin-test.js (via a new
window.__wrapperBounds() fixture hook) proving the wrapper carries
the anchor's [50,50] bounds -- this fails against the pre-fix code
(no wrapper existed) and passes after.
pin() relied on obj's 'added' event when the object wasn't on a stage yet, but the object is typically addChild'd to its (still-detached) placeholder BEFORE pinToTop is called -- so 'added' already fired and never fires again when the placeholder is later attached, silently dropping the pin. Retry on the ticker until getStage() is truthy. Adds a regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lets callers hide all pinned overlays (e.g. the portal QR) while a modal is open over them, restoring on close. Adds a regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DisplayObject.getStage() is deprecated and logs a warning on every call; the per-tick sync/teardown checks spammed thousands of warnings. Use the non-deprecated .stage getter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds pinToTop (element-level pin-to-top overlays) + stage._pinsHidden. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
📦 PR Preview deployed! Examples are available at: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an element-level pin-to-top primitive so a display object can render above a chosen layer while still tracking its original placeholder's position/scale/animation.
API
easelbone.pinToTop(obj)/easelbone.unpinFromTop(obj)RootView.setPinContainer(container)— designate the layer pinned objects render intostage._pinsHidden— hide all pinned overlays (e.g. while a modal is open)How it works
Pins reparent
objinto a per-pin wrapper on the pin container; the wrapper carries the anchor's bounds (so bounds-sizingFill/Backgroundobjects keep their size) and is re-synced to the anchor's global transform every paint. State lives on the sharedstageobject so separate easelbone bundles on one stage cooperate. Includes idempotent re-pin, auto-teardown when the anchor leaves the stage, deferred pinning until the object is on a stage, and a Playwright regression test (tools/pin-test.js).Consumed by catlab-remote-library # and client.quizwitz.com # to keep connect/portal QR codes above the flung-emoji layer. Release v1.2.8.
🤖 Generated with Claude Code