Chapter 26. Views

Table of Contents

1. Fundamental Properties
1.1. views are rectangular containers
1.2. view visibility
1.3. Z Axis
1.4. Nesting views within views
1.5. Sizing view to its child views
1.6. views, constraints and layouts
2. The View Coordinate system
2.1. Relative positions
2.2. Sizing of views
2.3. Scaling
2.4. Offsets
3. Positioning of Views
3.1. Declarative positioning of views using absolute coordinates
3.2. Declarative positioning of views using constraints
3.3. Negative coordinate values
4. View Hierarchy
4.1. Children and Parents
5. Views and Nodes
6. How views are built
6.1. Adding subviews
7. Distinguishing features of OpenLaszlo's Coordinate System

1. Fundamental Properties

Views are the fundamental visible elements of OpenLaszlo applications. Anything that is displayed on the canvas is a <view> or extends the view class.

1.1. views are rectangular containers

Visually, a <view> is a rectangular container. As such it has a height, a width, and a placement, which is denoted by the x and y values of its upper left corner. Views can have background colors, although by default they are transparent. Transparent views can contain arbitrarily shaped images. Thus although views are rectangular, OpenLaszlo applications can have virtually any appearance.

Logically, views are objects that act as containers. Views may contain other views, called subviews or child views, and may be associated with resources such as images, audio or video files, and Flash Player files in .swf format. Thus although a view usually has a visual representation on the canvas, it is also possible to have, for example, a view of zero width and zero height that plays, for example, an audio file.

Example 26.1. simple view

<canvas height="50" width="500">
  <view width="50" height="50" bgcolor="red"/> 
</canvas>

Example 26.2. invisible view

<canvas height="50">
  <view height="20" width="20" clickable="true"/>
</canvas>

The following example shows that the view, although invisible, is indeed present. To see that this is so, click in the upper left corner, and then elsewhere on the canvas.

Example 26.3. clicking on invisible view

<canvas height="160" debug="true">
  <debug y="30"/>
  <view height="20" width="20" onclick="Debug.write('howdy')"/>
</canvas>

1.2. view visibility

A view is only visible if it has a color or an image assigned to it, and if the height and width are greater than zero. For example, the following code displays only two images even though four views are defined. The second and third views exist but they are completely transparent. The second has no color assigned to it and the third has zero height. They still, however, affect the arrangement of the other two views.

Example 26.4. invisible views affect placement

<canvas height="60" width="500">
  <view width="50" height="50" bgcolor="red"/>  1
  <view width="50" height="50"/>                2
  <view width="0" height="50" bgcolor="blue"/>  3
  <view resource="../images/logo.png"/>         4
  <simplelayout axis="x" spacing="5"/> 
</canvas>

1 This view is a red square.
2 This view has no visual representation because it has no color, but it still exists and displaces other views.
3 This view has no visual representation because it has no width, but it still exists.
4 This view displays the jpg image.

1.3. Z Axis

Aside from the x and y axes, there is also a z axis in the OpenLaszlo view hierarchy. Two sibling views will overlap; the latter (in lexical order of the LZX code) will appear on top of the former. This order can be changed using the bringToFront() and sendToBack() methods:

Example 26.5. Simple Layout

<canvas height="100">
  <view bgcolor="red" width="50" height="50" 
        onclick="this.bringToFront()"/>
  <view bgcolor="blue" width="50" height="50" x="20" y="20"/>
  <view bgcolor="green" width="50" height="50" x="40" y="40"
        onclick="this.sendToBack()"/>
</canvas>

There are also methods such as sendBehind() and sendInFrontOf(), that allow you to move a view in the z-axis more precisely, relative to a specified view:

Example 26.6. fixme5

<canvas height="100">
  <view bgcolor="red" id="red" width="50" height="50" 
        onclick="this.sendInFrontOf(blue)"/>
  <view bgcolor="blue" id="blue" width="50" height="50" x="20" y="20"
        onclick="this.sendInFrontOf(green)"/>
  <view bgcolor="green" id="green" width="50" height="50" x="40" y="40"
        onclick="this.sendBehind(red)"/>
</canvas>

1.4. Nesting views within views

Views can also be contained within other views, allowing you to create complex visual elements. Each parent view can have any number of children, and each child view is positioned relative to the top-left corner of its parent, as shown here:

Example 26.7. view containing other views

