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 printString
CounterPresenter 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.