Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Morphic for beginners
Last updated at 5:27 am UTC on 25 June 2018
How to set up a UI in Morphic

0. A10 Open a new Morphic project
1. Creating a Morph
2. Basic properties of a Morph
2b. How to use an IconicButton in Morphic
3. Programming Morphs (Active Essay)
4. A4 and A3 sheets in Morphic
5. Morph Structure
6. Exercise: construct a traffic light morph through code
7. Example - aMorph addMorph: anotherMorph (no layout and TableLayout)
8. CrossMorph exercise : subclass Morph and override drawOn: method
9. How to get control over your objects
10. How do I center a child morph object within the parent object?
11. TableLayout listCentering demo (Squeak 3.2 and later)
12. TableLayoutExample with 100 squares - v2
13. Exercise: construct a launcher window with an AlignmentMorph
14. MousePositionInfoMorph
15. Exercise: construct a launcher window - version 2

Also useful and fairly easy to construct is a button which brings up a menu to execute various commands:
MenuMorph and Menu Morphs Demo.

More advanced is
Method for making Morphs


Note: The tutorial below was salvaged from archive.org
It is out of date.
Note: Some parts were updated but to make sense more work is needed.jrm

This tutorial explains some workings of morphic and shows how I implemented the GUI for the Subway Simulation Project.


Create a new Morphic project and add a SimpleButtonMorph


The first step in working with morphic is to create a new MorphicProject. Choose from the toolbar 'Projects' -> 'New project' -> 'New MorphicProject'.

A new MorphicProject is opened.

The background you see is a Morphic world. This is the main morph (actually an instance of PasteUpMorph, something like a "canvas") to which all the widgets and other components are added.

A morphic world is quite robust and supports adding objects as needed and allows for changes in color, and such, as well.

Let's add a simple button to the world.

1) Left click in the world. This gives you a World menu.

2) Select the 'new morph' menu entry.

3) Select Morphic-Widgets|SimpleButtonMorph.

4) Click anywhere to place your button.

You now have a MorphicProject with a button. Now experiment a little and add some other morphs to your world.

Another great feature of the morphs is that they can all be modified after being placed. Right clicking on our button brings up a menu of available modifications. Let's start by moving the button around.


Modify the SimpleButtonMorph

1) Right click on the button and choose 'select' from the menu. You get so called Halos placed around the morph.

SimpleButton_with_halos_2017-10-30.png

2) With the black halo (dot) you may move the button around. You can place the button anywhere you wish.

3. With other halos you can also make some other modifications such as resizing and color changing. Try out some of the other modifications until you feel comfortable with it.

Now that we discussed how to add the buttons for our GUI, let's add an area where we will show the text for our output.


Add an area for text (TextMorph)


We will use a TextMorph to show the output. Now add a TextMorph to the world.

1) Right click the world and select 'new morph'|Morph-Basic|TextMorph.

You now have text area to show the output.


Adding a button through code


Above you added a button through direct manipulation, i.e. by choosing an entry in a menu. You may also add a button by executing a snippet of code.


Open a Workspace by left click -> WorldMenu -> 'Workspace'


Enter the following text and execute it.

| aButton |
aButton := SimpleButtonMorph new initialize. "initialize the button"
aButton bounds: (112@69 corner: 158@87) . "set up it coordiantes and size"
aButton label:'Button Label'. "change the button label"
Project current world addMorph: aButton. "tell the world to add the button"

You will now get a button added to your world. You may wonder where I got the value for my bounds. Like all objects, morphs can be inspected, and from there we can get some information about them.

Do the following to this:

1) Right click on your button and select 'inspect'.

You now have a new window showing all the variables of the morph. Now just place the button where you would like it and copy the bounds value after inspecting, and you have the coordinates to accurately place any object.

I will now show, step by step, how I created my user interface.

The first thing I did was make a new object and subclassed it off of SimpleButtonMorph. The reason for this: you must overwrite 'doButtonAction' method which defines what occurs when the button gets clicked.

In my doButtonAction the first thing I do is create a temprorary SubwaySimulation. This will let us access the output from the simulation. To ease the process, you should create methods in your program to return the output as a string.

Next I get the textArea I will be changing. I do this with the line:
textArea := owner findA:TextMorph.

The owner is the worldMorph and is analogous to calling super. findA returns the textmorph that was added to the world.

The rest of the method goes through various tests to determine which button was clicked, and sets the textArea content accordingly.

This is probably not the best way to this, but it is the simplest and fastest. A better way involves changing the doButtonAction method when you set your button up, so the doButtonAction would be different for each instance of the button and no tests would be needed.

