Loading...
Searching...
No Matches
Introduction to the Layout API

Haiku's Layout API is centered around the BLayoutItem and BLayout classes. The BLayoutItem class represents thing that can be managed by a BLayout, which is itself a BLayoutItem. Before we go any further, it is a good idea to familiarize yourself with the different BLayout classes available in Haiku:

You'll notice that BSplitView is not actually a BLayout, but a BView. The BSplitView class uses a custom BLayout behind the scenes, but because it must also be able to draw, a BView is required. Other BLayout objects have BView objects that can be used for convenience.

Although it is not necessary to use these classes to make use of the corresponding layouts, it does make things easier.

Once you have an understanding of what each BLayout does, you can start designing an interface with them. Let's consider a very simple window, with a single item in the center. For this, any of the layouts mentioned above would work, but we'll use a BGroupLayout, because it suits this purpose the best.

The BGroupLayout constructor is:

BGroupLayout(orientation orientation, float spacing = B_USE_DEFAULT_SPACING)
orientation
Definition: InterfaceDefs.h:193
The BGroupLayout class is a simple BLayout subclass that arranges the items it holds within a vertica...
Definition: GroupLayout.h:10

Because we only have one item in this layout, orientation and spacing become irrelevant. Let's choose B_VERTICAL for orientation, and leave spacing at its default.

BWindow* window = MakeWindow();
window->SetLayout(group);
@ B_VERTICAL
Definition: InterfaceDefs.h:195
Window base class.
Definition: Window.h:93
virtual void SetLayout(BLayout *layout)
Sets the layout of the window.

Before we can add anything to our layout, we must attach it to something, and here we've used the BWindow::SetLayout() method to accomplish that. By doing this, window takes ownership of group, so there is no need to manually delete group when we're done with it.

Now that we've got our BGroupLayout in place, we can start adding things to it, so let's add a BStringView.

group->AddView(MakeStringView("Haiku rocks!"));
virtual BLayoutItem * AddView(BView *child)
Adds child to this layout as the last item. In a vertical BGroupLayout, child will be on the right,...

Now we've got a BWindow with a horizontal BGroupLayout holding a single BView. However, if we want to ensure that our BStringView is always centered in the window, we should give it an explicit BAlignment. So the last line becomes:

BLayoutItem* stringView = group->AddView(MakeStringView("Haiku rocks!"));
stringView->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
B_ALIGN_VERTICAL_CENTER);
Undocumented class.
Definition: Alignment.h:10
Abstract class representing things that are positionable and resizable by objects of the BLayout clas...
Definition: LayoutItem.h:19
virtual void SetExplicitAlignment(BAlignment alignment)=0
Set this item's explicit alignment, to be used in Alignment().

Now our BStringView will always be right in the middle of the space allotted to it, which at the moment is the whole of window.

Now let's add a BMenuBar:

group->AddView(0, MakeMenuBar());
group->SetInsets(0, 0, 0, 0);
void SetInsets(float left, float top, float right, float bottom)
Set the insets for this layout.

Because we want our BMenuBar to appear at the very top of the window, we have to insert it at index 0, above the BStringView we added earlier. We also use BTwoDimensionalLayout::SetInsets() to make sure that our BMenuBar is flush to the edges of window. We also want a bit of space between our BMenuBar and our BStringView, but group's spacing has already been set by the BGroupLayout constructor, so we don't need to do that.

Now that we've put our BGroupLayout to good use, we can rest easy, assured that GUI will always look nice, no matter what font is used, or how big or little window is stretched. Of course, very few interfaces are as simple as this one.

The layout classes can deal with complex layouts. Suppose, for example, that we wanted to add a grid of BButtons under our BStringView. We could use a BGridLayout for this. The BGridLayout constructor is:

BGridLayout(float horizontal = B_USE_DEFAULT_SPACING,
float vertical = B_USE_DEFAULT_SPACING);
The BGridLayout class a BLayout subclass that arranges the items it holds in a grid.
Definition: GridLayout.h:12

Because we want a bit of breathing room between our buttons, we'll leave vertical and horizontal spacing as is.

BGridLayout* grid = new BGridLayout();
group->AddItem(grid);
virtual bool AddItem(BLayoutItem *item)
Adds item to this layout as the last item. In a vertical BGroupLayout, item will be on the right,...

You'll notice that we've added grid directly to group. This means that any BView objects we add to grid will become children of window, but will be positioned by grid.

grid->AddView(MakeSmallButton(), 0, 0);
grid->AddView(MakeSmallButton(), 1, 0);
grid->AddView(MakeBigButton(), 0, 1, 2, 1);
grid->AddView(MakeSmallButton(), 1, 2);
virtual BLayoutItem * AddView(BView *child)
Adds child to this layout in the first empty cell available, or in a new column in the first row if t...

Now we've got a nice grid of BButton objects, let's go over it quickly:

  • grid has two columns and three rows.
  • The cells (0, 0), (1, 0), and (1, 2) hold small buttons
  • The cells (0, 1) and (1, 1) hold a single button that spans both cells.
  • The cell (0, 2) is empty.

One of the features you'll find incredibly handy in the layout API is the builders in LayoutBuilder.h. Here's how our whole layout would look if it were done with these builders:

.SetInsets(0, 0, 0, 0)
.Add(MakeMenuBar())
.Add(MakeStringView("Haiku rocks!"))
.Add(MakeSmallButton(), 0, 0)
.Add(MakeSmallButton(), 1, 0)
.Add(MakeBigButton(), 0, 1, 2, 1)
.Add(MakeSmallButton(), 1, 2);
ThisBuilder & Add(BView *view, int32 column, int32 row, int32 columnCount=1, int32 rowCount=1)
Add a BView to the BGridLayout this builder represents.
Definition: LayoutBuilder.h:868
BLayoutBuilder::Base subclass for building BGroupLayouts.
Definition: LayoutBuilder.h:53
ThisBuilder & Add(BView *view)
Add a BView to the BGroupLayout this builder represents.
Definition: LayoutBuilder.h:530
ThisBuilder & SetInsets(float left, float top, float right, float bottom)
Definition: LayoutBuilder.h:712
GridBuilder AddGrid(float horizontal=B_USE_DEFAULT_SPACING, float vertical=B_USE_DEFAULT_SPACING, float weight=1.0f)
Add a new viewless BGridLayout as a child to the layout that this builder represents and return a Gri...
Definition: LayoutBuilder.h:600

This is only one way that you could build this layout, but it is probably the most succinct. Functionally, this is equivalent to all the previous code in this introduction.

Special Handling for BBox
BBox is a "container" view that can contain other views. The use of the layout manager within an instance of BBox is a special case. Code such as is shown below is necessary to automatically layout views within a BBox.
BBox *box = new BBox("box-example");
.Add(button1)
.Add(button2);
box->AddChild(boxLayout->View());
@ B_HORIZONTAL
Definition: InterfaceDefs.h:194
A rectangular view with a border and an optional label to group related subviews visually.
Definition: Box.h:12
virtual BView * View()
Returns the same BView* as BLayout::Owner(), this method is inherited from BLayoutItem.
void AddChild(BView *child, BView *before=NULL)
Adds child to the view hierarchy immediately before before.