It’s easy to create custom morphs. Just create a subclass of an existing morph class. Then implement the drawOn: method or add and lay out sub-morphs.
Let’s make an example that draws an ellipse. Making it a subclass of BoxMorph gives it an extent instance variable which specifies its width and height.
BoxMorph subclass: #EllipseDemo instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'ArtOfMorph'
We adjust its default extent1:
defaultExtent
^ `200@200`
In our EllipseDemo, the extent represents the lengths of the x and y axes of the ellipse. We use it to draw it accordingly:
drawOn: aCanvas
| radius | radius := extent / 2.0. aCanvas fillColor: Color purple do: [ aCanvas ellipseCenter: radius radius: radius]
Finally, we instruct Cuis-Smalltalk that we want to use the Vector Graphics engine:
requiresVectorCanvas
^ true
To display an instance of EllipseDemo, open a Workspace and execute EllipseDemo new openInWorld.
Figure 1.1: Ellipse with axes, resized to an extent approximately equal to 300@100
The drawing always operates in the morph’s own coordinate system, and we have to ensure our drawing operations remain within the bounds defined by the morph origin, in the top-left corner, and its bottom-right corner delimited by its extent attribute, a point.
Before proceeding with events, we may want to add semantics to our protocol with #center and #semiAxes messages to use within the drawOn: method:
center
^ extent / 2.0
semiAxes
" the semi minor and major axes of the ellipse" ^ extent / 2.0
drawOn: aCanvas
aCanvas fillColor: Color purple do: [ aCanvas ellipseCenter: self center radius: self semiAxes ]