MVC, Morphic & Spec2
Smalltalk-80 introduced Model–View–Controller: the *model* holds domain state and notifies dependents (changed:), the *view* displays it (update:), and the *controller* handles input.
35.1 MVC: where it began
Smalltalk-80 introduced Model–View–Controller: the *model* holds domain state and notifies dependents (changed:), the *view* displays it (update:), and the *controller* handles input. Squeak still ships an MVC project type; Pharo's tools use Morphic for rendering and Spec2 for application UIs, but the separation of model and presentation remains central.
35.2 Morphic
Morphic (from Self, by John Maloney and Randy Smith) is a direct-manipulation UI framework: everything on screen is a morph you can grab, resize, inspect, and script live.
For animation, override step (called periodically) and stepTime (milliseconds between steps) — see §42.2.
Alt/Cmd+Shift+click (or middle-click, depending on settings) on a morph opens its halo with handles for resizing, rotating, inspecting, and debugging.
Pharo is also developing Bloc (a new vector-based graphics/UI framework, used by Glamorous Toolkit) as a future successor to Morphic.
| m |
m := Morph new.
m color: Color orange; extent: 100 @ 60; position: 200 @ 200.
m openInWorld.
m addMorph: (StringMorph contents: 'Hi!').
m delete.
(EllipseMorph new color: Color blue; extent: 80 @ 80) openInWorld.Morph << #ClickCounterMorph
slots: { #clicks };
package: 'Morphic-Demo'
ClickCounterMorph >> initialize
super initialize.
clicks := 0.
self extent: 120 @ 60; color: Color lightGreen
ClickCounterMorph >> drawOn: aCanvas
super drawOn: aCanvas.
aCanvas
drawString: 'Clicks: ' , clicks printString
at: self position + (10 @ 20)
font: nil
color: Color black
ClickCounterMorph >> handlesMouseDown: anEvent
^ true
ClickCounterMorph >> mouseDown: anEvent
clicks := clicks + 1.
self changed "request a redraw"ClickCounterMorph new openInWorld.35.3 Spec2
Spec2 is Pharo's declarative UI framework (default since Pharo 9). You define presenters (components) composed with layouts; Spec2 renders them with a backend — Morphic (default) or GTK — so the same code can produce native-looking UIs. All recent Pharo tools (Iceberg, debugger, inspector parts, Playground) are Spec2 applications.
SpPresenter << #CounterPresenter
slots: { #count . #label . #incButton . #decButton };
package: 'Spec-Demo'
CounterPresenter >> initializePresenters
count := 0.
label := self newLabel.
incButton := self newButton
label: '+';
action: [ self increment: 1 ];
yourself.
decButton := self newButton
label: '-';
action: [ self increment: -1 ];
yourself.
self updateLabel
CounterPresenter >> defaultLayout
^ SpBoxLayout newTopToBottom
add: label expand: false;
add: (SpBoxLayout newLeftToRight
add: decButton;
add: incButton;
yourself);
yourself
CounterPresenter >> initializeWindow: aWindowPresenter
aWindowPresenter
title: 'Counter';
initialExtent: 250 @ 120
CounterPresenter >> increment: delta
count := count + delta.
self updateLabel
CounterPresenter >> updateLabel
label label: 'Count: ' , count printStringCounterPresenter new open.35.4 Common Spec2 presenters
self newLabel self newButton Text input (single line) self newTextInput self newText self newCode
self newList (items:, display:, whenSelectionChangedDo:)
self newTable (addColumn: (SpStringTableColumn title: 'Name' evaluated: #name)) self newTree (roots:, children:) Checkbox / radio
self newCheckBox, self newRadioButton self newDropList Menu / toolbar self newMenu, self newToolbar self instantiate: OtherPresenter
35.5 Master–detail and legacy Spec
Wire presenters together with events: list whenSelectionChangedDo: [ :selection | detail text: selection selectedItem printString ], laid out with SpPanedLayout newLeftToRight. Older Spec (Spec 1) used a class-side defaultSpec method returning a layout and initializeWidgets; you will meet both in older code.
35.6 Spec applications
For larger tools, subclass SpApplication to manage windows, configuration, and backend selection (application useBackend: #Gtk), and use commands (CmCommand) for menus and toolbars.