Handles mouse and keyboard events occurring in a LabCanvas; either forwards events to an EventHandler, or does SimView panning (moving the content of the SimView with the mouse).

Key Events

Key events are forwarded to the EventHandler, but only when the event target is the LabCanvas, or when there is no specific target (document.body is the event target in that case). This avoids forwarding key events intended for some other target, for example a text edit area.

Mouse Events

If SimView panning is in effect, then mouse events are sent to the ViewPanner that was created. Otherwise, SimController calls MouseTracker.findNearestDragable which returns a MouseTracker instance that processes the mouse events before (possibly) sending them to the EventHandler.

The MouseTracker forwards the mouse events to the EventHandler along with information such as: the mouse position in simulation coordinates of the SimView; the nearest dragable DisplayObject; the initial offset between the mouse and the DisplayObject.

Even if no dragable DisplayObject is found (and SimView panning is not occurring) the MouseTracker still forwards the event to the EventHandler. The mouse position is given in simulation coordinates of the focus SimView of the LabCanvas.

Touch Events

Single touch events are handled like mouse events. Multiple touch events like pinch to zoom or two finger pan are ignored and left for the operating system to respond to.

Because people are inexact about putting all their fingers on the screen at the same exact moment, most multiple touch events start as a single touch, followed quickly by a multiple touch event. The typical sequence is:

  1. A single-touch touchStart comes thru, and we start dragging an object. We also allow the event to also be processed by the operating system (otherwise many multi-touch events are not processed by the system).

  2. A single-touch touchMove event or two occurs, which causes some mouse dragging to happen.

  3. A multi-touch touchStart event occurs, we stop mouse dragging. The multi-touch touchStart is then handled by the operating system.

Note that allowing the first touchStart to be processed by the system results in the canvas being highlighted (on iOS, probably others). To prevent that highlighting you can add this bit of CSS code:

canvas {
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
}

There are other such CSS options for other browsers.

SimView Panning

When specified modifier keys are pressed (such as option key) then mouse drag events will directly pan the focus SimView instead of sending events to the EventHandler. An instance of ViewPanner is created to handle the SimView panning. Panning is accomplished by modifying the simulation rectangle of the SimView with SimView.setSimRect.

If SimView panning is enabled, it only occurs when the specified combination of modifier keys are down during the mouse event, given in the panModifier parameter. Here is an example where SimView panning happens when no modifier keys are pressed:

new SimController(graphCanvas, null, {alt:false, control:false, meta:false, shift:false})

Here is an example where SimView panning happens when both the meta and alt keys are down:

new SimController(labCanvas, eventHandler, {alt:true, control:false, meta:true, shift:false})

Note that the exact combination of modifier keys must be pressed to enable SimView panning. For example, if only the alt key is specified, then SimView panning will not occur if any other modifier is also pressed.

The table below shows how modifier keys are named on different operating systems.

|          | Mac OS    |  Windows    |
| :----    | :-------- | :---------- |
| alt      | option    | alt         |
| meta     | command   | windows     |

TO DO Should this class be designed for inheritance? Seems like there could be a need for a class that doesn't look for dragable objects at all, but wants to get mouse drag info anyway.

TO DO See DoublePendulumCompareBuilder: It might be better if the simcontroller would ignore objects it finds that don't belong to the sim it is controlling? Instead, we have to set those objects to be non-dragable or wind up getting an exception in startDrag. Or if we don't throw an exception in startDrag, there is no way for the EventHandler to say "I didn't like that object, find me the next nearest one".

TO DO Write a EventHandler that overrides what a sim is doing, to ensure that it is possible.

TO DO Provide a way to specify what JComponent should have focus on startup. Currently we are giving the SimCanvas the focus.

TO DO provide a way to say 'pan all views' instead of just the focus view?

TO DO add to SimView interface a way for the SimView to say whether or not it is dragable/pannable. Then you could interrogate all the Views of the LabCanvas from front to back, and drag the first one that is dragable, instead of only dragging the focus view.

TO DO Make a unit test; especially for findNearestDragable. Note that it is possible to make synthetic events for testing in Javascript.

Implements

Constructors

  • Parameters

    • labCanvas: LabCanvas

      the LabCanvas to process events for.

    • Optional eventHandler: null | EventHandler

      the EventHandler to forward events to; or null or undefined to just do SimView panning.

    • Optional panModifiers: null | ModifierKeys

      which modifier keys are needed for SimView panning; if null, then SimView panning will not be done; if undefined then default is to do SimView panning when shift key is pressed.

    Returns SimController

Properties

enablePanning_: boolean = false

Whether to pan the view by mouse dragging. SimView panning is enabled by default (when panModifer is undefined).

labCanvas_: LabCanvas

the LabCanvas to gather events from

mouseDrag_: boolean = false

true when a mouse drag operation is in progress

mouseTracker_: null | MouseTracker = null

the object that handles dragging a DisplayObject.

myViewPanner_: null | ViewPanner = null

the object that handles panning a SimView

Methods

  • Process a mouseDown in the LabCanvas; decides whether to start panning the SimView, start dragging a DisplayObject, or simply send events to the EventHandler.

    The input coordinates are from MouseEvent.clientX and clientY which gives a value of (0,0) for the top left corner of the client area (the canvas). See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX

    Parameters

    • modifiers: ModifierKeys

      the modifier keys down during event

    • mouseX: number

      X-coordinate relative to the client area (canvas)

    • mouseY: number

      Y-coordinate relative to the client area (canvas)

    Returns void

  • Process mouseMove event, this translates to simulation coordinates and calls EventHandler.mouseDrag. Also does panning of the view, and will move the drag object when the EventHandler doesn't want to.

    The input coordinates are from MouseEvent.clientX and clientY which gives a value of (0,0) for the top left corner of the client area (the canvas). See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX

    Parameters

    • mouseX: number

      X-coordinate relative to the client area (canvas)

    • mouseY: number

      Y-coordinate relative to the client area (canvas)

    Returns void

  • Returns the event location in the LabCanvas's screen coordinates. This works even when dragging outside of the LabCanvas, and when the canvas is stretched by CSS styling.

    From https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.offsetWidth

    The HTMLElement.offsetWidth read-only property returns the layout width of an element. Typically, an element's offsetWidth is a measurement which includes the element borders, the element horizontal padding, the element vertical scrollbar (if present, if rendered) and the element CSS width.

    The event.offsetX and event.offsetY properties only relate to the target of the event, which is the element the mouse is over; when dragging outside of the canvas the target is no longer the canvas so we cannot use offsetX, offsetY.

    The input coordinates are from MouseEvent.clientX and clientY which gives a value of (0,0) for the top left corner of the client area (the canvas). See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX

    Parameters

    • mouseX: number

      X-coordinate relative to the client area (canvas)

    • mouseY: number

      Y-coordinate relative to the client area (canvas)

    Returns Vector

    the event location in the LabCanvas's screen coordinates

  • Finish mouse drag operation, if any, and reset state to 'not dragging'.

    Returns void

  • Called when a key has been pressed, forwards the event by calling EventHandler.handleKeyEvent. Only forwards when the event target is the LabCanvas, or when there is no specific target (document.body is the event target in that case).

    Parameters

    • evt: KeyboardEvent

      the key down event that occurred

    Returns void

  • Called when a key has been released, forwards the event by calling EventHandler.handleKeyEvent. Only forwards when the event target is the LabCanvas, or when there is no specific target (document.body is the event target in that case).

    Parameters

    • evt: KeyboardEvent

      the key up event that occurred

    Returns void

  • Callback for mouseDown event.

    Parameters

    • evt: MouseEvent

      the mouse down event that occurred

    Returns void

  • Callback for mouseMove event.

    Parameters

    • evt: MouseEvent

      the mouse move event that occurred

    Returns void

  • Callback for mouseUp event.

    Parameters

    • _evt: MouseEvent

      the mouse up event that occurred

    Returns void

  • Parameters

    • eventHandler: null | EventHandler

      the EventHandler to forward events to; or null to just do SimView panning.

    Returns void

  • Returns a minimal string representation of this object, usually giving just identity information like the class name and name of the object.

    For an object whose main purpose is to represent another Printable object, it is recommended to include the result of calling toStringShort on that other object. For example, calling toStringShort() on a DisplayShape might return something like this:

    DisplayShape{polygon:Polygon{'chain3'}}
    

    Returns string

    a minimal string representation of this object.

  • Callback for touchEnd event. If a mouseDrag is happening, then process as a mouse-up event.

    Parameters

    • _evt: TouchEvent

      the touch end event that occurred

    Returns void

  • Callback for touchMove event. If single touch in canvas, then process as a mouse-move event. Multiple touch cancels an ongoing mouse drag by calling finishDrag.

    Parameters

    • evt: TouchEvent

      the touch move event that occurred

    Returns void

  • Callback for touchStart event. If single touch in canvas, then process as a mouse-down event. Multiple touch cancels an ongoing mouse drag by calling finishDrag.

    Parameters

    • evt: TouchEvent

      the touch start event that occurred

    Returns void

  • Returns true if the two sets of modifier keys match.

    Parameters

    Returns boolean

    true if the two sets of modifier keys match.

  • Returns string representation of the set of modifier keys, for debugging.

    Parameters

    • modifiers: ModifierKeys

      the set of modifier keys of interest

    Returns string

    string representation of the modifierKeys

Generated using TypeDoc