Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add better support for stylus hovering #1355

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -26,6 +26,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 @@ -49,6 +51,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 @@ -391,25 +395,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 @@ -423,6 +442,7 @@ class CanvasGestureDetectorState extends State<CanvasGestureDetector> {
onPointerDown: _listenerPointerEvent,
onPointerMove: _listenerPointerEvent,
onPointerUp: _listenerPointerUpEvent,
onPointerHover: _listenerPointerHoverEvent,
child: GestureDetector(
onSecondaryTapUp: (TapUpDetails details) => widget.undo(),
onTertiaryTapUp: (TapUpDetails details) => widget.redo(),
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 @@ -508,6 +508,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 @@ -663,7 +664,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 @@ -727,20 +729,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 @@ -1364,6 +1378,8 @@ class EditorState extends State<Editor> {
onDrawStart: onDrawStart,
onDrawUpdate: onDrawUpdate,
onDrawEnd: onDrawEnd,
onHovering: onHovering,
onHoveringEnd: onHoveringEnd,
onStylusButtonChanged: onStylusButtonChanged,
onPressureChanged: onPressureChanged,
undo: undo,
Expand Down