Skip to content

Add shapes, the Insert tab, and an optional Insert button on the toolbar - #137

Merged
marcosqlbi merged 4 commits into
mainfrom
feature/shapes
Sep 17, 2026
Merged

marcosqlbi merged 4 commits into
mainfrom
feature/shapes

Conversation

@marcosqlbi

@marcosqlbi marcosqlbi commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Pull request 3 of the 1.6.0 plan in docs/design-objects.md, closing #130. It adds the
eight shapes, the Insert tab they come from, and the two Toolbar preferences that decide
where the Insert tools live and what happens after one has been drawn. It builds on #135,
which moved the per-type questions onto the records themselves — this adds a record and a
geometry and touches almost none of the window's switches.

Why it is shaped this way

A shape is taken hold of by its outline, never by its interior (decision 11). That is
the whole reason a shape can be a container: ink drawn inside one links to it and travels
with it, and a tap in the middle still reaches whatever is drawn there. The band is the
same eight screen pixels a frame's edge uses.

One description of the outline serves everybody. Core.Geometry.ShapeGeometry returns
a shape's outline as a start point and a list of line and arc steps. The surface hands the
arcs straight to WPF, so a circle stays a circle at any zoom; the hit test and the area
tests walk the same outline flattened to a polygon, sixty-four points to a full circle.
Nothing derives the geometry a second time, which is what stops the renderer and the hit
test disagreeing about where a shape's edge is — and it is what pull request 6 will read
to write native PowerPoint and PDF geometry.

The area asks the outline too. A rubber band over the empty corner of an ellipse's box
does not take the ellipse; one that crosses the curve does. A box test would have been
cheaper and would have felt wrong for exactly the shapes people draw around things.

Version 7 is asked for by the board that needs it. NeedsVersion7 is a one-line
predicate the label and connector work extends; a board with only frames still writes as 6
and one with neither as 5, so a board keeps opening in the release that can read it.

The two "applies when" tests in the property bar are named statics. HasPrimaryColor
and HasThickness are each one line for the text and connector pull requests to extend,
rather than a predicate repeated at every row. Applying a color, a thickness, or a fill
replaces every selected stroke and shape as one step and becomes what the next shape is
drawn with.

Free resize is the one thing a shape does differently. Its corner handle takes the
width and the height the pointer is at, and Shift keeps the aspect — the opposite way round
from a picture, which is aspect-locked. It applies only when the selection is exactly one
shape; a set still scales as a set.

The editable export keeps the shapes visible. The overlay picture now carries the ink
and the shapes, and the vector PDF path — which draws the ink itself — still gets an
overlay for the shapes alone. Native shapes are pull request 6; until then nothing
disappears from a deck.

The toolbar's width

Measured from the XAML for the floating palette, at the default where the Eraser is not on
the bar. With the preference off both new controls are Collapsed and contribute nothing,
so the width is the 1.5.2 width to the pixel.

Layout Insert and Lasso on the toolbar off on
Compact (Chevron or Icons beside the size chips) 140 202
Dual palette (the default) 286 310

The compact pair grows by the Insert button (42) and the Select chevron (20); the dual pair
by 24, which is what the Insert button adds to the shared color column that sets the
palette's width. Both pairs were confirmed against a running copy: the measured deltas are
62 and 24 exactly. With the Eraser on the bar — that is, with finger or mouse drawing on —
the compact pair is 182 and 244.

Decisions the plan did not settle

  • The shape defaults start at the pen's defaults, not at the live pen. Decision 5 says
    a shape's first outline color and thickness are "the current pen's". Reading the pen's
    setting at the moment a shape is drawn would make the same gesture produce different
    shapes depending on which color was last chosen for ink, so ShapeSettings instead
    starts at InkPalettes.DefaultPen (vermillion, 4) and is last-used from then on, which
    is what the settings table asks for.
  • Proportional corners and slants. A rounded rectangle's radius is a fifth of its short
    side, a parallelogram slants by a quarter of its width, a block arrow's head starts at
    three fifths and its shaft is half the height. Proportional rather than absolute, so a
    shape dragged larger keeps the look it had when it was drawn.
  • The pentagon fills its box rather than being inscribed in a circle inside it: a shape
    dragged out of a rectangle should occupy that rectangle.
  • The Insert row is sticky, which is what Add text labels: click to type on the board #136 settled while this was in flight:
    picking a tool is the start of drawing with it, and the row is where the next one comes
    from. This branch had it closing on a pick and took main's answer instead.
  • The Select chevron is in the compact layouts only. The plan names the Insert button
    for the Dual palette layout and says nothing about the chevron there; the dual palette's
    widths are set by a shared column and a chevron would disturb it for one setting. Lasso
    stays reachable from the Edit row in every layout.
  • The glyphs are drawn by hand, as the plan allows: Fluent UI System Icons has no
    outline shape set. Each is two nested contours filled even-odd on the same 24-unit grid,
    including the Insert button's.
  • Alt+I opens the Insert tab. F, E, V and H were taken.
  • Ctrl+C with one shape selected still copies nothing, as it does for a frame today.
    Decision 12 names a text container and a picture as the single-object cases and sends
    everything else through the rasterizer only when more than one thing is selected. Worth a
    look when the native export lands.

