diff --git a/lib/editor/canvas/pen_canvas.dart b/lib/editor/canvas/pen_canvas.dart index 3e81849..ce0d36b 100644 --- a/lib/editor/canvas/pen_canvas.dart +++ b/lib/editor/canvas/pen_canvas.dart @@ -42,8 +42,14 @@ class PenCanvas extends StatefulWidget { this.allowFingerDrawing = false, this.minScale = 0.5, this.maxScale = 8.0, + this.onPenDebug, }); + /// Debug hook: called with a readout of the latest pen event + /// (kind / pressure / pressureMin / pressureMax) so we can see what Windows + /// actually delivers. Null in release UI. + final void Function(String readout)? onPenDebug; + /// The rendered PDF page bitmap, already sized to [pageSize]. final Widget pageWidget; @@ -249,8 +255,18 @@ class _PenCanvasState extends State { // --- Listener callbacks --------------------------------------------------- + void _emitPenDebug(PointerEvent event) { + final cb = widget.onPenDebug; + if (cb == null) return; + cb('${event.kind.name} p=${event.pressure.toStringAsFixed(3)} ' + 'min=${event.pressureMin.toStringAsFixed(2)} ' + 'max=${event.pressureMax.toStringAsFixed(2)} ' + 'tilt=${event.tilt.toStringAsFixed(2)}'); + } + void _onPointerHover(PointerHoverEvent event) { if (_isStylus(event.kind)) { + _emitPenDebug(event); // Detect eraser (barrel button / inverted) while hovering. _eraserActive = _isEraserSignal(event); } @@ -258,6 +274,7 @@ class _PenCanvasState extends State { void _onPointerDown(PointerDownEvent event) { if (event.kind == PointerDeviceKind.trackpad) return; + if (_isStylus(event.kind)) _emitPenDebug(event); _activePointers[event.pointer] = event.kind; @@ -282,6 +299,7 @@ class _PenCanvasState extends State { } void _onPointerMove(PointerMoveEvent event) { + if (_isStylus(event.kind)) _emitPenDebug(event); if (event.pointer != _drawPointer) return; if (_activePointers.length >= 2) return; // pinch owns it _extendStroke(event); diff --git a/lib/editor/canvas/pen_editor_screen.dart b/lib/editor/canvas/pen_editor_screen.dart index ef300d6..e8bf9be 100644 --- a/lib/editor/canvas/pen_editor_screen.dart +++ b/lib/editor/canvas/pen_editor_screen.dart @@ -40,6 +40,15 @@ class _PenEditorScreenState extends State { /// Live page value while dragging the page slider (null when not dragging). double? _scrub; + /// Whether the page-jump slider is expanded (NOT persistent — toggled by + /// tapping the page label; collapses after a jump). + bool _showSlider = false; + + /// Latest pen-event debug readout (kind/pressure/min/max) — shown only when + /// the diagnostic toggle is on, to inspect what Windows delivers. + String _penDebug = ''; + bool _showPenDebug = false; + // Tool state. CanvasTool _tool = CanvasTool.pen; Color _color = Colors.black; @@ -164,6 +173,34 @@ class _PenEditorScreenState extends State { ), ), ), + // Pen diagnostic readout (top-right) — shows what Windows delivers. + if (_showPenDebug) + SafeArea( + child: Align( + alignment: Alignment.topRight, + child: Padding( + padding: const EdgeInsets.all(8), + child: Material( + color: Theme.of(context).colorScheme.inverseSurface, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 6), + child: Text( + _penDebug.isEmpty + ? 'hover / draw with the pen…' + : _penDebug, + style: TextStyle( + fontFamily: 'monospace', + fontSize: 12, + color: Theme.of(context).colorScheme.onInverseSurface, + ), + ), + ), + ), + ), + ), + ), ], ), ); @@ -213,6 +250,9 @@ class _PenEditorScreenState extends State { ? _highlighterWidthFraction : _penWidthFraction, allowFingerDrawing: _allowFingerDrawing, + onPenDebug: _showPenDebug + ? (s) => setState(() => _penDebug = s) + : null, onStrokeComplete: _commitStroke, onEraseStroke: _eraseStroke, pageWidget: PdfPageView( @@ -272,6 +312,12 @@ class _PenEditorScreenState extends State { onPressed: () => setState(() => _allowFingerDrawing = !_allowFingerDrawing), ), + _ToolButton( + icon: Icons.bug_report_outlined, + selected: _showPenDebug, + tooltip: 'Pen pressure diagnostic', + onPressed: () => setState(() => _showPenDebug = !_showPenDebug), + ), ], ), ), @@ -299,67 +345,85 @@ class _PenEditorScreenState extends State { ); } - /// Floating page-control pill: prev / drag-slider / next. No keyboard input - /// (Windows on-screen keyboard is unreliable) — the slider scrubs pages. + /// Floating page control: a COMPACT pill (prev / "n / total" / next). Tapping + /// the label reveals a drag-slider — which is NOT persistent (collapses again + /// on tap) so it doesn't block the page. No keyboard input (Windows IME is + /// unreliable). Widget _buildPagePill() { final doc = _document!; final cs = Theme.of(context).colorScheme; final total = doc.pages.length; final shown = (_scrub ?? (_pageIndex + 1).toDouble()).round(); - return Material( - color: cs.surfaceContainerHigh, - elevation: 3, - borderRadius: BorderRadius.circular(28), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 560), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - tooltip: 'Previous page', - icon: const Icon(Icons.chevron_left), - onPressed: - _pageIndex > 0 ? () => _goToPage(_pageIndex - 1) : null, - ), - if (total > 1) - Flexible( - child: Slider( - min: 1, - max: total.toDouble(), - value: (_scrub ?? (_pageIndex + 1).toDouble()) - .clamp(1, total.toDouble()), - label: '$shown', - divisions: total - 1, - onChanged: (v) => setState(() => _scrub = v), - onChangeEnd: (v) { - setState(() => _scrub = null); - _goToPage(v.round() - 1); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Text( - '$shown / $total', - style: TextStyle( - color: cs.onSurface, - fontWeight: FontWeight.w600, - ), + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Slider — shown only when expanded (not persistent). + if (_showSlider && total > 1) + Container( + margin: const EdgeInsets.only(bottom: 8), + constraints: const BoxConstraints(maxWidth: 420), + child: Material( + color: cs.surfaceContainerHigh, + elevation: 3, + borderRadius: BorderRadius.circular(28), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Slider( + min: 1, + max: total.toDouble(), + value: (_scrub ?? (_pageIndex + 1).toDouble()) + .clamp(1, total.toDouble()), + label: '$shown', + divisions: total - 1, + onChanged: (v) => setState(() => _scrub = v), + onChangeEnd: (v) { + setState(() => _scrub = null); + _goToPage(v.round() - 1); + }, ), ), - IconButton( - tooltip: 'Next page', - icon: const Icon(Icons.chevron_right), - onPressed: _pageIndex < total - 1 - ? () => _goToPage(_pageIndex + 1) - : null, - ), - ], + ), + ), + // Compact pill — always; fits content (no big frame). + Material( + color: cs.surfaceContainerHigh, + elevation: 3, + borderRadius: BorderRadius.circular(28), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + tooltip: 'Previous page', + icon: const Icon(Icons.chevron_left), + onPressed: + _pageIndex > 0 ? () => _goToPage(_pageIndex - 1) : null, + ), + TextButton( + onPressed: total > 1 + ? () => setState(() => _showSlider = !_showSlider) + : null, + child: Text( + '$shown / $total', + style: TextStyle( + color: cs.onSurface, + fontWeight: FontWeight.w600, + ), + ), + ), + IconButton( + tooltip: 'Next page', + icon: const Icon(Icons.chevron_right), + onPressed: _pageIndex < total - 1 + ? () => _goToPage(_pageIndex + 1) + : null, + ), + ], + ), ), ), - ), + ], ); } }