Skip to content

Introduction to morphs Layout

Dr. Nicola Mingotti edited this page Jan 21, 2022 · 23 revisions
  • I made a video tutorial mirroring this page content, i think it is much more fun and impressive than reading what follows. Reading is cool if you need to come back at some procedures, but sub-optimal if it is the first time you see this.
  • So, first of all, resources you could use to enrich this text
    • Read the Cuis Terse Guide, at the sections "Layout Morph" and "Layout Spec"
    • Also, this is a transition moment in Cuis interface, we just came into Cuis 6.X and there is a lot of class renaming going on. So, instead of pointing you to a link which may be old in a few weeks I recommend you to Google "Ken Dickey Cuis" or go to see this guy GitHub repositories. I could get started reading his documents. Or, if you feel strong enough, just see the Cuis implementation of itself in the Browser. Again, there is also a Juan document which is more terse but also more detailed.
  • Let's start !
  • At the end of this document there is the whole code in a chunk to facilitate you the copy and paste to your Workspace.
  • Ensure you updated Cuis to Cuis6.X, better safe than sorry.
  • make a LayoutMorph, which is just a container for other morphs
lay1 _ LayoutMorph new.
lay1 openInHand .
  • Change the layout color
lay1 beRow :: borderWidth: 1 :: borderColor: (Color black) .
lay1 color: (Color gray) .
  • Let's add in image morph, which by default contains the Cuis logo.
im1 _ ImageMorph new.
lay1 addMorph: im1.
  • Add a LabelMorph, which is just a text container, this text is not changeable by the user.
lab1 _ LabelMorph new.
lab1 contents: 'Example'.
lay1 addMorph: lab1.
lab1 borderWidth: 1 :: borderColor: (Color black).
lab1 layoutSpec
  • Let's add a second label
lab2 _ LabelMorph new.
lab2 contents: 'Example 2'.
lay1 addMorph: lab2.
lab2 borderWidth: 1 :: borderColor: (Color red).
  • Let's add  a BoxedMorph, which is just a morph with rectangular shape. This is what you may call a widget in other UI toolkit where basically everything has a rectangular border. Well, Cuis is more advanced than that and has gone beyond the year 1990.
rec1 _ BoxedMorph new.
lay1 addMorph: rec1.
rec1 color: (Color cyan).
rec1 borderWidth: 3 :: borderColor: (Color orange).
  • Exercise. Open the Browser and go to check who is the superclass of  LabelMorph
  • We choose a bad name for the last variable .... let's delete the morph
rec1 delete.
  • ... and re do it
box1 _ BoxedMorph new.
lay1 addMorph: box1.
box1 color: (Color cyan).
box1 borderWidth: 3 :: borderColor: (Color orange).
  • enlarge the layout with the mouse ...  nothing happens to the submorphs, they stay still
  • swap the row or column placement of the submorphs
lay1 beColumn .
lay1 beRow.
  • Set separation space between the morphs, note that it makes space also from the layout border
lay1 separation: 30.
  • Morph placement along the main axis. That is, center, left align, right align ... or, some variation of that. There are keywords to specify that but I prefer the numeric values. 0.0 is what you expect by default: left side, or top side, and 1.0 is the opposite side. That is our natural way of writing directions, we start writing at the top of the page and we go from left to right.
lay1 axisEdgeWeight: 1.0 .      " left side "
lay1 axisEdgeWeight: 0.0 .      " right side, the default "
lay1 axisEdgeWeight: 0.5 .      " in the middle "
  • Exercise. layout and vertical direction (beColumn) and re-run the last 3 lines.
  • Up to now the submorphs are indifferent to the shape of their container, the LayoutMorph lay1. But this is not what you want in general, the most usual case is that you want widgets to change their size according to the container windows. To do that we use a LayoutSpec which tell the containing Layout how we want the widget to be represented inside it.
  • Exercise. Open the Browser, go to the LayoutSpec class and read the comments.
  • Let's peek inside a LayoutSpec to have an idea of what we can do with it
lab2 layoutSpec explore .   ". to have an idea of what we can change "
  • We want lab2 to take all possible available extra x-space. ATTENTION. Tweak the layout lay1 dimension manually with the mouse to be sure dimensions get recomputed.
lab2 layoutSpec proportionalWidth: 1.0.  " take all posibble extra x-space "
  • Now, tell lab2 to take only half of the available x-space
lab2 layoutSpec proportionalWidth: 0.5.  " take half of the available extra x-space "
" -> again, toggle size manually to see the difference "
  • Now we want  lab1 and lab2 to share equally the extra space available.
lab1 layoutSpec proportionalWidth: 0.5.
  • We want to get the with and height of a morph, this of course makes sense for subclasses of BoxedMorph. See next exercise.
