Skip to content
Merged
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
146 changes: 136 additions & 10 deletions Pinta.Gui.Widgets/Widgets/Canvas/CanvasWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,16 @@ namespace Pinta;
public sealed class CanvasWindow : Gtk.Grid
{
private readonly Document document;
private readonly ChromeManager chrome;
private readonly ToolManager tools;

private readonly Ruler horizontal_ruler;
private readonly Ruler vertical_ruler;
private readonly Gtk.ScrolledWindow scrolled_window;
private readonly Gtk.EventControllerMotion motion_controller;
private readonly Gtk.GestureDrag drag_controller;
private readonly Gtk.GestureZoom gesture_zoom;

private PointD current_window_pos = PointD.Zero;
private PointD current_canvas_pos = PointD.Zero;
private double cumulative_zoom_amount;
private double last_scale_delta;
Expand All @@ -67,9 +69,7 @@ public CanvasWindow (
scrollController.OnDecelerate += (_, _) => gestureZoom.IsActive (); // Cancel scroll deceleration when zooming

PintaCanvas canvas = new (
chrome,
tools,
this,
document,
canvasGrid
) {
Expand All @@ -82,6 +82,15 @@ public CanvasWindow (
viewPort.AddController (scrollController);
viewPort.Child = canvas;

// Use the drag gesture to forward a sequence of mouse press -> move -> release events to the current tool.
// This is more reliable than using just a click gesture in combination with the move controller (see bug #1456)
// Note that we attach this to the root canvas widget, not the canvas, so that it can receive drags that start outside the canvas.
Gtk.GestureDrag dragController = Gtk.GestureDrag.New ();
dragController.SetButton (0); // Listen for all mouse buttons.
dragController.OnDragBegin += OnDragBegin;
dragController.OnDragUpdate += OnDragUpdate;
dragController.OnDragEnd += OnDragEnd;

Gtk.ScrolledWindow scrolledWindow = new () {
Hexpand = true,
Vexpand = true,
Expand All @@ -108,6 +117,7 @@ public CanvasWindow (
Focusable = true;

AddController (gestureZoom);
AddController (dragController);
AddController (motionController);

// --- Initialization (Gtk.Grid)
Expand All @@ -123,13 +133,16 @@ public CanvasWindow (

Canvas = canvas;

this.chrome = chrome;
this.tools = tools;
this.document = document;

scrolled_window = scrolledWindow;
gesture_zoom = gestureZoom;
horizontal_ruler = horizontalRuler;
vertical_ruler = verticalRuler;
motion_controller = motionController;
drag_controller = dragController;

// --- Further initialization

Expand Down Expand Up @@ -165,19 +178,35 @@ private void UpdateRulerSelection (object? sender, EventArgs e)
}

private void HandleMotion (
Gtk.EventControllerMotion _,
Gtk.EventControllerMotion controller,
Gtk.EventControllerMotion.MotionSignalArgs args)
{
PointD newPosition = new (args.X, args.Y);
PointD rootPoint = new (args.X, args.Y);

// These coordinates are relative to our grid widget, so transform into the child image
// view's coordinates, and then to the canvas coordinates.
this.TranslateCoordinates (Canvas, newPosition, out PointD viewPos);
this.TranslateCoordinates (Canvas, rootPoint, out PointD viewPos);

current_window_pos = newPosition;
current_canvas_pos = document.Workspace.ViewPointToCanvas (viewPos);
horizontal_ruler.Position = current_canvas_pos.X;
vertical_ruler.Position = current_canvas_pos.Y;

// Forward mouse move events to the current tool when not dragging.
if (drag_controller.GetStartPoint (out _, out _))
return;

if (document.Workspace.PointInCanvas (current_canvas_pos))
chrome.LastCanvasCursorPoint = current_canvas_pos.ToInt ();

ToolMouseEventArgs tool_args = new () {
State = controller.GetCurrentEventState (),
MouseButton = MouseButton.None,
PointDouble = current_canvas_pos,
WindowPoint = viewPos,
RootPoint = rootPoint,
};

tools.DoMouseMove (document, tool_args);
}

private void HandleGestureZoomScaleChanged (object? sender, EventArgs e)
Expand Down Expand Up @@ -206,9 +235,6 @@ private void HandleGestureZoomScaleChanged (object? sender, EventArgs e)
last_scale_delta = gesture_zoom.GetScaleDelta () - 1;
}

public PointD WindowMousePosition
=> current_window_pos;

public bool IsMouseOnCanvas
=> motion_controller.ContainsPointer;

Expand Down Expand Up @@ -314,4 +340,104 @@ private bool HandleScrollEvent (

return true;
}

private void OnDragBegin (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragBeginSignalArgs args)
{
// A mouse click on the canvas should grab focus away from any toolbar widgets, etc
// Using the root canvas widget works best - if the drawing area is given focus, the scroll
// widget jumps back to the origin.
GrabFocus ();

// Note: if we ever regain support for docking multiple canvas
// widgets side by side (like Pinta 1.7 could), a mouse click should switch
// the active document to this document.

// Send the mouse press event to the current tool.
// Translate coordinates to the canvas widget.
PointD rootPoint = new (args.StartX, args.StartY);
this.TranslateCoordinates (Canvas, rootPoint, out PointD viewPoint);
PointD canvasPoint = document.Workspace.ViewPointToCanvas (viewPoint);

ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = canvasPoint,
WindowPoint = viewPoint,
RootPoint = rootPoint,
};

tools.DoMouseDown (document, tool_args);
}

private void OnDragUpdate (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragUpdateSignalArgs args)
{
gesture.GetStartPoint (out double startX, out double startY);
PointD rootPoint = new (startX + args.OffsetX, startY + args.OffsetY);

// Translate coordinates to the canvas widget.
this.TranslateCoordinates (Canvas, rootPoint, out PointD viewPoint);

current_canvas_pos = document.Workspace.ViewPointToCanvas (viewPoint);
if (document.Workspace.PointInCanvas (current_canvas_pos))
chrome.LastCanvasCursorPoint = current_canvas_pos.ToInt ();

// Send the mouse move event to the current tool.
ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = current_canvas_pos,
WindowPoint = viewPoint,
RootPoint = rootPoint,
};

tools.DoMouseMove (document, tool_args);
}

private void OnDragEnd (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragEndSignalArgs args)
{
gesture.GetStartPoint (out double startX, out double startY);
PointD rootPoint = new (startX + args.OffsetX, startY + args.OffsetY);

// Translate coordinates to the canvas widget.
this.TranslateCoordinates (Canvas, rootPoint, out PointD viewPoint);
PointD canvasPoint = document.Workspace.ViewPointToCanvas (viewPoint);

// Send the mouse release event to the current tool.
ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = canvasPoint,
WindowPoint = viewPoint,
RootPoint = rootPoint,
};

tools.DoMouseUp (document, tool_args);
}

public bool DoKeyPressEvent (
Gtk.EventControllerKey controller,
Gtk.EventControllerKey.KeyPressedSignalArgs args)
{
// Give the current tool a chance to handle the key press
ToolKeyEventArgs tool_args = new () {
Event = controller.GetCurrentEvent (),
Key = args.GetKey (),
State = args.State,
};

return tools.DoKeyDown (document, tool_args);
}

public bool DoKeyReleaseEvent (
Gtk.EventControllerKey controller,
Gtk.EventControllerKey.KeyReleasedSignalArgs args)
{
ToolKeyEventArgs tool_args = new () {
Event = controller.GetCurrentEvent (),
Key = args.GetKey (),
State = args.State,
};

return tools.DoKeyUp (document, tool_args);
}
}
137 changes: 0 additions & 137 deletions Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ public sealed class PintaCanvas : Gtk.Picture
{
private readonly CanvasRenderer cr;
private readonly Document document;
private readonly CanvasWindow canvas_window;
private readonly ICanvasGridService canvas_grid;
private readonly Gtk.GestureDrag drag_controller;

private uint queued_update_id = 0;

Expand All @@ -51,19 +49,14 @@ public sealed class PintaCanvas : Gtk.Picture
private readonly uint selection_animation_timer_id;
private float selection_animation_dash_offset;

private readonly ChromeManager chrome;
private readonly ToolManager tools;

public PintaCanvas (
ChromeManager chrome,
ToolManager tools,
CanvasWindow window,
Document document,
ICanvasGridService canvasGrid)
{
this.chrome = chrome;
this.tools = tools;
canvas_window = window;
canvas_grid = canvasGrid;
this.document = document;

Expand All @@ -80,20 +73,6 @@ public PintaCanvas (
// Timer for selection outline animation
selection_animation_timer_id = GLib.Functions.TimeoutAdd (GLib.Constants.PRIORITY_DEFAULT, 80, SelectionAnimationTick);

// Use the drag gesture to forward a sequence of mouse press -> move -> release events to the current tool.
// This is more reliable than using just a click gesture in combination with the move controller (see bug #1456)
drag_controller = Gtk.GestureDrag.New ();
drag_controller.SetButton (0); // Listen for all mouse buttons.
drag_controller.OnDragBegin += OnDragBegin;
drag_controller.OnDragUpdate += OnDragUpdate;
drag_controller.OnDragEnd += OnDragEnd;
AddController (drag_controller);

// Forward mouse move events to the current tool when not dragging.
Gtk.EventControllerMotion motion_controller = Gtk.EventControllerMotion.New ();
motion_controller.OnMotion += OnMouseMove;
AddController (motion_controller);

// If there is additional space available, keep the image centered and prevent stretching.
Hexpand = false;
Halign = Gtk.Align.Center;
Expand Down Expand Up @@ -468,122 +447,6 @@ private void OnViewSizeChanged (object? o, System.EventArgs args)
SetSizeRequest (viewSize.Width, viewSize.Height);
}

private void OnDragBegin (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragBeginSignalArgs args)
{
// Note we don't call gesture.SetState (Gtk.EventSequenceState.Claimed) here, so
// that the CanvasWindow can also receive motion events to update the root window mouse position.

// A mouse click on the canvas should grab focus away from any toolbar widgets, etc
// Using the root canvas widget works best - if the drawing area is given focus, the scroll
// widget jumps back to the origin.
canvas_window.GrabFocus ();

// Note: if we ever regain support for docking multiple canvas
// widgets side by side (like Pinta 1.7 could), a mouse click should switch
// the active document to this document.

// Send the mouse press event to the current tool.
PointD window_point = new (args.StartX, args.StartY);
PointD canvas_point = document.Workspace.ViewPointToCanvas (window_point);

ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = canvas_point,
WindowPoint = window_point,
RootPoint = canvas_window.WindowMousePosition,
};

tools.DoMouseDown (document, tool_args);
}

private void OnDragUpdate (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragUpdateSignalArgs args)
{
// Send the mouse move event to the current tool.
gesture.GetStartPoint (out double startX, out double startY);
PointD window_point = new (startX + args.OffsetX, startY + args.OffsetY);
PointD canvas_point = document.Workspace.ViewPointToCanvas (window_point);

ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = canvas_point,
WindowPoint = window_point,
RootPoint = canvas_window.WindowMousePosition,
};

tools.DoMouseMove (document, tool_args);
}

private void OnDragEnd (Gtk.GestureDrag gesture, Gtk.GestureDrag.DragEndSignalArgs args)
{
// Send the mouse release event to the current tool.
gesture.GetStartPoint (out double startX, out double startY);
PointD window_point = new (startX + args.OffsetX, startY + args.OffsetY);
PointD canvas_point = document.Workspace.ViewPointToCanvas (window_point);

ToolMouseEventArgs tool_args = new () {
State = gesture.GetCurrentEventState (),
MouseButton = gesture.GetCurrentMouseButton (),
PointDouble = canvas_point,
WindowPoint = window_point,
RootPoint = canvas_window.WindowMousePosition,
};

tools.DoMouseUp (document, tool_args);
}

private void OnMouseMove (Gtk.EventControllerMotion controller, Gtk.EventControllerMotion.MotionSignalArgs args)
{
// Don't send duplicate mouse move events while a drag is active.
if (drag_controller.GetStartPoint (out _, out _))
return;

PointD window_point = new (args.X, args.Y);
PointD canvas_point = document.Workspace.ViewPointToCanvas (window_point);

if (document.Workspace.PointInCanvas (canvas_point))
chrome.LastCanvasCursorPoint = canvas_point.ToInt ();

ToolMouseEventArgs tool_args = new () {
State = controller.GetCurrentEventState (),
MouseButton = MouseButton.None,
PointDouble = canvas_point,
WindowPoint = window_point,
RootPoint = canvas_window.WindowMousePosition,
};

tools.DoMouseMove (document, tool_args);
}

public bool DoKeyPressEvent (
Gtk.EventControllerKey controller,
Gtk.EventControllerKey.KeyPressedSignalArgs args)
{
// Give the current tool a chance to handle the key press
ToolKeyEventArgs tool_args = new () {
Event = controller.GetCurrentEvent (),
Key = args.GetKey (),
State = args.State,
};

return tools.DoKeyDown (document, tool_args);
}

public bool DoKeyReleaseEvent (
Gtk.EventControllerKey controller,
Gtk.EventControllerKey.KeyReleasedSignalArgs args)
{
ToolKeyEventArgs tool_args = new () {
Event = controller.GetCurrentEvent (),
Key = args.GetKey (),
State = args.State,
};

return tools.DoKeyUp (document, tool_args);
}


#region Selection outline animation
private bool SelectionAnimationTick ()
{
Expand Down
Loading
Loading