Now let's add the buttons to the morph. This is done as outlined above. So simply add as many buttons as needed to satisfy your button needs. You can get their coordinates by first placing them then inspecting them.

Do the same for the text area and you are done. Now every time a button is clicked, the text area should change in accord with the string return from the associated method.

I include the code for my button and output methods so you can play around with it some more.

Nezir Teke: 
Winter 1998


Code needs to be updated

'From Squeak 1.3 of Jan 16, 1998 on 14 March 1998 at 3:31:15 pm'!
SimpleButtonMorph subclass: #SubwayButton
	  instanceVariableNames: ''
	  classVariableNames: ''
	  poolDictionaries: ''
	  category: 'Subway'!

!SubwayButton methodsFor: 'events' stamp: 'nt 2/26/98 00:00'!
doButtonAction</b>
	  "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments."
| textArea temp |
temp := SubwaySimulation new. 
textArea :=  owner findA:TextMorph. 

(self label = 'Train 1')
ifTrue: [ textArea contents: temp trainOutput1]
ifFalse: [ (self label = 'Train 2')
            ifTrue: [textArea contents: temp trainOutput2]
            ifFalse: [ (self label = 'Train 3')
                       ifTrue: [textArea contents: temp trainOutput3]
                       ifFalse: [ (self label = 'Train 4')
                                 ifTrue: [textArea contents: temp trainOutput4]
                                 ifFalse: [ (self label = 'Train 5')
                                 ifTrue: [textArea contents: temp trainOutput5]
                                 ifFalse: [textArea contents: temp systemOutput]]]]].
                        

! !



'From Squeak 1.3 of Jan 16, 1998 on 14 March 1998 at 3:31:18 pm'!
Object subclass: #SubwayOutput
	  instanceVariableNames: ''
	  classVariableNames: ''
	  poolDictionaries: ''
	  category: 'Subway'!

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 23:56'!
demo</b>

| aWorld TrainButton1 TrainButton2 TrainButton3 TrainButton4 TrainButton5

mytext outputText SystemOutputButton|



aWorld := WorldMorph new initialize.

mytext := 'I am the smartest man alive'.



"--text Area---"

outputText := TextMorph new initialize.

outputText bounds: (119@99 corner: 402@309).

outputText contents: mytext.



"--button 1---"

TrainButton1 := SubwayButton new initialize.

TrainButton1 bounds: (112@69 corner: 158@87) .

TrainButton1 label:'Train 1'.




"---button 2----"

TrainButton2 := SubwayButton new initialize.

TrainButton2 bounds: (171@69 corner: 207@87) .

TrainButton2 label:'Train 2'.




"---button 3----"

TrainButton3 := SubwayButton new initialize.

TrainButton3 bounds: (231@69 corner: 279@87) .

TrainButton3 label:'Train 3'.




"---button 4----"

TrainButton4 := SubwayButton new initialize.

TrainButton4 bounds: (291@69 corner: 351@87) .

TrainButton4 label:'Train 4'.




"---button 5----"

TrainButton5 := SubwayButton new initialize.
TrainButton5 bounds: (351@69 corner: 423@87) .

TrainButton5 label:'Train 5'.



"---button 6----"

SystemOutputButton := SubwayButton new initialize.
SystemOutputButton bounds: (112@42 corner: 219@60) .

SystemOutputButton label:'System Histogram'.



"-----Adds to the world------"

aWorld addMorph: TrainButton1.

aWorld addMorph: TrainButton2.

aWorld addMorph: TrainButton3.

aWorld addMorph: TrainButton4.

aWorld addMorph: TrainButton5.

aWorld addMorph: SystemOutputButton.


aWorld addMorph: outputText.



aWorld openWithTitle:'SimTrains'.! !

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 23:44'!

systemOutput</b>
"returns the output from the stats of the system"
^'Hey look at me Im the system stats'.!
]style[(12 40 10 5 34 2)f1b,f1,f1b,f1,f1b,f1! !

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:08'!
trainOutput1</b>
"returns the output from the stats of train 1"
^'I am Train 1'.! !

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!
trainOutput2</b>
"returns the output from the stats of train 1"
^'I am Train 2'.! !


!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!
trainOutput3</b>
"returns the output from the stats of train 1"
^'I am Train 3'.! !

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!
trainOutput4</b>
"returns the output from the stats of train 1"
^'I am Train 4'.! !

!SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:07'!
trainOutput5</b>
"returns the output from the stats of train 1"
^'I am Train 5'.! !



tagBeginner