Taking in the text labels (#136)

origin/main was merged in after #136 landed, so the two sit side by side rather than in
two places:

  • One Insert tab and one row: the eight shapes, a separator, then Text, which is the
    order the flyout shows and the order the connectors will be inserted into.
  • NeedsVersion7 covers labels and shapes; the archive keeps both cases and both sets of
    DTO fields.
  • The property bar's tests now read: Color for a stroke, a shape, or a label; Thickness for
    a stroke or a shape; Fill for a shape; Font for a label. A color reaches strokes and
    shapes as one step, and labels through their own path — the one that also changes the box
    being typed into while a label is open.
  • The overlay picture carries the ink, the labels, and the shapes, and the vector path
    still emits one for the design objects alone.
  • After inserting an object now governs the Text tool too. It was written for the
    shapes, but decision 3 is about every Insert tool, so a committed label either leaves the
    tool for the next click or hands it back to Select.
  • The toolbar's Insert flyout ends with Text, and the Insert button reads as on for either
    tool.

Tests

Core harness: the outline of every kind at a known box; the band hitting the outline and
missing both the middle and the outside, and narrowing with the zoom; an area taking a
shape by its outline under both rules and refusing an empty corner of its box; a stroke
touching only a shape linking to it; the archive round trip with and without a fill, the
three versions, and an unknown kind normalizing to a rounded rectangle; the round trip and
normalization of both Toolbar settings and the shape defaults.

To try by hand

  • Draw each of the eight from the Insert tab with the pen, and with a finger under Finger
    drawing. Only a mouse was driven here.
  • Shift while dragging one out, for the square.
  • Tap without dragging, for the 160 x 120 shape at the tap.
  • Turn on Insert and Lasso on the toolbar and check the flyout and the Select chevron
    with a pen, which reaches them through the palette's own stylus path rather than through
    mouse promotion.
  • Compare the toolbar's width against 1.5.2 with the preference off, in both layouts.
  • Draw ink inside a shape, move the shape, and check the ink comes with it.
  • Set After inserting an object to Return to Select and draw one.
  • Export a board with shapes to an editable deck and to a PDF, and check they are in both.
  • A drag that is long in one direction and flat in the other makes a shape with almost no
    height. It is what was dragged and Ctrl+Z removes it, but it is worth a look before the
    connectors land on top of it.

🤖 Generated with Claude Code

marcosqlbi and others added 4 commits September 17, 2026 12:57
ShapeBoardObject is a container selected by an eight-pixel band along its
outline rather than by its interior, so what is drawn inside one stays
reachable. ShapeGeometry answers for the outline once - as arcs for the
renderer and as a polygon for the hit test and the area tests - so nothing
derives it twice.

A board that holds a shape is written as version 7; one with only frames
stays 6 and one with neither stays 5, extending the rule frames set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The surface draws a shape from the Core outline as one geometry, filled
then stroked with the outline thickening with the zoom as ink does.

The property bar's Color and Thickness rows now say which objects they
apply to through named tests that the label and connector work extends by
a line each, and a Fill row appears when everything selected is a shape.

The editable export's overlay picture carries the shapes as well as the
ink, and a writer that draws the ink itself still gets an overlay for
them, so nothing disappears from a deck or a vector PDF before the native
mapping lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An Insert tab joins the strip between View and Help with the eight shapes
and Alt+I. Picking one selects a shape tool: a drag draws the shape,
Shift constrains it to a square, and a tap inserts one at 160 x 120. The
new shape becomes the selection so the property bar can change it, and
the tool stays for the next drag unless After inserting an object says to
hand it back.

Insert and Lasso on the toolbar, off by default, adds an Insert button
beside Select with the same shapes in a flyout, and a chevron on Select
offering Rectangle and Lasso that stays in step with the Edit row's
toggle. Both are Collapsed when it is off, so the toolbar keeps the width
it had.

Select moves a shape by its outline band and resizes it freely from the
corner handle, where Shift keeps the aspect - the opposite way round from
a picture, which is the one difference a shape is allowed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Text labels landed first, so this takes them in and puts the two side by
side rather than in two places.

- One Insert tab, one row: the eight shapes, a separator, then Text, which
  is the order the flyout shows and the order the connectors will be
  inserted into. The row is sticky, as main made it.
- NeedsVersion7 covers labels and shapes; the archive keeps both cases and
  both sets of DTO fields.
- The property bar's tests now read Color for a stroke, a shape, or a
  label, Thickness for a stroke or a shape, Fill for a shape, and Font for
  a label. A color change reaches shapes and strokes as one step and
  labels through their own path, which is also the one that changes the
  box being typed into.
- The export overlay carries the ink, the labels, and the shapes, and the
  vector path still emits one for the design objects alone.
- After inserting an object now governs the Text tool too: a committed
  label either leaves the tool for the next click or hands it back to
  Select, which is what decision 3 asks of every Insert tool.
- The toolbar's Insert flyout ends with Text, and the Insert button reads
  as on for either tool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@marcosqlbi
marcosqlbi merged commit 783025c into main Sep 17, 2026
5 checks passed
@marcosqlbi
marcosqlbi deleted the feature/shapes branch September 17, 2026 11:16
marcosqlbi added a commit that referenced this pull request Sep 17, 2026
)

