View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Layouts with Morphic

Brought to you by Matt Manzel and the Dream Team:


Types of layouts

Intro to layouts
Let's start out with the table layout. Open up a workspace and get started with this:
    "This will be our parent morph. BorderedMorph's are blue by default"
tblMorph := BorderedMorph new.
"Make it a little bigger so its submorphs fit nicely"
tblMorph width: 90; height: 90.
"Here we set the layout policy"
tblMorph layoutPolicy: (TableLayout new).
"Now add some morphs with color so we can tell them apart"
tblMorph addMorph: (Morph new color: Color yellow).
tblMorph addMorph: (Morph new color: Color red).
tblMorph addMorph: (Morph new color: Color green).
"show the morphs"
tblMorph openInWorld.

This is what you should see at the top left:
Uploaded Image: 1-tbl.png

That is not really what we wanted. By default the submorphs are both set to be rigid and will not resize with their parent morph. To get them to resize with their parent you must change their resizing policies (changes in red):
tblMorph addMorph: (Morph new color: Color yellow
hResizing: #spaceFill;
vResizing: #spaceFill)
tblMorph addMorph: (Morph new color: Color red;
hResizing: #spaceFill;
vResizing: #spaceFill)
tblMorph addMorph: (Morph new color: Color green;
hResizing: #spaceFill;
vResizing: #spaceFill)

Uploaded Image: 2-tbl.png

That looks better. Now you can resize it and the submorphs flow with the parent. You can also send the hResizing or vResizing message to a submorph with #rigid as it's argument if you don't want it to resize in that direction when the parent morph is resized. Give it a try, change the red and green morphs like so:
tblMorph addMorph: (Morph new color: Color red;
hResizing: #rigid;
vResizing: #spaceFill).
tblMorph addMorph: (Morph new color: Color green;
hResizing: #rigid;
vResizing: #spaceFill).

Uploaded Image: 3-tbl.png

When you open it you can see some of the parent morph showing (the blue parts). Try resizing the morph to see the effect of the changes.

You can also get the table layout to place submorphs horizontally instead of vertically. This is done by sending the listDirection message to the parent morph. Default is #topToBottom and for horizontal layout it's #leftToRight. You might have noticed that #topToBottom actually places the morphs in reverse (bottom to top) order. The same is true for #leftToRight (right to left). Squeak just likes to screw with your head sometimes. Anyways, set the listDirection to #leftToRight after you set the layout. You also might want to change #rigid back to #spaceFill (from the previous step) to make it look nicer
tblMorph layoutPolicy: (TableLayout new).
tblMorph listDirection: #leftToRight.

Uploaded Image: 4-tbl.png

Got it? Good. Onto proportional layout. This time we'll just jump right in there. I'll explain the details below.
    "This will be our parent morph"
propMorph := BorderedMorph new.
"make it bigger"
propMorph width: 200;
height: 200.
"set the layout"
propMorph layoutPolicy: (ProportionalLayout new).
"make some morphs"
box1 := Morph new color: Color white.
box2 := Morph new color: Color orange.
box3 := Morph new color: Color black.
box4 := Morph new color: Color lightBlue.
"make some rectangles dimensions"
rect1 := (0 @ 0 extent: 0.5 @ 0.5).
rect2 := (0 @ 0.5 extent: 0.5 @ 0.5).
rect3 := (0.5 @ 0 extent: 0.5 @ (1/3)).
rect4 := (0.5 @ (1/3) extent: 0.5 @ (2/3)).
"add morphs to the parent"
propMorph addMorph: box1
fullFrame: (LayoutFrame fractions: rect1).
propMorph addMorph: box2
fullFrame: (LayoutFrame fractions: rect2).
propMorph addMorph: box3
fullFrame: (LayoutFrame fractions: rect3).
propMorph addMorph: box4
fullFrame: (LayoutFrame fractions: rect4).
propMorph openInWorld.

Execute that and you should see this:
Uploaded Image: 5-prop.png

Okay, that code looks confusing but it's not too bad. First we create the parent morph and set it's size and layout just like before. Then we create a bunch of morphs that we are going to stick in the parent morph. Easy so far.

After we create the boxes we then define rectangle dimensions. These define how each box will resize to fill the parent morphs dimensions. The first point given is in the form of (x @ y) and is the top left corner of where the triangle is to be drawn. Each x and y is a number from 0 to 1 describing a point relative to the parent rectangles full size. So (0 @ 0) would be the top left and (0.5 @ (1/3)) would be halfway to the right, and 1/3 the way down. The second point (after extent:) similarily is of the form of (width @ height). This is also a number from 0 to 1 and is relative to the parent. So (0.5 @ (2/3)) is a rectangle whose width is half of the width of the parent morph and whose height is 2/3 of the parent morph. So the full syntax for giving the rectangle dimensions is (x @ y extent: width @ height).

Then we add the box to the morph at the given dimensions and show the morph. Resize the morph and see how everything adapts.

How to use layouts effectively
Table layouts and proportional layouts aren't too useful by themselves. To make your components behave how you want you have to use the different layouts in combination. Once you get the hang of layouts you can create incredibly useful programs like this one:
    "The idiot morph will be the parent morph"
idiotMorph := BorderedMorph new color: Color white.
idiotMorph width: 400;
height: 400.
idiotMorph layoutPolicy: (TableLayout new).
"The eyebar morph will be a bar at the top with eyes on either end"
eyeBarMorph := BorderedMorph new color: Color green.
eyeBarMorph height: 46.
"we want it to stretch horizontally but not vertically"
eyeBarMorph hResizing: #spaceFill.
eyeBarMorph layoutPolicy: (TableLayout new).
"add morphs horizontally right to left (no that's not a typo)"
eyeBarMorph listDirection: #leftToRight.
"create a spacefill morph to place between the eyes"
"keeping them separated when we resize the window"
spacerMorph := Morph new color: Color green.
spacerMorph hResizing: #spaceFill.
"add the eyes and spacer"
eyeBarMorph addMorph: MovingEyeMorph new.
eyeBarMorph addMorph: spacerMorph.
eyeBarMorph addMorph: MovingEyeMorph new.
"This is a big panel with a bunch of widgets that will go"
"below the eyebar morph"
widgetPanelMorph := Morph new.
"fill all available space"
widgetPanelMorph hResizing: #spaceFill;
vResizing: #spaceFill.
widgetPanelMorph layoutPolicy: (ProportionalLayout new).
box1 := MonthMorph new.
box2 := Flasher new.
box3 := WatchMorph new.
box4 := ClockMorph new.
box5 := FrameRateMorph new.
"widget dimensions defined as rectangles"
rect1 := (0 @ 0 extent: 0.5 @ 0.5).
rect2 := (0 @ 0.5 extent: 0.5 @ 0.5).
rect3 := (0.5 @ 0 extent: 0.5 @ 0.5).
rect4 := (0.5 @ 0.5 extent: 0.5 @ 0.25).
rect5 := (0.5 @ 0.75 extent: 0.5 @ 0.25).
"add the wigets to the widget panel"
widgetPanelMorph addMorph: box1 fullFrame: (LayoutFrame fractions: rect1);
addMorph: box2 fullFrame: (LayoutFrame fractions: rect2);
addMorph: box3 fullFrame: (LayoutFrame fractions: rect3);
addMorph: box4 fullFrame: (LayoutFrame fractions: rect4);
addMorph: box5 fullFrame: (LayoutFrame fractions: rect5).
"add reverse order"
idiotMorph addMorph: widgetPanelMorph.
idiotMorph addMorph: eyeBarMorph.
"prepare to be amazed"
idiotMorph openInWorld.

You should see this beast:
Uploaded Image: 6-idiot.png

Further reading
This tutorial just touches on the basics of squeak layouts. Look in the package loader (squeak map) for TableLayout Tutorial Project and poke around sqeuak to learn more.

Links to this Page