feat(pen): persist brush kind so it survives reload
Some checks failed
CI / Windows build (push) Has been cancelled

Closes TODO(brush-persist). EditorStroke now serializes its brush as
the stable BrushKind name; sidecars written before this field, and any
unknown name, load as fountainPen (back-compat). PenStroke<->EditorStroke
carry brush both ways, so a ballpoint/highlighter/pencil stroke keeps
its opacity/blend after a document is closed and reopened.

Note: InkStroke (the note/scratchpad world-coord format) has no brush
field, so notes derive brush from the tool — highlighter is preserved,
ballpoint/pencil collapse to fountainPen on reload (TODO: extend
InkStroke). PDF documents persist brush fully. analyze clean,
379 tests green.
This commit is contained in:
2026-06-24 23:40:40 +08:00
parent 6c2dd71b82
commit 46589a4c87
8 changed files with 206 additions and 40 deletions

View File

@@ -20,19 +20,31 @@
import 'dart:ui' show Color, BlendMode;
import 'package:freezed_annotation/freezed_annotation.dart';
/// The four selectable brushes. Extensible: add a kind here + a preset in
/// [kBrushPresets]. The eraser is NOT a brush — it stays a separate tool.
///
/// The `@JsonValue` names are the STABLE on-disk identifiers persisted in the
/// sidecar (`EditorStroke.brush`); they are decoupled from the Dart enum
/// identifiers so renaming a constant here never breaks existing sidecars. A
/// brush whose stored name is unknown (e.g. a future brush opened by an older
/// build) is read back as [fountainPen] (see `EditorStroke.brush`'s JsonKey).
enum BrushKind {
/// Strong pressure→width (rnote Pow2 / quadratic), soft taper, solid ink.
@JsonValue('fountainPen')
fountainPen,
/// Near-constant thin width; pressure carries OPACITY (the ballpoint "tell").
@JsonValue('ballpoint')
ballpoint,
/// Broad, flat width, translucent, square (uncapped) ends.
@JsonValue('highlighter')
highlighter,
/// Moderate width + opacity from pressure (rnote Sqrt / √p), scratchy.
@JsonValue('pencil')
pencil,
}