lab2 morphWidth . " 487.0 "
lab2 morphHeight . " 36 "
  • Exercise. Open the Browser and go to visit the LabelMorph class. Righ click over it and select BrowseProtocol. Now, find the method morphWidth and check it is defined because LabelMorph is a subclass of BoxedMorph.
  • Displace the widget from its natural alignment axis ... this is easier to see than to say.
lab2 layoutSpec offAxisEdgeWeight: 0.
lab2 layoutSpec offAxisEdgeWeight: 1.
lab2 layoutSpec offAxisEdgeWeight: 0.5.
  • Change the vertical size of the contained Morphs
lab2 layoutSpec proportionalHeight: 0.8.
lab1 layoutSpec proportionalHeight: 0.6.
  • ... to the ImageMorph nothing happens, why that ? Well, the image is defined in pixel size so if we increase its size it is not so obvious what the image should do. But the ImageMorph got your intent and you can visualize it adding a border.
im1 layoutSpec proportionalHeight: 0.9.
im1 borderWidth: 1 :: borderColor: (Color purple).
  • What will happen to the box? well,  we expect it to resize fully in a bigger/smaller cyan area since this is just the common sense operation.
box1 layoutSpec proportionalHeight: 0.2 .

All the code in one piece

". INTRO ---- make 2 independent simple morphs " 
lab1 _ LabelMorph new. 
lab1 contents: 'hello world !' .
lab1 openInHand . 

lab2 _ LabelMorph new. 
lab2 contents: '... and something else' .
lab2 openInHand . 

". make a layout, i.e.  a container for other Morphs" 
lay1 _ LayoutMorph new.
lay1 openInHand .
lay1 beRow :: borderWidth: 1 :: borderColor: (Color black) .

". change the color "
lay1 color: (Color gray) .

". make an image morph and add it to the layout "
im1 _ ImageMorph new.
lay1 addMorph: im1.

". add a first label, and add it to the layout "
lab1 _ LabelMorph new.
lab1 contents: 'Example'.
lay1 addMorph: lab1.
lab1 borderWidth: 1 :: borderColor: (Color black).


". add a second label "
lab2 _ LabelMorph new.
lab2 contents: 'Example 2'.
lay1 addMorph: lab2.
lab2 borderWidth: 1 :: borderColor: (Color red).

". add a boxedMorph, which is just a morph with rectangular shape "
rec1 _ BoxedMorph new.
lay1 addMorph: rec1.
rec1 color: (Color cyan).
rec1 borderWidth: 3 :: borderColor: (Color orange).

". we choose a bad name for the variable .... let's delete the morph and make a new one "
rec1 delete.
box1 _ BoxedMorph new.
lay1 addMorph: box1.
box1 color: (Color cyan).
box1 borderWidth: 3 :: borderColor: (Color orange).


". with the mouse enlarge the layout morph, nothing happens to the submorphs, they stay still ! "

". swap the row or column placement of the labels "
lay1 beColumn .
lay1 beRow.

". set sparation space between the morph, note that space from the border
  is included "
lay1 separation: 30.

". align: left,right,center   "
lay1 axisEdgeWeight: 1.0 .      " left side "
lay1 axisEdgeWeight: 0.0 .      " right side, the default "
lay1 axisEdgeWeight: 0.5 .      " in the middle "

"EXERCISE: Repeat the operation after a beColumn "

". let's expand individual widgets using LayoutSpec  "

". to have an idea of what we can change "
lab2 layoutSpec explore .   

 " lab2 takes all posibble extra x-space. TOGGLE layout size to force dimensions recompute  "
lab2 layoutSpec proportionalWidth: 1.0. 


" take half of the available extra x-space "
lab2 layoutSpec proportionalWidth: 0.5.  


". now lab1 and lab1 share the extra space equally "
lab1 layoutSpec proportionalWidth: 0.5.

" . for a BoxedMorph subclass you can ask for width and height.  "
lab2 morphWidth .   " 487.0 "
lab2 morphHeight .  " 36 "

". displace the widget from its naturali alignment axis "
lab2 layoutSpec offAxisEdgeWeight: 0.
lab2 layoutSpec offAxisEdgeWeight: 1.
lab2 layoutSpec offAxisEdgeWeight: 0.5.

". let's adapt the widget Y-dimension " 
lab2 layoutSpec proportionalHeight: 0.8.
lab1 layoutSpec proportionalHeight: 0.6.

" . let's do it to the image morph ... nothing happens ? add a border and see. "
im1 layoutSpec proportionalHeight: 0.9.
im1 borderWidth: 1 :: borderColor: (Color purple).
Clone this wiki locally