feat(pen): partial/segment erase + fix side-button while drawing
All checks were successful
CI / Windows build (push) Successful in 16m23s

W4/P0 engine: add engine/stroke_eraser.dart (pure, aspect-corrected) with
whole-stroke `strokeHit` + partial `splitStrokeByCircle`. Grazing a long
stroke now CUTS it into surviving pieces instead of deleting it whole.
Wired through PenCanvas.onEraseStroke (now (index, replacements)) →
pen_editor_screen._eraseStroke (replaceRange); undo/persistence unchanged
(whole-page snapshot). 8 new unit tests; 66/66 pass.

Fix side-button (侧键): _isEraserSignal used `buttons == kSecondaryButton`,
but tip-down + barrel = kStylusContact|kPrimaryStylusButton = 0x03, so the
side button only registered on hover, never while drawing. Now a bitmask
test. (Eraser-end/tilt remain blocked on the silent native badnote/pen
channel — needs on-device native logging.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 12:42:26 +08:00
parent 3295018ee3
commit c045fdd3ca
4 changed files with 259 additions and 15 deletions

View File

@@ -247,7 +247,9 @@ class _PenEditorScreenState extends State<PenEditorScreen> {
_schedulePageSave(_pageIndex, snapshot);
}
void _eraseStroke(int index) {
/// Replace committed stroke [index] with its surviving pieces after a partial
/// (segment) erase. An empty [replacements] list removes the stroke entirely.
void _eraseStroke(int index, List<PenStroke> replacements) {
final list = _strokesByPage[_pageIndex];
final willMutate = list != null && index >= 0 && index < list.length;
if (willMutate) {
@@ -256,7 +258,8 @@ class _PenEditorScreenState extends State<PenEditorScreen> {
}
setState(() {
if (list != null && index >= 0 && index < list.length) {
final next = List<PenStroke>.of(list)..removeAt(index);
final next = List<PenStroke>.of(list)
..replaceRange(index, index + 1, replacements);
_strokesByPage[_pageIndex] = next;
}
});