Skip to main content
Engineering LibreTexts

10.5: Layouts

  • Page ID
    43719
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    A layout defines how a collection of elements is automatically arranged. To apply a layout, use the on: message with a collection of ROElements as parameter. In the example shown in in Figure \(\PageIndex{1}\) we use the spriteOn: convenience message to create a collection of ROElements, each one with size 50, shaped with a red border and draggable. We then apply a layout to arrange the elements on a grid.

    view := ROView new.
    view addAll: (ROElement spritesOn: (1 to: 4)).
    ROGridLayout on: view elements.
    view open.
    
    A grid layout.
    Figure \(\PageIndex{1}\): ROGridLayout applied to a group of ROElements.

    Figure \(\PageIndex{2}\) illustrates some of the layouts available in Roassal. These layouts, in addition to those not presented here, can be found as subclasses of ROLayout.

    Some of the available layouts.
    Figure \(\PageIndex{2}\): Some of the layouts available applied to a group of elements.

    As a layout is applied to a collection of elements, different sets of elements can have different layouts. In the following example two collections of elements are arranged with two layouts. The first one aligns elements along a vertical line and the second along a horizontal line. We first create elements for the vertical line, apply the ROVerticalLineLayout and shape them with a label. We then do the same for the second group, using the ROHorizontalLineLayout and spacing them to avoid overlapping.

    | view verticalElements horizontalElements |
    view := ROView new.
    verticalElements := ROElement spritesOn: (1 to: 3).
    ROVerticalLineLayout on: verticalElements.
    verticalElements do: [ :el | el + ROLabel ].
    horizontalElements := ROElement spritesOn: (4 to: 6).
    ROHorizontalLineLayout on: horizontalElements.
    horizontalElements do: [ :el |
        el + ROLabel.
        el translateBy: (60@ 0) ].    "spacing"
    view
        addAll: horizontalElements;
        addAll: verticalElements.
    view open.
    
    Applying different layouts to elements.
    Figure \(\PageIndex{3}\): Applying different layouts to different sets of elements.

    Layouts in nested structures. Layout of nested elements is relative to each element’s containers. In the following example, two elements are created, each one with three child elements arranged as a grid. We finally arrange the parent elements using a horizontal line layout.

    view := ROView new.
    elements := (ROElement spritesOn: (1 to: 2)).
    elements
        do: [ :el | el addAll: (ROElement spritesOn: (1 to: 3)).
            "arranging the children nodes"
            ROGridLayout on: el elements.].
    view addAll: elements.
    ROHorizontalLineLayout on: view elements.
    view open.
    
    Nested elements with different layouts.
    Figure \(\PageIndex{4}\): Nested elements with different layouts.

    Creating a new layout. Roassal offers a number of layouts (over 23 at the time this chapter is being written). It may happen that one needs a new layout to accommodate a particular representation. This section is about dedicate layouts. Before jumping into the creation a new layout, we first need to understand how layouts are structured.

    All layout classes inherit from ROLayout. This class defines the most commonly used method to apply a layout, on:, from the instance or from the class side. The method on: calls to executeOnElements:, which is the main method to apply a layout. This method is shown in the following code:

    ROLayout >> executeOnElements: elements
        "Execute the layout, myself, on the elements"
        maxInterations := elements size.
        self doInitialize: elements.
        self doExecute: elements asOrderedCollection.
        self doPost: elements.
    

    The executeOnElements: method invokes three hook methods:

    1. doInitialize: Method executed before beginning the layout. Useful when the graph to be ordered needs to be prepared;
    2. doExecute: Apply the layout algorithm. Elements are relocated accordingly;
    3. doPost: Method executed after having performed the layout.

    A pre- and post-processing may be defined. This is useful for example if the layout is multi-staged or if appropriate events have to be emitted. These actions are set as callbacks using the ROLayoutBegin and ROLayoutEnd events. ROLayoutBegin and ROLayoutEnd are announced by doInitialize: and doPost:, respectively. An example of its use is shown in the following code:

    | layout t |
    t := 0.
    layout := ROHorizontalLineLayout new.
    layout on: ROLayoutBegin do: [ :event | t := t + 1 ].
    layout on: ROLayoutEnd do: [ :event | t := t + 1 ].
    layout applyOn: (ROElement forCollection: (1 to: 3)).
    self assert: (t = 2).
    

    The doExecute: method arranges elements using a particular algorithm. This method takes as parameter the collection of elements to lay out.

    Now we know the structure of the ROLayout class, we will define a new layout, called RODiagonalLineLayout, to position elements along a diagonal line. Creating a subclass of ROLayout is the first step.

    ROLayout subclass: #RODiagonalLineLayout
        instanceVariableNames: 'initialPosition'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Roassal-Layout'
    

    The instance variable initialPosition defines where the virtual line starts, which means, where the first element of the line will be located. This variable is set in an initialize method:

    RODiagonalLineLayout >> initialize
        super initialize.
        initialPosition := 0@0.
    
    RODiagonalLineLayout >> initialPosition: aPoint
        initialPosition := aPoint
    
    RODiagonalLineLayout >> initialPosition
        ^ initialPosition
    

    If the layout needed to execute special actions before or after it is applied, we would overwrite the doInitialize: or doPost: methods. However, this is not the case. The method we need to overwrite is doExecute: which actually does the job: translating all the elements along the virtual diagonal line:

    RODiagonalLineLayout >> doExecute: elements
        | position |
        position := initialPosition.
        elements do: [ :el |
            el translateTo: position.
            position := position + el extent ] 
    

    We can test our layout with the following code:

    | view elements |
    view := ROView new.
    elements := ROElement spritesOn: (1 to: 3).
    view addAll: elements.
    RODiagonalLineLayout on: view elements.
    view open.
    
    The diagonal line layout.
    Figure \(\PageIndex{5}\): Diagonal Line layout applied to a collection of elements.

    One key point of the layouts in Roassal is to consider the size of the elements to layout. When defining a new layout, remember to make your algorithm use the element size.

    The Collection hierarchy example

    As we need a hierarchy for the Collection example, the ROTreeLayout is useful in obtaining an adequate visualization.

    "Create the elements to be displayed"
    view := ROView new.
    classElements := ROElement forCollection: Collection withAllSubclasses.
    view addAll: classElements.
    associations := OrderedCollection new.
    
    classElements do: [:c |
            "Make each element reflect their model characteristics"
        c width: c model instVarNames size.
        c height: c model methods size.
            "Add shape for the element to be seen"
        c + ROBorder.
            "Make it draggable by the mouse"
        c @ RODraggable.
            "Create associations to build edges"
        (c model superclass = Object)
            ifFalse: [ associations add: ((view elementFromModel: c model superclass) -> c)]
        ].
    
        "Add edges between each class and its superclass"
    edges := ROEdge linesFor: associations.
    view addAll: edges.
        "Arrange all the elements as a hierarchy"
    ROTreeLayout new on: view elements.
    view open
    

    The resulting visualization can be seen in Figure \(\PageIndex{6}\).

    The Collection class hierarchy visualization.
    Figure \(\PageIndex{6}\): Collection class hierarchy with width representing the number of instance variables and height the number of methods.

    This page titled 10.5: Layouts is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Alexandre Bergel, Damien Cassou, Stéphane Ducasse, Jannik Laval (Square Bracket Associates) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.