* fix(engine): refuse to convert an expression on a layer of more than 2^18 frames
* address review: refuse a non-positive frame duration; count a partial last frame; check every selected layer before converting
* Convert Expression to Keyframes: cap at 2^20 frames (a 3-hour 60 fps comp is 648,000)
In a comp whose Start Timecode is negative, the current-time display shows
the first frames as `-0:00:00:12`, but typing that back (the Go to Time
dialog opens with it since #3419, and the Timeline's time field takes it)
read the leading `-` as "move back", so OK on an unedited dialog jumped to
another frame. There, a `-` before a full timecode is now the time's sign,
as it already is before Feet + Frames; a bare `-n` still steps back, and
comps that start at 0 or later are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A typed +10 on several selected layers went through the same-change
offset path, so when the edited layer's range cut its change short
(Opacity 95 + 10 stops at 100) the others moved by 5. Send the typed
operand as prop.set addend: each other layer takes its own value plus
it, within its own range; scrubs keep the offset path.
A typed *-1 (the edited layer clamped at 0 by the panel) or a change
by the same amount could take another selected layer's Mask Feather
below 0. Clamp it at 0 there, as the panels do for the edited layer.
Number fields read + - * /, brackets and signs through a small bounded
parser (crates/ui-egui/src/num_math.rs): no code is run, length and
nesting are capped, every step must stay finite and a division by zero
keeps the old value with no undo step.
Hot-text values (Timeline, Effect Controls, Properties and the other
panels) also take a leading *, / or + applied to the value shown, as
the reporter describes for After Effects (not confirmed in public
docs). A leading - stays a negative number. Hot text used to read +50
as 50; it now adds 50. On an angle the step goes from the signed
remainder (+10 on 0x-30 is 0x-20). With several layers selected each
layer takes the step from its own value: + through the offset path, *
and / through a new prop.set factor. Typed results in whole-number hot
text round instead of truncating.
Dialog fields (the shared drag_value wrapper, Essential Graphics,
Gradient Editor, colour picker) re-read their text per keystroke, so
they take absolute arithmetic only.
Fixes#3195
The shape-tool double-click, Cmd+W handling and their tests move to the split header, menus/invoke
and engine test modules (#2491); the docking check in the Cmd+W test uses the craft_ui dock API.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U