Ctrl+[ (and Cmd+Shift+,) shrank any size above 66 straight to 66: the
step list stopped at 66, so the largest list size below 120 was 66.
The list now holds PowerPoint's sizes up to 96, as the Font Size box
already listed them, and above 96 shrink steps back 8 points at a
time, as grow steps up: 120 -> 112 -> 104 -> 96 -> 88 -> 80 -> 72 ->
66. The Font Size box lists the same SIZES instead of adding its own
tail.
Fixes#2059
* engine: apply the photo album limits when reopening an album
* address review: refuse an album Update when the editor seed omitted items, instead of deleting them
* Apply review fixes
- crates/engine/src/cmd/insert/album.rs
* address review: refuse an album replace when a slide past the item limit holds other content
* address review: notes and comments on a shapeless slide past the item limit block an album replace
* address review: slide background, transition, animations, colour map and tags count as slide content
---------
Co-authored-by: autocraft-bot[bot] <354215646+autocraft-bot[bot]@users.noreply.github.com>
* engine: check the automation write roots before appending to the user dictionary
* address review: check the original Path for the dictionary write; isolate the confinement test
* Spelling: stop an affix header's count at the end of the file, share aliased dictionaries
* address review: share the first inserted dictionary on a concurrent load
* engine: edit each target shape once and read its box without copying it
* engine: check the fill colour field in the repeated-ids test
* Apply review fixes
- crates/engine/src/tests.rs
* address review: shape.lock skips targets inside a targeted group
* address review: dedupe shape.lock targets before walking groups
---------
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
* engine: count a slide's notes, comments and tags in the undo memory budget
* engine: drive the undo-budget test through slide.rename
* address review: size text cost by the Paragraph and Run struct sizes
* engine: extend an ink stroke in place and cap a gesture's samples
* engine: move the ink extension out of drag
* address review: close scribbles at the release point; keep vertices when a capped press adds none
* Apply review fixes
- crates/engine/src/tools/pointer.rs
* raise ink gesture cap to 100000 points
---------
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
Ungrouping a top-level group and a group inside it in one call, in
either order, frees the shapes of both onto the slide in the top-level
group's place, drawn where they were, returns them once each and
selects them. The other ungroup tests leave the outermost group
unnamed, so none checked that the inner group is found at the slide's
top level after the same call frees the group around it.
Ungrouping a middle group, the group around it and then the innermost
group, all inside a fourth group, returns the four freed rectangles
once each, selects them, and leaves them in the fourth group in order.
Before the previous commit, the innermost group was listed twice and
replaced once, so the call also returned that group's id after the
group was gone.
The test compared the pasted shape's connection sites with the original's
through zip, which stops at the shorter list. It now checks that both have
the same number of sites first.
The test compared each shape's connection sites before and after through
zip, which stops at the shorter list, so a shape that lost sites would still
pass. It now checks the counts first. The case with three nested groups also
checks that the freed shapes are selected.
The test compared the copy's connection sites with the original's through
zip, which stops at the shorter list. It now checks that both have the same
number of sites first.
Found while testing crates/engine/src/cmd/text/caret.rs:60 (delete_dir, forward) with crates/model/src/edit.rs:423 (char_move)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
edit.copy and edit.cut put a shape inside a group on the clipboard with
its box in the group's own space. Pasted, it landed where it would be
drawn without the group's move, stretch, turn or flip: a shape from a
group moved 200 pt to the right pasted 200 pt left of where it was
drawn, and one from a group stretched to twice its width pasted half as
wide.
The copied shape now carries the box it is drawn in on the slide, mapped
out through each group around it as Ungroup maps a freed shape. It
pastes at the slide's top level where it was drawn, offset as any paste
on the same slide is. Cut still deletes the shape from its group.
A turned shape in a group stretched more one way than the other is drawn
skewed, which a box cannot hold; it pastes with the unskewed box Ungroup
gives it. Copying with a text caret is unchanged.
Test: a_shape_copied_from_a_group_pastes_where_it_was_drawn.
arrange.ungroup looked only at the slide's top-level list, so a group
inside another group answered `{"ids": []}`, stayed grouped, and the
selection was emptied.
Such a group's children now take its place among the outer group's
children, with their boxes mapped into the outer group's space by
child_to_parent, as for a top-level group, so they are drawn where they
were. They are selected and returned. A shape freed from a named group
and then from a named group around it is listed once.
Test: ungrouping_a_nested_group_frees_its_shapes_into_the_outer_group.
edit.duplicate took its copies from the slide's top-level list only, so
duplicating a shape inside a group answered `{"ids": []}` and changed
nothing.
A shape inside a group is now copied into that group, after its other
children, and the copy is selected and returned. The duplicate offset is
converted into the group's space, so the copy is drawn as far from the
original on the slide as a top-level copy is, however the groups around
it are moved, stretched or turned. Duplicating a moved copy again
repeats the move (#966), measured on the slide. A shape whose group is
duplicated in the same call is copied only with the group's copy.
Test: duplicating_a_shape_in_a_group_copies_it_into_the_group.
shape.merge worked on the slide's top-level list. Given shapes inside a
group, it merged them from their boxes in the group's own space, left
them in the group and added the result to the slide's top level, so in a
moved or resized group the merged shape was drawn away from the shapes
it came from, which stayed drawn as well.
A call whose shapes include one inside a group is now refused with the
shape's name and a request to ungroup it first, and the slide is left as
it was. Merge Shapes is unavailable while a shape inside a group is
selected; a call that names top-level shapes in `ids` still runs
whatever is selected, as it does when nothing is selected.
Test: a_shape_in_a_group_is_refused.
Found while testing crates/engine/src/cmd/format/paragraph.rs:123-129
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/shape/appearance.rs:8 (adjust, index via usize_param); same pattern in quick_style at :559
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On Windows a folder can be set to tell case apart, and then IN beside the root in passed as inside
it, because names were compared with to_lowercase. Both paths have the spelling the file system
gives them by then, so another spelling of the root still matches with an exact compare. A missing
name keeps the spelling it was given, but it can only match a root that exists, and resolve has
already found that one in its real spelling.
is_device_name also missed COM¹, COM², COM³ and LPT¹, LPT², LPT³, which Windows reserves too.
It also cut the name only at a period, so COM1: and NUL:x were not seen as device names.