Skip to content

Commit

Permalink
feat: Add better support for stylus hovering (#1355)
Browse files Browse the repository at this point in the history
* feat: add better support for stylus hovering

* feat: consistent tool switching
  • Loading branch information
3akev authored Dec 14, 2024
1 parent 5ca2ac3 commit 3037472
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 14 deletions.
28 changes: 24 additions & 4 deletions lib/components/canvas/canvas_gesture_detector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class CanvasGestureDetector extends StatefulWidget {
required this.onDrawUpdate,
required this.onDrawEnd,
required this.onPressureChanged,
required this.onHovering,
required this.onHoveringEnd,
required this.onStylusButtonChanged,
required this.undo,
required this.redo,
Expand All @@ -50,6 +52,8 @@ class CanvasGestureDetector extends StatefulWidget {
/// Called when the pressure of the stylus changes,
/// pressure is negative if stylus button is pressed
final ValueChanged<double?> onPressureChanged;
final VoidCallback onHovering;
final VoidCallback onHoveringEnd;
final ValueChanged<bool> onStylusButtonChanged;

final VoidCallback undo;
Expand Down Expand Up @@ -392,25 +396,40 @@ class CanvasGestureDetectorState extends State<CanvasGestureDetector> {

void _listenerPointerEvent(PointerEvent event) {
double? pressure;
bool stylusButtonPressed = false;

if (event.kind == PointerDeviceKind.stylus) {
pressure = event.pressure;
stylusButtonPressed = event.buttons == kPrimaryStylusButton;
} else if (event.kind == PointerDeviceKind.invertedStylus) {
pressure = event.pressure;
stylusButtonPressed = true; // treat eraser as stylus button
} else if (Platform.isLinux && event.pressureMin != event.pressureMax) {
// if min == max, then the device isn't pressure sensitive
pressure = event.pressure;
}

widget.onPressureChanged(pressure);
widget.onStylusButtonChanged(stylusButtonPressed);
}

bool stylusButtonWasPressed = false;

void _listenerPointerHoverEvent(PointerEvent event) {
if (event.kind != PointerDeviceKind.stylus) return;

// Apparently flutter synthesizes a hover event on pointer down,
// so these are used to detect when hovering ends
if (event.synthesized) {
widget.onHoveringEnd();
} else {
widget.onHovering();
if (stylusButtonWasPressed != (event.buttons == kPrimaryStylusButton)) {
stylusButtonWasPressed = event.buttons == kPrimaryStylusButton;
widget.onStylusButtonChanged(stylusButtonWasPressed);
}
}
}

void _listenerPointerUpEvent(PointerEvent event) {
widget.onPressureChanged(null);
stylusButtonWasPressed = false;
widget.onStylusButtonChanged(false);
}

Expand All @@ -424,6 +443,7 @@ class CanvasGestureDetectorState extends State<CanvasGestureDetector> {
onPointerDown: _listenerPointerEvent,
onPointerMove: _listenerPointerEvent,
onPointerUp: _listenerPointerUpEvent,
onPointerHover: _listenerPointerHoverEvent,
child: GestureDetector(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints containerBounds) {
Expand Down
36 changes: 26 additions & 10 deletions lib/pages/editor/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ class EditorState extends State<Editor> {
/// Used to record a move in the history.
Offset moveOffset = Offset.zero;

bool isHovering = true;
int? dragPageIndex;
double? currentPressure;
bool isDrawGesture(ScaleStartDetails details) {
Expand Down Expand Up @@ -653,7 +654,8 @@ class EditorState extends State<Editor> {
));
} else if (currentTool is Eraser) {
final erased = (currentTool as Eraser).onDragEnd();
if (stylusButtonPressed || Prefs.disableEraserAfterUse.value) {
if (tmpTool != null &&
(stylusButtonPressed || Prefs.disableEraserAfterUse.value)) {
// restore previous tool
stylusButtonPressed = false;
currentTool = tmpTool!;
Expand Down Expand Up @@ -717,20 +719,32 @@ class EditorState extends State<Editor> {
currentPressure = pressure == 0 ? null : pressure;
}

void onHovering() {
isHovering = true;
}

void onHoveringEnd() {
isHovering = false;
}

void onStylusButtonChanged(bool buttonPressed) {
// whether the stylus button is or was pressed
stylusButtonPressed = stylusButtonPressed || buttonPressed;

// if needed, switch to eraser
if (!stylusButtonPressed) return;
if (currentTool is Eraser) return;
if (currentTool is Pen && dragPageIndex != null) {
// if the pen is currently drawing, end the stroke
(currentTool as Pen).onDragEnd();
if (isHovering) {
if (buttonPressed) {
if (currentTool is Eraser) return;
tmpTool = currentTool;
currentTool = Eraser();
setState(() {});
} else {
if (tmpTool != null) {
currentTool = tmpTool!;
tmpTool = null;
setState(() {});
}
}
}
tmpTool = currentTool;
currentTool = Eraser();
setState(() {});
}

void onMoveImage(EditorImage image, Rect offset) {
Expand Down Expand Up @@ -1357,6 +1371,8 @@ class EditorState extends State<Editor> {
onDrawStart: onDrawStart,
onDrawUpdate: onDrawUpdate,
onDrawEnd: onDrawEnd,
onHovering: onHovering,
onHoveringEnd: onHoveringEnd,
onStylusButtonChanged: onStylusButtonChanged,
onPressureChanged: onPressureChanged,
undo: undo,
Expand Down

0 comments on commit 3037472

Please sign in to comment.