A click on the Tabs panel's ruler added a stop, and a drag moved one,
at the fraction of a point under the pointer. The reference app's Tabs
page says that the tab ruler does not set stops in increments smaller
than 1, and that the X or Y field sets them as finely as 0.01 point.
The page does not name the unit of 1. The ruler here uses 1 point, the
unit the page gives for the X or Y field, in every General unit.
With Snap to Unit off and Shift not held, a stop placed or dragged on
the ruler now lands on the nearest whole point. With Snap to Unit on,
or Shift held, it still lands on the ruler's nearest tick, which on a
millimeter ruler is a fraction of a point. A position typed in the X
or Y field keeps its hundredths of a point.
Docs: docs/target-app-parity.md.
The Tabs panel named its alignment buttons Left-, Center-, Right- and
Decimal-Justified Tab and labeled the position field X for every text.
The reference app's alignments for vertical text include Top-Justified
Tab and Bottom-Justified Tab, and its position field is Y for vertical
text.
In vertical type the Left-Justified button is now named Top-Justified
Tab and the Right-Justified button is named Bottom-Justified Tab, in
their tool tips and for screen readers, and the position field is
labeled Y. The reference page does not say which buttons these are;
VectorCraft's own mapping follows the line direction: a line of
vertical type runs from top to bottom, so a Left stop aligns the text
to the top and a Right stop aligns it to the bottom. Screen readers now
call every alignment button by its name; before, they read its glyph.
In horizontal type the tool tips and the X label do not change.
Docs: docs/target-app-parity.md.
When text.tabs.set refused a new X (past 100,000 pt), the panel kept
the stops it had changed and sorted, so later in the same frame the
ruler painted the refused position and highlighted another stop.
apply_moved now moves and sorts a copy of the stops and keeps it only
when the stops are set.
A tab stop moved past another stop in the Tabs panel, by a drag on the
ruler or by a value typed in X, lost the selection. The panel sorts the
stops by position after the move but kept the selected index, so the
ruler highlighted another stop and the X, Leader and Align On fields
showed and changed that stop. With stops at 30 and 50 pt and the 50 pt
stop dragged to 10 pt, the panel selected the stop at 30 pt.
After a drag or an X value re-sorts the stops, the panel now selects
the moved stop at its new place, found by its position as a click that
adds a stop does. When text.tabs.set refuses the new position (an X
past 100,000 pt), the stops and the selection stay as they were.
Docs: docs/target-app-parity.md.
`shift_drag_snaps_a_stop_to_the_nearest_tick` landed while this branch was out
and asserts the tick an inch ruler used to carry: "the ticks are a tenth of an
inch (7.2 pt) apart", snapping 100 pt to 100.8. That tenth is what this change
removes -- an inch ruler divides the inch in half, not in ten.
The nearest tick to 100 pt is now 99 pt: 1 3/8 in, an eighth of an inch from
1 1/4. The test says so, and also asserts the result is a binary fraction of an
inch rather than a particular number, so it holds if the ladder is retuned.
A tab stop placed or dragged on the Tabs panel's ruler always landed
at a fractional position, and the ruler was numbered in inches with a
tick every 18 pt whatever the document's units. The reference app
snaps stops to the ruler units with Snap To Unit in the panel menu or
with Shift held during a drag, and numbers its tab ruler in the
document's units.
The ruler is now numbered in the General unit with the canvas rulers'
label step at the ruler's scale, and has a tick every tenth of a step.
The panel menu has Snap to Unit, checked while it is on. With it on,
or with Shift held, a stop placed by a click or dragged on the ruler
lands on the nearest tick, through Unit::snap_to_ruler_tick as a
Shift-dragged ruler guide does. A snapped click that lands on an
existing stop's tick selects that stop and records no undo step. The
stops still change through text.tabs.set. The reference app's page
names Shift for a drag only; Shift also snapping a click, and a
snapped click onto a stop's tick selecting that stop, are VectorCraft's
own rules.
Docs: docs/target-app-parity.md.
Repeat Tab on a paragraph's first tab stop spaced the copies by the
stop's distance from the ruler's 0. The reference app measures that
distance from the paragraph's left indent. With a left indent of 20 pt
and one stop at 50 pt, the copies were 50 pt apart instead of 30.
Repeat Tab now spaces the copies of the first stop by its distance
from the left indent of the paragraph whose stops the Tabs panel
shows: the paragraph the caret is in while the Type tool edits text,
otherwise the first paragraph of the first selected type object. A
stop left of the indent repeats at its distance from the indent: a
stop at 5 pt in a paragraph indented 20 pt repeats every 15 pt. The
reference app's help page does not cover this case, so this follows
VectorCraft's own rule. The copies of a later stop are still spaced by
its distance from the stop before it, and the limits of 20 copies and
2,000 pt stay.
Docs: docs/target-app-parity.md.
The Tabs panel menu passed false as each item's enabled argument and
its condition as the checked argument, so Clear All Tabs, Delete Tab
and Repeat Tab were always dimmed and, with a stop selected, showed a
check mark.
The three items are now enabled while the ruler shows tab stops and
show no check mark. Delete Tab and Repeat Tab act on the stop the ruler
shows selected. After Delete Tab removes the last stop, the ruler shows
the stop before it selected, and the next Delete Tab removes that one.
Clear All Tabs runs text.tabs.clear on the paragraphs the panel acts
on, so its undo step is named Clear All Tabs.
Docs: docs/target-app-parity.md.
egui reports drag_started only once the pointer is past its drag
threshold, farther from the press than the ruler's 5 px hit tolerance,
so hit-testing the current pointer usually missed the stop. Hit-test the
press origin instead, falling back to the pointer position.
Fixes#1458
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both fields read their text from the tab stop again every frame, so each
keystroke was lost before Enter or a click away could apply it: the
field seemed to clear itself while typing. They now use the panels' text
field, which keeps the text being typed until it is committed. The
leader keeps spaces (". " for spaced dots), up to eight characters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Tabs panel's ruler was a fixed strip in the panel, at no relation to
the text on the canvas, so stops couldn't be placed by eye. Above Text
(Position Panel Above Text) floats the panel just above the selected
text, as wide as its ruler needs to put 0 on the text's left edge and
its marks at the canvas zoom: each stop sits over the column it sets.
Floating panel groups keep a width they are given.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each paragraph has its own alignment, indents, spacing, hyphenation, tab
stops and paragraph style (TextObject.paras; para stays paragraph 0's, so
old files and readers work). Layout, editing (split/merge), threads, the
Paragraph/Tabs/Paragraph Styles panels, ranged text.setStyle/setFormat/
tabs/paraStyle.apply, SVG export/import and the fuzzers follow.
Strings in code stay English and are the lookup keys; `crates/ui-egui/src/i18n`
maps them to display text at render time from one `*.tsv` catalog per language
(`LANGUAGES` registry, `tl!`/`t`/`tr`/`tr_ctx`/`tr_id`/`tn`/`fmt`). Command ids,
menu paths, `ui.menu.list`, the control channel, the CLI, MCP, status messages
and undo labels never see translated text, so agents, scripts and the existing
tests are unaffected (under `cargo test` the UI is always English).
Wired: in-window menus and the macOS native menu (rebuilt on a language change),
dynamic labels (Undo X, Reset X, Last Effect: X as templates), the command
palette (matches English and translated text), dock tabs, panel tooltips, the
toolbar (category names now cut by characters, not bytes), the dialog frame, the
shared widgets, the Preferences dialog, the shortcut editor, the hint and status
bars, the Home screen, and the literals of every panel and dialog.
Preference `interfaceLanguage` (User Interface › Language): `auto` or a code.
`auto` follows `VECTORCRAFT_LOCALE`, `LC_ALL`/`LC_MESSAGES`/`LANG`/`LANGUAGE`,
the macOS preferred languages and, on Windows (which sets no `LANG`), the user
locale from `HKCU\Control Panel\International`. `zh-TW`/`zh-HK`/`zh-MO`/
`zh-Hant-*` resolve to `zh-hant`; Simplified locales stay English until a
`zh-hans` catalog exists.
`zh-hant.tsv` (~2,850 rows) is clean-room, in the vocabulary used in Taiwan;
terms shared with PhotoCraft reuse its wording, vector terms use everyday
vector-drawing vocabulary. Tests: catalog well-formedness (placeholders,
ellipses, command ids, no Simplified characters), locale resolution, and two
coverage tests that fail when a menu string, panel/tool/preference label,
dialog button, hint-bar fragment or `tl!` literal has no row in a language
marked `complete_menus`. `VECTORCRAFT_I18N_DUMP=file cargo test -p
vectorcraft-ui-egui dump_source_strings` lists the strings for translators.
Also: two clippy findings under rustc 1.97 fixed in passing.
Integrated with main (squashed onto it, 42 commits on): the Czech interface
(#213) moves from its Rust table to `i18n/cs.tsv` on the registry (all 556
rows, Czech plural rule, its menu-coverage and glyph tests kept); Find Font's
resolution notes and the canvas context menu's labels are translated and
covered by the catalog tests (`menus::CONTEXT_LABELS`); the glyph test checks
CJK text only when the craft-fonts build input is embedded (#220 moved the
Japanese fonts out of this repository); a language saved by an older version
carries over to `interfaceLanguage`; Windows' locale lookup runs reg.exe by its
full path.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The General unit is the active document's units: Preferences > Units >
General sets them (one undo step) and is the units new documents start
in (and the unit shown with no document open); Document Setup changes
only the document. The Transform panel, Properties, the Control bar, the
Info panel, rulers, the status bar, Align spacing, Tabs, paragraph
indents, Pattern Options, dialog distances (Move, shape sizes, path
dialogs, Artboard Options, effect distances, Transform Each, parameter
dialogs), New Document and the canvas measurement labels (smart-guide
sizes, moves, the Measure tool, the Live Corners radius) all follow it,
live. Units > Type drives font size, leading and baseline shift; Units >
Stroke also reaches the Appearance panel and the Magic Wand weight
tolerance. Typed units ("5 mm", "12 pt") work in any field; all ten
units are choices; length preferences show in their unit and accept a
unit; offset/simplify/grid/type-size params read typed units. Typed text
starting with a multi-byte character no longer panics the parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>