## Angry Red Planet # Layout Library |

### Yet Another Layout Library...

This layout library was started back in the days of DR8. It didn't change for a long time after that first prototype because, until R4, manually resizing BViews has been too slow to (IMHO) make a layout library based on them practical. However, now with the coming of R4 and its new Begin/EndViewTransaction() mechanism -- as well as the fact that there still doesn't seem to be a layout library that completely meets our needs -- I have cleaned up the code so that it is semi-usable (at least for my needs), and decided to release what I have as open source.

The main features/goals of this library are:

- Integrate well with the rest of the BeOS system. This means it should use the existing Be-supplied controls wherever appropriate, with as little modification to them as possible. It also should make use of other existing OS services (such as the BMessage) instead of implementing its own.

- Be light-weight and simple. Related to the first requirement, the library should introduce as few new concepts as possible and require little memory and code in addition to the existing Be classes.

- Distributed as Open Source. So that someone using the library does not become dependent on the person who created the source.

- Automatically conform to any font size(s) and automatically resize layout to conform to its exterior dimensions. Which goes without saying, I suppose. More pointedly, I don't want to have to think about where controls should be placed in terms of pixels, "dialog units", or anything like that. I don't care. I just want to generally specify their positions relative to each other, and let the program take care of the pixel location details.

- A flexible and well defined layout architecture. This means to provide the equivalent of the traditional concept of "layout managers." Organize a view hierarchy as user interface controls that are placed inside parent containers, where the latter actually determine where to place the basic controls on the screen. (But hopefully we can learn something from X and other implementations of this, and do a better job at it...)

- Include the ability to embed a layout hierarchy in a larger BView hierarchy. In other words, the layout library should not depend on having any control over the surrounding window. You should be able to create a layout of controls and pass this as a root BView to some other containing object that knows nothing about the layout library being used.

Be sure to look at the source code in `ArpTest/ArpLayout` to get a feel for one (possible) way to use the library!

# Overview

The ArpLayout library implements a traditional "layout manager / control" GUI architecture. That is, there are two fairly distinct types of classes:

Controls

  These are the parts of the interface that the user interacts with. A control always a BView, because it needs to see user events such as the keyboard and mouse to provide something the user can interact with.

  In addition to all of the functionality of a BView, a control must be able to report accurate information about its dimensions based on its current font and other parts of the environment. This is similar to the existing BView::GetPreferredSize() method, but includes a bit more information and must return a useful value, whatever the object's current state.

  (Unfortunately, the GetPreferredSize() implemented in a lot of the Be-supplied classes is either touchy about returning correct information or doesn't return anything useful at all.)

Layout Managers

  These are the objects used to construct an actual layout hierarchy. A layout manager contains one or more child objects, and implements a specific algorithm to determine where the children should be placed within the manager's screen space (i.e. its Bounds() rectangle) and how big they should be. Ideally, the library architecture should allow a layout manager class to implement any arbitrary layout algorithm it likes.

  The children of a layout manager may be either basic controls or other layout managers. Building a tree of layout managers and controls defines an explicit relationship between the various user interface objects, so that they will automatically be correctly positioned based on the font(s) being used and the size of the containing area.

  A layout manager is often not associated with an actual BView -- it does not need to directly interact with the user, so there is no need to include all of that overhead. Two layout managers currently come with the ArpLayout library, ArpRunningBar and ArpScrollArea.

The core piece of the ArpLayout library is a class called ArpLayoutable. This is a simple base class (derived only from BArchivable) that defines the abstract interface between parent and child in the layout hierarchy. Its major pieces of functionality are:

The ability to query an object for its desired dimensions.
  A layout manager uses this capability of its children to determine how best to place them.
Managing the position and size of an object.
  A layout manager uses this capability of its children to actually put them where it wants them to go.
The ability to associate "constraints" with an object.
  These are used as parameters to the layout manager, which provides additional information about how to place the child that they are associated with.
The association of a BView with an ArpLayoutable.
  An ArpLayoutable object may not be associated with a BView if it doesn't need to interact with the user. There are various functions to manage this situation.

## The Layout Hierarchy

At the most fundamental level, ArpLayoutable provides a set of functions for managing a hierarchy of object. These directly match the ones found in BView:

> AddLayoutChild() | AddChild()
> RemoveLayoutChild() | RemoveChild()
> LayoutParent() | Parent()
> CountLayoutChildren() | CountChildren()
> LayoutChildAt() | ChildAt()
> NextLayoutSibling() | NextSibling()
> PreviousLayoutSibling() | PreviousSibling()
> LayoutRemoveSelf() | LayoutRemoveSelf()
> FindLayoutable() | FindView()

This is an important point: The ArpLayoutable class defines an additional hierarchy, on top of the existing BView hierarchy. This relationship is easy to derive -- the underlying BView hierarchy is simply the ArpLayoutable hierarchy with all of the objects that do not have an associated BView object removed.

Another way to think of this is as light-weight ArpLayoutable objects sitting "in-between" the full BViews, guiding them in the layout. For example, consider a simple BView hierarchy such as this:

>     BView Root -+- BMenuBar
>                 +- BTextView
>                 +- BBox ------+- BButton
>                               +- BButton
>                               +- BButton

This may be embedded within a larger ArpLayoutable hierarchy that runs like this:

>     View/Layout Root - VerticalGroup -+- Wrap - BMenuBar
>                                       +- Wrap - BTextView
>                                       +- Wrap - BBox --- HorizontalGroup -+- Wrap - BButton
>                                                                           +- Wrap - BButton
>                                                                           +- Wrap - BButton

Which would probably result in a window that looks something like this:

>      _____________
>     /Layout Window\_____________________________
>     |                                           |
>     | BMenuBar                                  |
>     |-------------------------------------------|
>     |                                           |
>     |                                           |
>     |                 BTextView                 |
>     |                                           |
>     |                                           |
>     |-------------------------------------------|
>     |                                           |
>     | /--- BBox ------------------------------\ |
>     | |                                       | |
>     | | +---------+  +---------+  +---------+ | |
>     | | | BButton |  | BButton |  | BButton | | |
>     | | +---------+  +---------+  +---------+ | |
>     | |                                       | |
>     | \---------------------------------------/ |
>     |                                           |
>     |___________________________________________|

A few things to note:

- "View/Layout Root" is a class that combines a BView and an ArpLayoutable into a single object that represents the "root" of a layout hierarchy. You don't need to use this class, but every ArpLayoutable hierarchy must have some ArpLayoutable with an associated BView at its top level.
- "Wrap" indicates a place where we "wrap" a raw BView-based object into an ArpLayoutable object, so that it can be included in the layout hierarchy. While ArpLayoutable objects without BViews are allowed, BViews without ArpLayoutables are not -- they implicitly end the layout hierarchy. (Note that this does -not- say you can not attach raw BView objects to other BViews in the hierarchy. Simply that any such objects obviously cannot directly participate in the layout operation.)

## Wrapping BViews

Wrapping is an important operation. It is accomplished with the ArpViewWrapper class, which is-a ArpLayoutable that knows how to do useful things with a raw BView. In particular, it can massage the BView in such a way that a number of the Be-supplied controls will return useful information from GetPreferredSize(), and then fill this information into the ArpLayoutable dimensions. It also uses special functions of ArpLayoutable to mark its object as one that owns a BView, so that the ArpLayoutable will do the correct thing with coordinate spaces and other details.

The ArpViewWrapper can be used in one of two ways. The simplest is to instantiate it, passing in an instance of the BView it is to wrap in its constructor. This will magically include the BView in the layout hierarchy (assuming it reports a useful preferred size). Alternatively, one can create a subclass that is a mix-in of the ArpViewWrapper and BView-based object it is associated with. This method is used in places where special code must be written to report size information about the BView or otherwise modify its default behavior.

## Constraints

In order to perform any kind of complicated layout operation, a layout manager will need to associate some special information with its children. This is additional information attached to a specific child, which is used to control how it is placed in the container's area. Examples of constraints are such things as a "weight", which controls how much of the parent's space is given to one child in relation to its others, or "gravity", which determines how a child will be placed in a larger area that it does not need.

Constraints are a difficult issue because they have two conflicting needs: the child must own the constraints, but only the parent knows what they are.

ArpLayout solves this problem by implementing constraints as a BMessage. Every ArpLayoutable includes a BMessage that contains its constraints. The ArpLayoutable object owns this BMessage object as part of its data, but doesn't touch it: instead, there is an interface for its parent to retrieve it and examine or modify it as needed.

## Parameters and Globals

The ArpLayoutable class includes an additional capability, the management of "parameters" associated with a single object and "globals" associated with an entire layout hierarchy.

Note: this is NOT an intrinsic part of the layout architecture. In fact, I am tempted to remove it -- it adds a lot of additional complexity to the library that could just be dropped and, as a first prototype of an idea, I am not sure that I am happy with how it turned out. (I am particularily not satisfied with how poorly the Be-supplied controls react to the dynamic environment I was trying to create, that is another story...)

On the other hand, this does make it quite a bit easier to write an example program that shows off how the ArpLayout library works. So for now, I am leaving it in... but I am not going to include any more real documentation on it. The ArpLayoutable class header includes fairly extensive comments on how these two things work, if you are interested.

# Classes

## ArpLayoutable (header file)

This is the base class for the layout architecture, defining the interaction between objects in a hierarchy. It is not intended to be directly instantiated; instead, you will want to use one of the pre-built subclasses, or make your own to implement either a layout manager or, combined with a BView, a control.

The ArpLayoutable header file is extensively commented, so that is currently the place to go for information on this class.

## ArpLView (header file)

ArpLView is-a mix-in of a BView and an ArpViewWrapper. Essentially, it is the moral equivalent of BView, but for classes that want to implement controls that automatically work with the ArpLayout architecture. When implementing a subclass, you should implement all of the normal BView stuff (except GetPreferredSize()), as well as the ArpLayoutable methods you need to override. In particular, you should always override ComputeDimens() to return the appropriate dimensions of your object. Layout managers also must override Layout().

## ArpRootLayout (header file)

ArpRootLayout is-a ArpLView that is specialized to be the root of a layout hierarchy. There are four important parts to this:

- The object marks itself as the root (by overriding IsLayoutRoot()), so that the underlying ArpLayoutable knows that its own dimensions are not to be touched -- the ArpRootLayout is under the direct control of its parent BView.
- ArpRootLayout intercepts FrameResized() events so that its underlying layout hierarchy will be forced to conform to its size.
- The class includes a BMessage containing the global parameter values for its underlying layout hierarchy, and supplies functions for retrieving and manipulating them.
- If the ArpRootLayout is out the top level of a BView tree (that is, its direct parent is a window), it can help integrate that window with the layout hierarchy by automatically setting its size limits.

An ArpRootLayout can only contain one child. This is typically a manager widget that knows how to place multiple children in its area.

## ArpRunningBar (header file)

This is the only general-purpose manager widget that is currently included with the ArpLayout library. It simply places all of its children next to each other, in either a horizontal or vertical direction. When the running bar has extra space that goes beyond the childrens' preferred size, it will distribute this amongst them according to a weighting factor: each child is given a single float weight; the amount of space it gets is directly proportional to its weight relative to its other children. If a child's weight is zero, its size is never grown beyond its preferred size.

For example, consider three children with weights (0, 1, and 2). The running bar reports it preferred size to be that of the sum of its children sizes, so that it is always given enough space to contain them. If it is made larger than that, it will distribute any extra space beyond that summed preferred size based on their weights. Say we add 60 pixels to its size. The additional space assigned the children is, in order, (0, 20, 40).

## ArpScrollArea (header file)

ArpScrollArea is a specialized layout manager for a child that will be placed within a larger scrollable area. It is a complete reimplementation of the BScrollArea, because BScrollArea does not work at all well with the ArpLayout library.

This layout manager can only have one child, which is the view to scroll around in. The child ArpLayoutable must have an associated BView object for it to attach to. This class uses normal BScrollBar objects as the controls around the scrollable view, and these require the BView for something to hold on to.

## ArpViewWrapper (header file)

This is a simple subclass of ArpLayoutable that allows you to "wrap up" a raw BView-based object into an ArpLayoutable that the layout library can manipulate. The resulting combination (either as one instance holding another, or a mix-in of the two classes) can then be seamlessly used within a layout hierarchy.

This class also implements some special magic to try to get usable preferred size dimensions from Be's supplied control classes. However, very few of them actually work. The ones that I do know work (and hence don't require subclassing) are:

- BButton (but not if you dynamically change its font size)
- BRadioBox
- BColorControl

## ViewStubs (header file)

This is actually a file that contains a collection of stub classes to fix problems that keep some Be-supplied controls from working with the ArpLayout library. In other words, it does what we'd really like to avoid: subclasses Be objects with thin wrappers.

The following classes are included (their corresponding Be class should be easy to figure out):

- ArpBox
- ArpButton
- ArpListView
- ArpOutlineListView
- ArpMenuBar
- ArpMenuField
- ArpTextControl

A number of others will have to be created (I know, for example, that BStringView doesn't work by itself), but these are the only ones I have needed so far.

# Copyright

All files and code contained in this package are Copyright ©1998 Angry Red Planet, except where otherwise noted.

The files contained here-in are distrubuted under a modifed form of the Artistic License. See the file `@License.html` or `@License.txt` for a copy of this license.