widgets::keep_focus (Project panel rename and comment, Render Queue, Essential
Graphics and Timeline fields) selected the whole text whenever it saw its field
without the keyboard focus, not just on the frame the editor opened. Anything
that took the focus for a frame (an input method, another handler) then
re-selected the name, so the arrow keys seemed to do nothing and Backspace erased
the whole name, as reported on Linux. The editor now remembers the pass it was
last shown: it selects all only when it opens and otherwise just takes the
keyboard back with the caret where it was.
The harness does not reproduce the report's Linux behaviour itself (arrows reach
the field there); the tests cover the path that did: a unit test drops the focus
for a frame and checks the caret survives (and that reopening selects all), and a
kittest renames a Project item and a Timeline layer with Left / Right / Up /
Down / End and Backspace, editing part of each name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
Cause: two things kept Arabic out of a layer name on Windows.
- Windows switches the keyboard layout with Alt+Shift. Releasing Shift
before Alt left Alt held alone, and the in-window menu bar (#279)
counted the release as an Alt tap: it took the keyboard focus, the
rename field closed, and the letters typed next were swallowed by the
menu bar. The Project panel's rename field lost them the same way.
- The UI fonts are Inter plus the CJK system fallbacks, so Arabic,
Hebrew, Indic and Thai names that did arrive (pasted, or from a
project) showed as missing-glyph boxes.
Fix:
- An Alt tap only counts when Alt went down with no other modifier
held, so Alt+Shift in either release order is no tap; a plain Alt tap
still focuses the menu bar.
- The UI registers installed system faces for Arabic, Hebrew, Devanagari,
Bengali, Gurmukhi, Gujarati, Tamil, Telugu, Kannada, Malayalam and Thai
after the CJK ones, the same way (native only; the web build has no
system fonts). A face covering several scripts is registered once
(Segoe UI: Arabic and Hebrew; Nirmala UI: every Indic script).
- The text engine's fallback tries each script's plain platform families
(Segoe UI, Nirmala UI, Leelawadee UI, Geeza Pro, Kohinoor, Noto…)
before any covering face, so neither the UI nor a text layer gets a
calligraphic face that merely sorts first.
- The Timeline's rename field registers `timeline.layer.<id>.nameEdit`.
egui 0.36 shapes each run of one face, so Arabic letters join and a word
reads right to left; it has no bidirectional layout, so the words of a
multi-word Arabic name (and mixed Arabic/Latin) still follow each other
left to right.
Tests: ui_inline_edit a_layout_switch_keeps_the_name_field_and_arabic_
names_are_kept (Alt+Shift both ways in the layer rename, Arabic typed and
stored; Hebrew/Devanagari/Thai in the Project panel; a plain Alt tap
still focuses File; failed before: the field closed), theme
installed_script_fallbacks_draw_arabic_hebrew_indic_and_thai_names
(failed before: replacement glyph for Arabic) and
arabic_words_are_joined_and_read_right_to_left, text
other_scripts_fall_back_to_a_plain_system_family and the script
classification.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What: each property row that can take an expression (it has a stopwatch) now
has a property pick whip in the Parent & Link column, as in After Effects
CC 2018 and later (automation id `timeline.prop.<uid>.propertyPickWhip`).
Dragged onto another property's name or one of its values, it runs
`prop.pickWhip`: the reference is added as the property's expression, or
replaces the one it has, and the expression is enabled. A drop on one value
picks that dimension (`position[1]`).
The parent, expression and property pick whips share one widget and one
drop path (rubber band, autoscroll, dimension pick); only the expression
pick whip inserts at the cursor of an expression being edited.
Pick whips (expression and property) now also drop on properties in
Effect Controls: its property rows register as targets while a pick whip
is dragged, with their per-dimension value fields.
Why: the last part of #363. Adobe's docs say the Property Link pick whip
adds the expression without enabling expressions first, and a pick whip
outside an edit replaces the whole expression text. No modifier keys for
the property pick whip are documented, so none are implemented.
Tests: `the_property_pick_whip_links_a_property_to_another` (ui_inline_edit)
drops it on a property's name, on one of Position's values, on a property
with a disabled expression (replaced and enabled), on an Effect Controls
slider and one value of a point, and on itself (no change). It fails
before the change; with Effect Controls' targets removed it fails at the
slider. Looked at the result with the headless snapshot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tab in a value being typed (hot text) dropped the keyboard: egui moved
the focus to the next widget, and the inline editors asked for the
focus again every frame, which also reset what keys a field keeps for
itself.
As in After Effects, Tab now commits the value and types in the next
value field of the same panel (the next dimension, then the next
property's), Shift+Tab the previous one. The hot-text fields of each
panel register in drawing order; the dock tags each panel's Ui so the
order stays within it. `keep_focus` asks for the focus only when the
field doesn't have it.
Tests: tab_commits_a_value_and_moves_to_the_next (ui_inline_edit:
Timeline and Effect Controls).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The inline expression field took its height from the saved
expression, so lines typed into it ran over the rows below until the
expression was committed.
Its height now comes from the text being typed (up to 8 lines, as
before), and its bottom edge drags it to any height in whole lines, as
in After Effects; the dragged height is kept per property in the
Timeline state (`expr_lines`).
Tests: the_expression_field_grows_while_editing_and_drags_taller
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dropping the expression pick whip on one of a property's values (the X
of Position) referenced the whole property. As in After Effects, a
drop on one value now picks that dimension: `transform.position[0]`;
a drop on the name still picks the whole property.
`prop.pickWhip` takes `target.dimension`.
The rest of #363 is unchanged: inserting at the cursor landed with
#284, and After Effects' property pick whip in the Parent & Link
column is left for later.
Tests: pick_whip_picks_one_dimension_of_the_target (engine),
the_expression_pick_whip_picks_the_dimension_it_is_dropped_on
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pick whip dragged to the top or bottom of the Timeline's layers left
them where they were, so a property out of view couldn't be picked.
While a pick whip (expression or parent) is held within a row of the
top or bottom edge of the layers, or past it, the layers scroll that
way, faster the deeper, as in After Effects.
Tests: the_pick_whip_scrolls_the_timeline_at_its_edges
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Time Display Style could only be changed in Project Settings. As in
After Effects, Ctrl-click (Cmd-click on macOS) on the current-time
display of the Timeline or the Composition panel now toggles it between
Timecode and Frames (Feet + Frames goes to Timecode). It runs
`file.projectSettings {timeDisplay}`, so it is one undo step.
Under the Timeline's display the other style shows now (frames under
timecode, timecode under frames), as in After Effects; it always showed
frames.
Tests: ctrl_click_on_the_time_display_toggles_timecode_and_frames
(ui_inline_edit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging an expression's pick whip always replaced the whole
expression, also while it was being edited (and the editor then wrote
its old text back over the reference). As in After Effects, a pick
whip pressed while the expression is being edited now puts the
reference at the cursor, replacing the selected text, and the editing
goes on with the cursor after the reference. Without an edit in
progress it replaces the expression as before.
`prop.pickWhip` takes the text being edited (`expression`) and the
selection in it (`range`, in characters) to insert there.
Tests: the_expression_pick_whip_inserts_at_the_cursor_while_editing
(ui_inline_edit), pick_whip_inserts_into_the_expression_being_edited
(engine).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cause: the click-to-edit fields (hot numbers and revolutions, the Timeline
time display and layer comments, Project rename/comment, the Render Queue
output path, Essential Graphics control names) opened a text edit with
egui's default caret at the end of the text, so typing appended to the old
value. The Timeline time display, layer comment and Essential Graphics name
editors also re-requested focus before checking `lost_focus()`, which hid
the loss, so Enter, Escape and clicking away never closed them.
Fix: a shared `widgets::keep_focus` keeps an open inline editor focused and
selects its whole text on the frame it opens (`widgets::select_all`, which
the layer rename now reuses). Those editors check for a lost focus first,
and Escape now cancels in the time display and the Essential Graphics
name editor.
Test: ui_angles `typing_replaces_the_clicked_value` (degrees and revolutions
in Effect Controls, Timeline and Properties, typed without Ctrl+A);
ui_inline_edit `project_rename_replaces_the_name` and
`timecode_field_replaces_commits_and_cancels`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>