Let’s explore how custom morphs can react to mouse clicks, mouse hovers, and keystrokes.
Here is a modification of our previous example whose color toggles between red and green each time it is clicked.
As we need a color, we first modify our EllipseDemo to be a subclass of ColoredBoxMorph:
ColoredBoxMorph subclass: #EllipseDemo instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'ArtOfMorph'
Then we initialize it with the red color:
initialize
super initialize. color := Color red
First, we request that we want to handle the mouse click down event:
handlesMouseDown: aMouseEvent
"This enables the morph to handle mouse events such as button presses." ^ true
Then, at each mouse click, we toggle the color attribute between red and green:
mouseButton1Down: aMouseEvent localPosition: aPosition
color := (color = `Color red`) ifTrue: [`Color green`] ifFalse: [`Color red`]. self redrawNeeded
Of course, we adjust the drawing method to use the color attribute:
drawOn: aCanvas
aCanvas fillColor: color do: [ aCanvas ellipseCenter: self center radius: self semiAxes ]
To render this, open a Workspace and evaluate EllipseDemo new openInWorld. Click the circle several times to toggle its color.
Now let’s modify our EllipseDemo to toggle its color based on whether the mouse cursor is hovering over it.
This time, we want to handle events when the mouse pointer is hovering over our EllipseDemo:
handlesMouseOver: aMouseEvent
"This enables the morph to handle mouse enter and leave events." ^ true
Of course, we remove the handlesMouseDown: method, or alternatively, we edit it so it returns false, to let Cuis-Smalltalk handle this event:
handlesMouseDown: aMouseEvent
"This enables the morph to handle mouse events such as button presses." ^ false
There are two event handlers associated with handling mouse over: when entering and when leaving a morph. We edit the methods accordingly to toggle the morph’s color:
mouseEnter: aMouseEvent
color := `Color green`. self redrawNeeded
mouseLeave: aMouseEvent
color := `Color red`. self redrawNeeded
Create an instance as seen previously, then hover onto and off the ellipse to toggle its color.
Observe how the boundary between inside and outside of the ellipse is a rectangle. This is because our EllipseDemo is a kind of BoxMorph optimized for rectangular shapes. To have exact pixel detection, including shapes drawn with holes, our EllipseDemo would require being a direct subclass of PlacedMorph. In the process, we will lose the extent and color attributes and will have to define our own.
Now let’s combine the mouse hover and mouse click events: at a button 1 click, the ellipse shrinks slightly; at a button 2 click, it grows greatly.
To do so, we introduce a shrink attribute initialized to 0:
initialize
super initialize. color := Color red. shrink := 0
Then it changes depending on user actions; its value increases slightly at a button 1 click:
mouseButton1Down: aMouseEvent localPosition: aPosition
shrink := (shrink + 0.5) min: (extent x min: extent y) // 2. self redrawNeeded
and decreases quickly at a button 2 click:
mouseButton2Down: aMouseEvent localPosition: aPosition
shrink := (shrink - 5) max: 0. self redrawNeeded
Of course, we have to bound the shrink attribute between 0 and the smaller extent axis of the whole morph.
Then we adjust our semiAxes method used to draw the ellipse:
semiAxes
^ (extent / 2.0) - shrink