<canvas height="200" width="500">
  <view bgcolor="red" x="50" y="50" width="100" height="100">
    <view bgcolor="blue" width="50" height="50"/>
    <view bgcolor="yellow" x="50" y="50" width="60" height="105">
       <view width="50" height="50" bgcolor="green"/>
    </view>
   </view>
</canvas>

Each child view has one and only one parent view. The relationship between parent and child views is examined below and in later chapters.

1.5. Sizing view to its child views

If no width and height are actually defined for a view, then it adopts the width and height of the bounding box of its subviews. The example above also shows, however, that the width and height of a view can be different than the dimensions of the bounding box of its child views. The 'yellow' view is not clipped even though it lies outside the boundary of its parent. If clipping is desired, however, then the attribute clip="true" can be added to the parent:

Example 26.8. using the clip attribute

<canvas height="150" width="500">
  <view bgcolor="red" x="50" y="50" width="100" height="100" clip="true">
    <view bgcolor="blue" width="50" height="50"/>
    <view bgcolor="yellow" x="50" y="50" width="60" height="105">
       <view width="50" height="50" bgcolor="green"/>
    </view>
  </view>
</canvas>

As stated above, views don't need to display an image or color to affect other views. These types of views are referred to as "blank views". In fact, this technique of using blank views to group others views is used extensively in the sample applications. The code below shows what happens when bgcolor="red" is removed from the outermost view.

Example 26.9. blank view

<canvas height="200" width="500">
  <view x="50" y="50" width="100" height="100">
    <view bgcolor="blue" width="50" height="50"/>
    <view bgcolor="yellow" x="50" y="50" width="60" height="105">
       <view width="50" height="50" bgcolor="green"/>
    </view>
  </view>
</canvas>

1.6. views, constraints and layouts

The fact that a view can be assigned different dimensions than the bounding box of its subviews is taken advantage of by other elements within OpenLaszlo such as constraints (Chapter 27, Constraints) and layouts (Chapter 17, Layout and Design).

To illustrate this point, the following example has two views with identical dimensions and subviews. The only difference in their code is that the top view has a <simplelayout> and the second view has a <stableborderlayout> . The <simplelayout> ignores the width of the parent, while the <stableborderlayout> stretches the middle subview so that the combined width of all three subviews matches the width of their parent.

Don't worry if you don't understand how to use the layouts described below; layouts are fully explained in Chapter 17, Layout and Design. The crucial point to grasp is that a view can have different dimensions than the bounding box of its subviews. This fundamental property of the OpenLaszlo view system comes up in countless situations.

Example 26.10. layouts and bounding box of subviews

<canvas height="100" width="500">
  <view bgcolor="red" width="200" height="30">
    <view bgcolor="blue" width="30" height="30"/>
    <view bgcolor="yellow" width="30" height="30"/>
    <view bgcolor="blue" width="30" height="30"/>
    <simplelayout axis="x" spacing="0"/> 
  </view>
  <view bgcolor="red" width="200" height="30" y="50">
    <view bgcolor="blue" width="30" height="30"/>
    <view bgcolor="yellow" width="30" height="30"/>
    <view bgcolor="blue" width="30" height="30"/>
    <stableborderlayout axis="x"/> 
  </view>
</canvas>

2. The View Coordinate system

Starting with the canvas, each view has its own internal 2D coordinate system, with as you would expect, the horizontal axis named x and the vertical axis named y. The position of a view is calculated relative to its parent's coordinate system.

As view hierarchies can get complex, it's important to keep in mind, for any view:

  1. where its origin is

  2. units of measurement

  3. its relationship to its parent's coordinate system

In the simple case where a view is, for example, the child of the canvas, understanding its origin and coordinate system is simple. Other cases can get rather subtle — as for example when a view's x or y value is a negative number, or when a parent view changes size. These cases are explored below.

2.1. Relative positions

To position the floating view over the clicked view, you can use the getAttributeRelative() method of view to obtain the x and y coordinates of the view relative to a view other than its parent. For example, consider the case below:

Example 26.11. relative positions

<canvas height="350" debug="true">
  <debug y="60" />
  <view name="parentView" x="10" bgcolor="blue" height="40" width="40"
   oninit="Debug.write('parentView x is', x)" >
    <view name="childView" x="35" bgcolor="yellow" height="20" width="20"
    oninit="Debug.write('childview x is', x)"/>
  </view>
</canvas>

childView's x value is 35, but its x value relative to the canvas is 45 (because its parent, parentView, is already 10px in from the canvas's origin). To place a view cover that is a child of canvas on top of childView, give it an x value of 45, because this is the same offset relative to cover's parent (the canvas) that childView has relative to the canvas.

2.2. Sizing of views

A view that has no width or height specified will be 0px wide by 0px high:

<view/>

Either dimension of a view can be set explicitly:

<view width="20"/> 1
<view width="20" height="35"/>
1 This view is not visible, because it has a height of zero

If a view's dimension is not explicitly set, and that view contains subviews (or has a resource attached), then the view will automatically scale to the extents of its contents or resource. In this case the yellow view will be 40px wide and 25px high:

<view bgcolor="yellow">
  <view bgcolor="red" width="10" height="10" x="30" y="15"/>
</view>

In the case above, the yellow view will resize as its contents resize and move. If a dimension had been set at any point then that axis would not resize. This is an important consideration if a nested view is to be moved while the application is running (e.g. if it is dragged by the user). The size of its parent will adjust as necessary:

Example 26.12. Resizing View

<canvas height="100">
  <class name="dragBox" onmousedown="dragger.apply()"
           onmouseup="dragger.remove()" clickable="true" 
           width="10" height="10" bgcolor="red">
      <dragstate name="dragger"/>
  </class>

  <view bgcolor="yellow" y="10" x="10">
    <dragBox x="30" y="10"/>
  </view>
</canvas>

To get around this problem (in the example of a view that is dragged), there are two solutions:

  1. Limit the drag area of the view to its parents dimensions, and give its parent an explicit size.

  2. Make the view that is dragged a child of the canvas, so that it is not embedded in the view hierarchy.

There is no way to unset a dimension, so that the view returns to resizing to its contents automatically.

Even if a view's dimensions are set explicitly, and the contents of the view are larger than those dimensions, the view will remain the set size. You can retrieve the computed width or height using the measureWidth() and measureHeight() methods respectively.

<view bgcolor="yellow" width="30" height="100" clickable="true">
  <handler name="onclick">
    Debug.write("Actual Width: " + this.width);
    Debug.write("Actual height: " + this.height);
    Debug.write("Measured Width: " + this.measureWidth());
    Debug.write("Measured Height: " + this.measureHeight());
  </handler>

  <view bgcolor="red" width="10" height="10" x="30" y="15"/>
</view>