Pull request 7 of the 1.6.0 plan in `docs/design-objects.md`: the
release cut. The five
features are on `main` — the grid (#134), selection by area (#135), text
labels (#136),
shapes and the Insert tab (#137), and connectors (#139) — and none of
them touched the
prose, so this is where a person is told what they got. **#138, the
native editable export,
must merge before this one**: the release notes and the guide both say
shapes and labels
leave as objects in a deck and a PDF, which is true once it lands.

`VersionPrefix` goes 1.5.2 → 1.6.0, so `CHANGELOG.md` gains the matching
section and the
**Release notes** check applies to this pull request.
`./scripts/release-notes.ps1 -Mode
Verify -Version 1.6.0` passes, and `dotnet build Whiteboard.sln -c
Release` is clean.

## What it affects

- **`CHANGELOG.md`** — one `## 1.6.0 - 17 September 2026` section with
three entries, which
is the shape of 1.5.0 (3), 1.5.1 (1) and 1.5.2 (2): the Insert tab with
shapes,
connectors and text, selection by area, and the grid. The toolbar
preference is one
sentence inside the first entry rather than a fourth entry, because it
is where somebody
meets it. The first entry also carries the one thing a person deciding
whether to upgrade
has to know: a board holding any of the new objects does not open in
1.5.x.
- **`README.md`** — three bullets in *Included in the application*,
seven rows in
*Controls*, the Preferences lists in the bullet and in the table, and
the containers
paragraph, which now says shapes and labels are containers and that a
shape is picked up
  by its outline.
- **`site/guide.html`** — a new *Shapes, connectors, and text* section
after *Text and
code*, a paragraph on area selection under *Containers*, one sentence on
the grid under
  *Navigation*, and the Insert tab named in the command-strip section.
- **`site/shortcuts.html`** — Insert, Lasso and Grid rows, the rubber
band and Ctrl-to-add,
and the Alt-mnemonic row extended with Alt+I, Edit L, View G, and Insert
T.
- **`site/index.html`** — two feature tiles, shapes/connectors/text and
select by area.
- **`docs/decisions.md`** — decision 31, why design objects are ordinary
board objects,
why a shape is taken by its outline, and why the grid is an application
preference. The
rest stays in `docs/design-objects.md`, whose status line is now shipped
in 1.6.0.
- **`TODO.md`** — the current version, which still said 1.3.0, and what
1.6.0 left out.

## Readings taken, since nobody could be asked

- **`site/compare.html` is untouched.** It has no row saying Whiteboard
lacks shapes, text,
a grid or multi-select; the nearest, *Ink and objects*, is a statement
about strokes
travelling with a container and is still true. The brief said to change
such cells and
  nothing else, so nothing else changed.
- **`site/faq.html` is untouched.** No answer on it became wrong.
- ***Architecture* in `README.md` is untouched.** The `Core` bullet
names no types — it
says "world geometry, camera math, retained board objects, commands, hit
testing" — so
there was no list for `ShapeGeometry`, `ConnectorGeometry` and
`RotatedRectangle` to join,
  and the brief said to leave it alone in that case.
- **Escape is an edited row, not a new one**, in both the README table
and the shortcut
  list: Escape already had one, and a second would contradict it.
- **`site/changelog.html` is untouched**, as it is generated from
`CHANGELOG.md` at
  deployment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@marcosqlbi marcosqlbi mentioned this pull request Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant