Skip to main content
Engineering LibreTexts

11.5: Interaction and Animation

  • Page ID
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

    To build live user-interfaces using morphs, we need to be able to interact with them using the mouse and the keyboard. Moreover, the morphs need to be able respond to user input by changing their appearance and position — that is, by animating themselves.

    Mouse events

    When a mouse button is pressed, Morphic sends each morph under the mouse pointer the message handlesMouseDown:. If a morph answers true, then Morphic immediately sends it the mouseDown: message; it also sends the mouseUp: message when the user releases the mouse button. If all morphs answer false, then Morphic initiates a drag-and-drop operation. As we will discuss below, the mouseDown: and mouseUp: messages are sent with an argument — a MouseEvent object — that encodes the details of the mouse action.

    Let’s extend CrossMorph to handle mouse events. We start by ensuring that all crossMorphs answer true to the handlesMouseDown: message.

    \(\bigstar\) Add this method to CrossMorph:

    Code \(\PageIndex{1}\) (Squeak): Declaring That CrossMorph Will React to Mouse Clicks

    CrossMorph»handlesMouseDown: anEvent

    Suppose that when the red mouse button is clicked, we want to change the color of the cross to red, and when the yellow button is clicked we want to change the color to yellow. This can be accomplished by Code \(\PageIndex{2}\).

    Code \(\PageIndex{2}\) (Squeak): Reacting to Mouse Clicks by Changing the Morph’s Color

    CrossMorph»mouseDown: anEvent
        anEvent redButtonPressed
            ifTrue: [self color: Color red].
        anEvent yellowButtonPressed
            ifTrue: [self color: Color yellow].
        self changed

    Notice that in addition to changing the color of the morph, this method also sends self changed. This makes sure that morphic sends drawOn: in a timely fashion. Note also that once the morph handles mouse events, you can no longer grab it with the mouse and move it. Instead you have to use the halo: blue-click on the morph to make the halo appear and grab either the brown move handle or the black pickup handle at the top of the morph.

    The anEvent argument of mouseDown: is an instance of MouseEvent, which is a subclass of MorphicEvent. MouseEvent defines the redButtonPressed and yellowButtonPressed methods. Browse this class to see what other methods it provides to interrogate the mouse event.

    Keyboard events

    To catch keyboard events, we need to take three steps.

    1. Give the “keyboard focus” to a specific morph: for instance we can give focus to our morph when the mouse is over it.
    2. Handle the keyboard event itself with the handleKeystroke: method: this message is sent to the morph that has keyboard focus when the user presses a key.
    3. Release the keyboard focus when the mouse is no longer over our morph.

    Let’s extend CrossMorph so that it reacts to keystrokes. First, we need to arrange to be notified when the mouse is over the morph. This will happen if our morph answers true to the handlesMouseOver: message.

    \(\bigstar\) Declare that CrossMorph will react when it is under the mouse pointer.

    Code \(\PageIndex{3}\) (Squeak): We Want to Handle “Mouse Over” Events

    CrossMorph»handlesMouseOver: anEvent

    This message is the equivalent of handlesMouseDown: for the mouse position. When the mouse pointer enters or leaves the morph, the mouseEnter: and mouseLeave: messages are sent to it.

    \(\bigstar\) Define two methods so that CrossMorph catches and releases the keyboard focus, and a third method to actually handle the keystrokes.

    Code \(\PageIndex{4}\) (Squeak): Getting the Keyboard Focus When the Mouse Enters the Morph

    CrossMorph»mouseEnter: anEvent
        anEvent hand newKeyboardFocus: self

    Code \(\PageIndex{5}\) (Squeak): Handing Back the Focus When the Pointer Goes Away

    CrossMorph»mouseLeave: anEvent
        anEvent hand newKeyboardFocus: nil

    Code \(\PageIndex{6}\) (Squeak): Receiving and Handling Keyboard Events

    CrossMorph»handleKeystroke: anEvent
    | keyValue |
    keyValue := anEvent keyValue.
    keyValue = 30 "up arrow"
        ifTrue: [self position: self position - (0 @ 1)].
    keyValue = 31 "down arrow"
        ifTrue: [self position: self position + (0 @ 1)].
    keyValue = 29 "right arrow"
        ifTrue: [self position: self position + (1 @ 0)].
    keyValue = 28 "left arrow"
        ifTrue: [self position: self position - (1 @ 0)]

    We have written this method so that you can move the morph using the arrow keys. Note that when the mouse is no longer over the morph, the handleKeystroke: message is not sent, so the morph stops responding to keyboard commands. To discover the key values, you can open a Transcript window and add Transcript show: anEvent keyValue to Code \(\PageIndex{6}\). The anEvent argument of handleKeystroke: is an instance of KeyboardEvent, another subclass of MorphicEvent. Browse this class to learn more about keyboard events.

    Morphic animations

    Morphic provides a simple animation system with two main methods: step is sent to a morph at regular intervals of time, while stepTime specifies the time in milliseconds between steps.1 In addition, startStepping turns on the stepping mechanism, while stopStepping turns it off again; isStepping can be used to find out whether a morph is currently being stepped.

    \(\bigstar\) Make CrossMorph blink by defining these methods as follows:

    Code \(\PageIndex{7}\) (Squeak): Defining the Animation Time Interval

        ↑ 100

    Code \(\PageIndex{8}\) (Squeak): Making a Step in the Animation

    (self color diff: Color black) < 0.1
        ifTrue: [self color: Color red]
        ifFalse: [self color: self color darker]

    To start things off, you can open an inspector on a CrossMorph (using the debug handle in the morphic halo), type self startStepping in the small workspace pane at the bottom, and do it. Alternatively, you can modify the handleKeystroke: method so that you can use the + and keys to start and stop stepping.

    \(\bigstar\) Add the following code to Code \(\PageIndex{6}\).

    keyValue = $+ asciiValue
        ifTrue: [self startStepping].
    keyValue = $- asciiValue
        ifTrue: [self stopStepping].

    1. stepTime is actually the minimum time between steps. If you ask for a stepTime of 1 ms, don’t be surprised if Squeak is too busy to step your morph that often.

    This page titled 11.5: Interaction and Animation is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Andrew P. Black, Stéphane Ducasse, Oscar Nierstrasz, Damien Pollet via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.