Interaction System
How pointer input becomes selection, drags, connections and undoable actions
Input handling is split between InteractionHelper (the state machine that interprets pointer events) and SelectionManager (the selection model). The manager wires them together and turns their results into undoable DrawingActions.
Table of Contents
Input Flow
- The host receives a native pointer event and converts the coordinates into world space (subtract
PanOffset, divide byZoom). - It forwards the event to the manager:
HandleMouseDown(point, modifiers, button),HandleMouseMove(point, modifiers),HandleMouseUp(...)andHandleMouseWheel(point, delta). InteractionHelperhit-tests ports, resize handles, components and lines in that priority order and enters a drag state.- On completion it either mutates the model directly (with a matching
DrawingActionpushed ontoHistoryManager) or raises an event for the host (for exampleComponentDropped).
// What the host does on a mouse-down (world coordinates)
float zoom = drawingManager.Zoom == 0 ? 1f : drawingManager.Zoom;
var world = new SKPoint(
(screen.X - drawingManager.PanOffset.X) / zoom,
(screen.Y - drawingManager.PanOffset.Y) / zoom);
drawingManager.HandleMouseDown(world, SKKeyModifiers.None, mouseButton: 0);
Drag States
InteractionHelper exposes its live state so hosts can render cursors, ghosts and previews:
| Member | Type | Meaning |
|---|---|---|
IsDragging | bool | A component drag is in progress. |
DraggingComponent | SkiaComponent | The component being dragged. |
IsDraggingLine | bool | A line is being re-routed or re-attached. |
DraggingLine | IConnectionLine | The line being dragged. |
IsDrawingLine | bool | A new connection is being drawn from a port. |
IsSelecting / SelectionRect | bool / SKRect | Marquee selection in progress and its world-space rectangle. |
When a component drag ends, the helper builds a ComponentDropEventArgs (component, canvas and screen position, final bounds) and the manager raises ComponentDropped.
Selection Model
SelectionManager owns three independent selections — components, lines and connection points — and is reachable as drawingManager.SelectionManager:
| Member | Description |
|---|---|
SelectComponent(component, addToSelection) | Replaces or extends the component selection. |
AddToSelection(component) / RemoveFromSelection(...) | Incremental changes for all three selection kinds. |
SelectComponentsInRect(rect, components, addToSelection) | Marquee selection; the helper calls this on mouse-up. |
SelectLine(line, ...) / SelectConnectionPoint(point, ...) | Line and port selection. |
SelectedComponents / SelectedLines / SelectedConnectionPoints | Read-only views of the current selection. |
IsSelected(...), SelectionCount, SelectAll(), SelectNext(forward), ClearSelection() | Query and navigation helpers. |
SelectionChanged | Event raised on any change; the manager re-raises it for hosts. |
Selection is a model concern, not a rendering concern: components expose IsSelected for drawing, but the truth lives in the manager.
Keyboard and Clipboard
The host forwards key events; the manager implements the standard editor set and exposes clipboard operations that use the same DTO serializer as files:
| Action | Manager API |
|---|---|
| Select all | SelectAllComponents() |
| Copy | CopySelectedComponents() (serializes to a clipboard DTO string) |
| Paste | PasteComponents(position) (deserializes, offsets, pushes PasteComponentsAction) |
| Delete | DeleteSelectedComponents() (components plus attached lines as one action) |
| Undo / Redo | Undo() / Redo(), with CanUndo/CanRedo for menu state |
| Move selection | MoveSelectedComponents(offset) |
| Arrange | BringToFront() / SendToBack() and the align helpers |
Escape cancels an in-progress drag or connection draw; the preview line is discarded and no action is recorded.
Undo Integration
Every mutation that goes through the manager pushes a DrawingAction onto HistoryManager. Drags are recorded once, on mouse-up, with the original positions captured at mouse-down — so a 200-pixel drag is a single undo step, not 200.
// The manager records actions itself; hosts only push custom ones
drawingManager.MoveSelectedComponents(new SKPoint(20, 0)); // recorded
// Custom operations use the same history
drawingManager.HistoryManager.ExecuteAction(new AlignComponentsAction(...));
HistoryChanged fires on every push, undo and redo, which is what menu commands bind to. The full action catalog is listed on the Undo/Redo System page; the events raised by input are cataloged on the Event Catalog page.