If a view attaches a resource (and doesn't have its dimensions set), then it sizes to its resource. In this example, if someimg.jpg is 70px wide by 30px high, the view will also be 70px wide by 30px high:

<view resource="someimg.jpg"/>

In cases where the resource for a view is loaded at run-time (i.e. over HTTP), the view will be 0px wide by 0px high until the resource is loaded. This can cause unexpected behavior if other elements in your application are dependent on the size of the view in question. For example:

<canvas>
  <simplelayout axis="y" spacing="2"/>
  <view id="myImageView"/>
  <view width="100" height="20" bgcolor="yellow"/>
  <view width="100" height="20" bgcolor="red"/>
  <button x="10" y="110">Set Image Now!
    <handler name="onclick">
      myImageView.setSource("someimage.jpg");
      </handler>
  </button>
</canvas>

At init time, the yellow view will be positioned at (0, 0), because myImageView will have dimensions of 0px wide by 0px high. When the image is loaded (after the button gets clicked) myImageView will resize to accommodate the image, and display the image. Since myImageView and the other siblings are being acted on by <simplelayout>, they will all move down.

For more on the subject of assigning resources to views, see Chapter 18, Media Resources.

2.3. Scaling

Consider the following example, which illustrates the effect of scaling a view's coordinate system. On each click of the view, the value of the height attribute diminishes by 10.

First let's look at a simple example that uses text, because this makes the effect more striking. As you click on the red rectangle, it shrinks, along with the text inside. But the font size does not change. The text is smaller relative to the canvas and to the computer screen, but not to its parent view.

Example 26.13. Simple scaling wtih text

<canvas width="700" height="100">
      <font src="helmetb.ttf" name="helmet"/>
  <view stretches="both" height="100" bgcolor="red" 
           onclick="this.animate('height', -10, 1000 , true )">
    <attribute name="aspect" value="${unstretchedwidth/unstretchedheight}"/>
    <attribute name="width" value="${ height * aspect}"/>
    <text font="helmet" fontsize="22" resize="true">
      This is some text
    </text>
  </view>
</canvas>

Here we have modified the example to further illustrate the same point. Notice how the width of the green view, as reported by the debugger, does not change, even as the view visibly shrinks.

Example 26.14. Simple scaling with text and colored view

<canvas width="700" height="300" debug="true">
      <debug y="120"/>
      <font src="helmetb.ttf" name="helmet"/>
  <view stretches="both" height="100" bgcolor="red" 
           onclick="this.animate('height', -10, 1000 , true );
           Debug.write('greenview new width is', greenview.width)">
    <attribute name="aspect" value="${unstretchedwidth/unstretchedheight}"/>
    <attribute name="width" value="${ height * aspect}"/>
    <view name="greenview" height="100" width="80" bgcolor="green"/>
    <text font="helmet" fontsize="22" resize="true">
      This is some text
    </text>
  </view>
</canvas>

2.4. Offsets

Every view has an internal registration pointthat is at (0,0) by default, the upper left hand corner of the view. This point can be changed using the xoffset and yoffset attributes. In short, the offset attributes are applied to the registration point of the view. Now, the x and y of this view is actually the position of its registration point within the view's parent coordinate system. Here is a simple app that demonstrates this. See below for an explanation of what's going on in this program.

Example 26.15. Offsets and registration point

<canvas height="250">
    <view x="100" y="100"
          bgcolor="yellow" xoffset="20" yoffset="20"
          width="100" height="100" >
        <view x="${parent.xoffset}" 
              width="1" height="100%" bgcolor="black" />
        <view y="${parent.yoffset}" 
              width="100%" height="1" bgcolor="black" />
         <animatorgroup repeat="-1" >
            <animator attribute="xoffset" duration="1000" to="70" />
            <animator attribute="xoffset" duration="1000" to="20" />
         </animatorgroup>
         <animatorgroup repeat="-1" >
            <animator attribute="yoffset" duration="500" to="70" />
            <animator attribute="yoffset" duration="500" to="20" />
         </animatorgroup>
    </view>
</canvas>

The reason that the intersection point does not move is that the parent view (in this case the canvas) is placing the colored view at that point, and that point does not change. it is only the xoffset and yoffset that are changing, not the x and y of the colored view.

Views rotate around the registration point. By default, xoffset and yoffset values are both zero and the registration point is in the upper left corner of a each view. Offsets allow you to change the rotation point. In the example below, the pivot point of the red square is the origin is its upper left corner; the pivot point of the blue square is its center (where x and y are set to "20").

Example 26.16. Simple Offsets

<canvas height="100">
  <view x="60" y="60" height="40" width="40" bgcolor="red"
        onclick="this.rotate.doStart()">
    <animator name="rotate" attribute="rotation" to="45" duration="500"
              start="false" relative="true"/>
  </view>
  <view x="60" y="60" height="40" width="40" bgcolor="blue" xoffset="20" yoffset="20"
        onclick="this.rotate.doStart()">
    <animator name="rotate" attribute="rotation" to="45" duration="500" start="false"
              relative="true"/>
  </view>
</canvas>

Offsets allow the view to be placed relative to the point (xoffset, yoffset). (Otherwise the view is placed using the view's top left corner as a reference point.) If a view has an offset, it will affect how layouts position it relative to other views. See for example <constantboundslayout> .

3. Positioning of Views

There are a variety of ways to set the position of a view. You can explicitly set its x and y values using tags; you can constrain the x and y values to values of other attributes, including those contained in a dataset, you can use layouts which "let the system" determine a view's position, or you can change the position at runtime by using script.

  • Declaratively

    • Using absolute x and y values

    • Using constraints

    • Using path constraints

    • Using layouts

  • Programmatically

    • Using setAttribute()

    • Instantiating from script

3.1. Declarative positioning of views using absolute coordinates

You can position views using absolute x and y coordinates, and the view will be positioned relative to its parent's origin:

Example 26.17. absolute positioning

<canvas>
  <view bgcolor="yellow" width="300" height="300" x="15" y="20">
    <view bgcolor="red" width="20" height="20" x="25" y="75"/>

    <view bgcolor="blue" width="20" height="20" x="75" y="200"/>

    <view bgcolor="green" width="20" height="20" x="50" y="50"/>
  </view>
</canvas>

3.2. Declarative positioning of views using constraints

You can also use constraints to position views. In the example below, the red view's position is set absolutely, the blue view's x and y positions are constrained to a function of the red view's coordinates.

Example 26.18. Using constraints to position views

<canvas>
  <view bgcolor="yellow" width="300" height="300">
    <attribute name="someXValue" type="number" value="50"/>

    <view bgcolor="red" name="red" width="20" height="20" 
            x="$once{20 + 5}" y="$once{70 + 5}"/>

    <view bgcolor="blue" width="20" height="20" 
            x="${parent.red.y}" y="${parent.red.x}"/>

    <view bgcolor="green" width="20" height="20" 
            x="${parent.someXValue}" y="${this.x}"/>
  </view>
</canvas>

In the example above,

  • The red x and y coordinates are constrained to mathematical expressions (20+5 and 70+5 respectively).

  • The blue view's x and y coordinates are constrained to the red view's y and x coordinates respectively.

  • The green view's x and y coordinates are constrained to some arbitrary attribute value.

See Chapter 27, Constraints for more on constraint syntax.

3.3. Negative coordinate values

The x and y values can be set to negative values. In the example below, clicking on the square causes successive views to appear and to move left or right. The parent square (teal color) expands to contain the blue and black squares, but not to contain the red and silver squares, which have negative x and y values.

Example 26.19. Positive and negative offsets

<canvas height="250" bgcolor="gray">
  <view x="100" y="100" bgcolor="teal">
    <view bgcolor="red" width="20" height="20"
            onclick="this.animate('y' , -50 , 1000, true)"/>
    <view bgcolor="silver" width="20" height="20"
            onclick="this.animate('x' , -50 , 1000, true)"/>
    <view bgcolor="blue" width="20" height="20"
            onclick="this.animate('y' , 50 , 1000, true)"/>
    <view bgcolor="black" width="20" height="20"
            onclick="this.animate('x' , 50 , 1000, true)"/>
  </view>
</canvas>

4. View Hierarchy

Each view has a position in the view hierarchy, that is, the tree structure of objects in which each view has a parent, and, optionally, children and/or sibling views. In simple cases such as the following the relationship of parents and children is obvious from their position in the code.

Example 26.20. simple view hierarchy

<canvas height="100">
   <view height="100" width="100" name="grandparent" bgcolor="red">
      <view height="50" width="50" name="parent" bgcolor="green">
        <view height="30" width="30" name="child" bgcolor="blue"/>
      </view>
    </view>
</canvas>

However, things are usually more complicated, especially when views are created within classes. Views can be created or destroyed at runtime, and moreover a single line of code can cause the creation of an arbitrary number of views. In many cases there are, in effect, two hierarchies- the lexical hierarchy of views in the code, and the node hierarchy of objects that are created by the code.

LZX employs the terms parent and immediate parent to distinguish between a view's relationship in different hierarchies. These terms are explained in greater depth in later chapters, but here's a short preview.

4.1. Children and Parents

Example 26.21. Parent and Immediate Parent

<canvas height="100">
  <class name="mywindow"
        onmousedown="dragger.apply()" onmouseup="dragger.remove()"
        bgcolor="blue">
    <dragstate name="dragger"/>

    <attribute name="defaultplacement" value="mycontent" type="string"/>

    <view name="mycontent" x="10" y="10" bgcolor="white"
             width="${parent.width-20}" height="${parent.height-20}">
      1
    </view>
  </class>

  <mywindow id="w" width="100" height="100">
    <text>hello</text>
  </mywindow>
</canvas>

1 default placement view: instance or subclass children go here

The view that contains the text hello has a parent of w and an immediateparent of w.mycontent. Note that "mycontent" could be nested in other views and it would still work. We call this ordering of objects the "node hierarchy." The term "view hierarchy" is more intuitive, but "node hierarchy" is more correct. (Notice that in the above example, "dragger" is a node, not a view)

If you're an experienced javascript programmer you will have seen this pattern before. When you see "immediateparent" in JavaScript, think "container".

Similarly, if you've looked at XAML, you may have seen that XAML distinguishes between the logical tree and the visual tree. The logical tree corresponds to the hierarchy as represented in the source (what we have sometimes called the source hierarchy). The visual hierarchy corresponds to the hierarchy as rendered.

Given this class definition:

Example 26.22. Simple placement

<canvas height="60">
  <class name="myclass">
    <simplelayout/>
    <view name="content"/>
    <text>label</text>
  </class>
    
   <myclass><text placement="content">content1</text></myclass>
   <myclass><text placement="content">content2</text></myclass>
   <simplelayout axis="y"/>
</canvas>

this source fragment:

<myclass><text placement="content">content1</text></myclass>
<myclass><text placement="content">content2</text></myclass>

has the logical tree:

myclass (text "content1")
myclass (text "content2")

and the visual tree:

myclass (view (text "content1")) (text "label")
myclass (view (text "content2")) (text "label")

Thus parent refers to the parent in the logical tree. immediateparent (in JavaScript) and placement (in XML) refers to the parent in the visual tree. Placement is similar to, but not the same as immediateparent. You can think of them as meaning the same thing with different types, but just to be clear: placement is a string which means: "find the view in my parent's children (or any descendant) which has this name, and when I am created make me a child of that view." immediateparent is then a reference to that view.

Placement is an instruction from a view to its parent about where it belongs within the parent's internal hierarchy. By default, this is interpreted to mean: 'find the subview with this name', but the parent is free to interpret it however it wants.

These concepts are explained in greater detail, and with several more examples, in Chapter 33, Extending Classes.

5. Views and Nodes

In LZX, the fundamental objects are called nodes. Nodes are abstract entities and as such they have no visual representation. Views are the most common kind of node, and in most cases you when you need a container object, you should use a view even though a node would work. In fact, although you can use <node> s for most of the abstract infrastructure (dataset manipulation, content management, polling, etc.), the compiler complains when putting datapointers and datasets in nodes. Nodes are only created through script; there is no tag for creating them.

As discussed in later chapters, in situations when you are trying to squeeze out the last bit of performance, you may want to examine how much overhead there is in using a view versus a node (when there's no presentation associated with the element).

6. How views are built

Sometimes it's important to understand the sequence that takes place when views are created. This is explained in depth in Appendix A, Understanding Instantiation, but here's a brief summary:

The evaluation of a view occurs in three phases:

  1. Construction: The object representing the view is created, and attributes with constant initial values are filled in.

  2. Instantiation: The attributes with dynamic initial values are filled in (which includes the construction and instantiation of any child views), the initialize() method is executed, and the onconstruct event is sent.

  3. Initialization: The init() method is executed and the oninit event is sent.

Note that construction and instantiation occur sequentially, but that initialization may be arbitrarily delayed, depending on the value of the initstage attribute. Attributes with dynamic initial values may not depend on other attributes with dynamic initial values, nor on the initialize() or init() methods having been run.

6.1. Adding subviews

When an view is built, subviews[] is initialized to an empty array. (In releases before OpenLaszlo 3.0 it was null.)

addSubview() is like an event handler; it's a protected method of view that is called when a view is added to its parent. You can't call it directly, but you can override it. However, in most cases you will want to use the onaddsubview event instead.

7. Distinguishing features of OpenLaszlo's Coordinate System

If you have experience doing graphics programming, you may be interested in the following paragraph. Otherwise you can safely skip this topic.

The fundamental properties of a view are:

  • stretches —which says whether to scale or not and in which dimensions

  • unstretched{height,width}—which are the original's dimensions (and can be used to implement a scale factor)

  • {height,width}—which will clip or scale according to the setting of `stretches`

The OpenLaszlo system is equivalent to the Flash Player's, but syntactically OpenLaszlo is closer to HTML/CSS standard (and would be if stretches defaulted to `both` instead of `none`).

Other systems define only a translation, or only a translation and scale, or allow an arbitrary 3x2 or 3x3 transformation. OpenLaszlo is distinctive both in not defining a translation ('x' and 'y' position the bounds of the view within its parent as well as defining a translation that precedes the rotate/scale, so it can't be used for this), and in representing the matrix in terms of its ops (which leaves some gaps, such as skew — but is much better for animation, which requires a much heavier-duty symbolic mechanism in SVG).

The fact that resources aren't child views leads to some tension in the system, where designers expect them to be view-like, and there's a pull to make them view-like by adding rotation or translation to them that's independent of the view that embeds them, and to make it possible to retrieve their bounds. (This is largely independent from the fact that they're represented as ids instead of objects.)