Repository navigation
fts-focus + the iOS fixes - #4
Merged
Merged
Conversation
handle_pointermove indexed the slab with mousedown_node_id. A press that re-renders — a tab that remounts a view, a button that replaces itself — removes that node before the drag moves, and the next move panicked with 'invalid key'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A press can land on a node the same press removes — a menu row that closes its menu. The next focus change then blurred the removed node, and snapshot_node indexed the slab with its stale id: "invalid key", a crash on the next click. Snapshotting a missing node is now nothing, focusing one is refused, and a focus left on one is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closing a menu frees its nodes at once, but the laid-out tree still points into them until the next layout: a node's layout_parent, and what a hit test finds. A pointer move in that moment walked up into a freed node — node_layout_ancestors indexed the slab with it, "invalid key", and the window went with it (hovering off a dropdown as it closed). The walks up (layout ancestors, element ancestors, absolute position) now end at a missing node, a hit on a removed node hovers nothing, and a hover or cursor left on one is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ever walked up from Closing a card by its own close button: the button is detached (removed, not yet dropped) while its parent is dropped, and the hover still names it. Unhovering it restyled it, and marking its ancestors dirty indexed the slab with its stale parent id — "invalid key" at the click. Hover now only touches nodes still in the document, snapshot_node_and skips detached ones, and the parent walks (ancestors dirty, child_index, forward, backward) end at a missing parent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A click's handler can remove its own target (Together, on the share card, re-renders the card under it). The default action runs after the handlers, on the stale id: handle_dom_event indexed the slab with it — "invalid key" on the click. It now returns when the target is gone or detached; every path below it walks from the target, so a live target is all they need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paint_scene offsets the scene in physical pixels, but both calls handed it the insets converted to logical ones. On an iPhone (scale 3) the picture sat a third of the way down the notch while pointer_coords took the whole inset off input: every tap landed two-thirds of the inset below what it pressed. Every other use of safe_area_insets here was already physical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… safe area too The fill started at the insets times the scale: the insets are physical pixels now (see blitz-shell), so that pushed it past the status bar and left it, and the home indicator's strip, the renderer's clear colour - white on a phone in light mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resume sized the surface to the viewport alone, while every resize adds the insets; on an iPad that launches already the right way up no resize follows, and the whole picture stays stretched into the taller window, its bottom off screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…resh Sizing the surface to the viewport plus the insets read at creation stretched the picture the other way on an iPad launched in landscape: those insets (and so the viewport) can be another shape's. The first frame now does what a resize does — current surface size, current safe area, viewport from both, surface the whole window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… area afresh" This reverts commit 3a83281.
This reverts commit 4d75406.
…d use physical safe-area insets (DioxusLabs#735)
Touch has no move before a press, so the shell synthesises one to put the hover under the finger. It carried the pressed button, so the document measured it from the previous press's position as a drag, took any tap somewhere new for a pan, and dispatched no click on release: every other tap in a different place was lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On iOS a rotation resizes the surface before UIKit updates the safe-area insets, so the insets read at the resize were the old orientation's: a phone turned on its side kept its upright top and bottom bands. Re-read them each frame, and ask for frames for half a second after a resize so the new ones are picked up even when nothing else is drawing.
…d right edges A phone on its side reports the camera housing's inset on both sides, though only one side has it, and only mid-height: an app that minds the sides itself can have the width back, keeping the top and bottom insets.
- is_animating asks each custom widget's needs_redraw rather than counting a mounted widget as animating: a window with one drew every frame forever. - The shell reads is_animating after painting, so a widget whose paint sets it moving again (a fling, a playhead) is heard in the same frame. - Border edges with no width are skipped, and the corner split angle is defined when one or both widths are zero: Tailwind's preflight gave every rounded element a NaN path per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- resolve: frames with no damage skip every tree walk; frames whose damage is paint-only (colour, shadow, transform) run only the damage propagation and transforms passes, not construct/flush/layout. - compute_layout_damage marks restyles whose layout style structs are unchanged (stylo ORs in RELAYOUT for box-shadow etc.), and resolve takes the relayout bit off those nodes. - set_attribute: an unchanged value is a no-op; a style attribute no longer forces ALL_DAMAGE on the element and its parent. - custom widgets no longer keep the window animating; a widget with something new to draw schedules an update. - blitz-shell: redraws requested by document updates are paced to FTS_MAX_FPS (default 30, 0 = unpaced); input still redraws at once. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
- A poll that only woke timers or streams (no mutation, no widget needing a redraw) no longer redraws the window. DocumentMutator records whether it mutated; BaseDocument::take_mutated and widgets_need_redraw decide. - A widget that is still moving after a frame asks for the next one, paced to FTS_MAX_FPS. - View::poll polls until the runtime is pending again, so its waker is always armed. It used to stop after one productive poll and relied on every redraw's window event to poll again, which is what kept idle windows redrawing forever. - FTS_FPS_LOG=1 prints frames per second and mean/worst frame cost. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Layered compositing, so a frame where only widgets changed skips Blitz's paint and vello entirely: - vendor/anyrender (0.11.1) gains anyrender::composite: the painter tells the renderer whether it may reuse its base and which layers to draw. - vendor/anyrender_vello (0.12.0): the window renderer rasterises the page into a kept base texture and, every frame, composites the base plus the layers (textures, faded; overlays) in one render pass. Rasterizer renders any widget scene into a texture of its own (shared vello renderer per device). No blocking wait on the GPU after present. - Widget::composite_texture: a widget whose picture is one texture can be a layer. blitz-paint leaves such widgets out of the page and lists them as layers (unless under a transform, filter, clip-path or mask; an opacity is carried by the layer), and lifts page content painted over a layer into an overlay drawn above it. - blitz-shell: a frame is layers-only when nothing but layer widgets changed; FTS_FPS_LOG reports layers-only frames. custom-widget is on by default (it was compiled out, with freeing dropped widgets' textures). - set_attribute to an unchanged value no longer counts as a mutation. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Stripping temporary traces took them with it. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
A press that lands on a custom widget captures the pointer: its moves and its release go to that widget, over it or not, until the button is released (or the pointer cancelled). A knob dragged off a face kept turning only while the mouse was over the face, and never heard a release made outside it — the drag stuck. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…d idle-frame fixes beside the custom-widget compositing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
winit-uikit decides whether a view is Metal-backed by asking if the *view* is a CAMetalLayer, which a UIView never is, so every redraw it is asked for becomes setNeedsDisplay — and UIKit only turns that into a frame while it is handling a touch. A frame a poll or a timer asked for waited for the next touch: every tap showed the frame before it, and nothing that moves on its own moved. `frame_now` draws at once on iOS (and asks elsewhere); an animating frame's successor comes back through the event loop, paced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct execution - A frame drawn on iOS (`frame_now`) never draws the next one inside itself: an animation's next frame goes back through the pacer's timer, always after a wait. A frame slower than the pace (the simulator paints on the CPU) otherwise recursed, and the run loop — touches with it — never got a turn. - anyrender_vello's Rasterizer declines a device without indirect execution (the iOS simulator's Metal): Vello's compute stages need it, and wgpu aborted the app at the first rasterised widget. The widget paints into the page instead, as on any renderer without a device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
fts-focus(custom-widget compositing, pointer capture) withios-safe-area-paint(safe area, touch, idle frames).Signal pins
6b54c521(FastTrackStudios/signal#15).🤖 Generated with Claude Code