Repository navigation
Conversation
Ink keeps its points, per-point pressure, pen (pen, pencil, highlighter), width and colour effect in Shape::ink instead of x,y text; old text strokes upgrade on load. Outline and triangle geometry, opacity and effect colours live with the model.
fill_alpha registers one ExtGState per opacity, so highlighters and pencils print translucent.
Ink saves as InkML parts behind mc:AlternateContent, with a DrawingML path (and a urn:gridcraft:ink:v1 extension keeping pen, pressure and effect) for readers without InkML. Reading is bounded (points, traces, nodes, a workbook-wide point budget) and never follows external parts. Plain DrawingML polylines from other files still open as editable ink (storytold#551), now in the typed model; ink left of column A or above row 1 keeps its place across saves.
Editable pen gallery (pen, pencil, highlighter; colour, width, effects), stroke and segment erasers (segments cut at crossings, one undo step per drag), freehand lasso select/move/delete, smoothed strokes with pressure or inverse-speed width, and ink in printed PDFs.
The Draw ribbon (pen gallery and settings, colours and effects, eraser modes, lasso, trackpad switch), ink painted as the saved stroke (translucent and effect strokes), and the floating trackpad canvas fed by TrackpadFrame: one finger draws, two move the canvas, any key exits. Force Touch press maps to stroke force (touch thinnest, click and press thicker). Labels in every catalog, including Serbian.
macOS AppKit indirect touches, Linux evdev, Windows precision-touchpad HID; the device is probed off the main thread and opened only when the mode starts. On macOS touch events drain in a private run-loop mode (draining the default mode inside winit's handler froze the UI for 1-2 s), the pad uses NSPressureBehavior.primaryGeneric for continuous stage-1 pressure without a force click, and a new finger identity starts a new stroke.
The two [1,2,1] passes now move a point at most 1.5 px, and the curve is a centripetal Catmull-Rom spline through every sample (points every ~3 px on long segments), so a fast zigzag keeps its full-height turns instead of curling into loops.
A stroke reads every PointerMoved of the frame (times spread across it) and keeps following the pointer over the headers or out of the window, like Excel; the grid clips what lies outside. On macOS, while a Draw tool is on, mouse drags aren't coalesced and the pad uses generic pressure, so a hard press mid-stroke doesn't force-click.
The precision-touchpad descriptor reader spells out what it reads: usages by name and page (CONTACT_COUNT is Digitizer 0x54), item types and tags as in HID 1.11 §6.2.2, named bounds, and a FingerState instead of a four-slot array. Tests build their descriptors item by item. No behaviour change.
Superseded Draw-tab PRsWe are closing the small Excel-parity Draw PRs in favour of this work. The issues they targeted should be tracked here:
This PR replaces the fixed-pen Draw tab with the editable pen gallery and shares Still separate and not covered here:
Round-trip evidence for this PR's InkML workExcel 16.0 (Build 20430) writes a Draw-tab stroke as |
Brings the Draw tab closer to Excel's. Ink gets a typed model with pressure, Excel's InkML file format, editable pens and erasers, and Draw with Trackpad with Force Touch pressure on macOS.
What changes for users
NSPressureBehavior.primaryGeneric, so there is no force click or Look Up.mc:AlternateContent, with a DrawingML fallback for readers without InkML. Plain DrawingML polylines from Preserve editable ink strokes across XLSX saves #712 still open as editable ink.Commits
One commit per layer, in dependency order: model → PDF → XLSX → engine → UI → app (trackpad backends) → docs. Each commit passes
cargo check --all-targetsfor the crates it touches. The whole branch passescargo xtask ci(fmt, clippy, test, assets, layers, wasm, parity, keytips).Notes for review
nextEventMatchingMaskin the default run-loop mode inside winit's redraw handler re-entered winit's and Core Animation's observers.nextDrawablethen hit its 1 s timeout. Touch events are now drained in a private run-loop mode.NSApp.currentEvent. Seeing every event would needaddLocalMonitorForEventsMatchingMask:handler:, which is anunsafe fnin objc2, and the workspace forbidsunsafe. A new finger identity always starts a new stroke, so a missed lift doesn't join two strokes.Testing