In this article we're going to add layouting infrastructure.

To each element we associate two rectangular regions of the window, called its bounds and its clip.

typedef struct Element {
	uint32_t flags; 
	uint32_t childCount;
	Rectangle bounds, clip; // NEW!
	struct Element *parent;
	struct Element **children;
	struct Window *window;
	void *cp;
	MessageHandler messageClass, messageUser;
} Element;

The bounds of an element indicate where it exists in the window. The rectangle's coordinates are relative to the top-left corner of the drawable area of the window (the 'client area').

A picture of an example window showing the bounds of a button.

Sometimes though the bounds of the element do not tell the full story. Imagine a button that has been scrolled so that it is halfway off the top of the scroll container. The button needs to be clipped so that only the bottom half is drawn. This is what the clip field in the Element header stores: the subrectangle of the element's bounds that is actually visible and interactable.

Similar to our discussion about the window field in the Element header, the clip rectangle could be computed on the fly any time it's needed (as the intersection of the bounds of the element and all its ancestors). However, it's probably a better idea to keep the value around, since we'll need to use it often.

We won't actually get around to things like scrolling in this tutorial, however we may get elements that go off the edge of the window that still need to be clipped correctly. And indeed they will be clipped because the clip rectangle will be intersected with the window's bounds, since the window is a common ancestor of all its elements.

We decree that sibling elements (that is, element with the same parent element) must not overlap. This will suffice for our purposes, and it simplifies painting and input handling. However it is common in real interfaces that elements may need to overlap, be it temporarily during the course of an animation, partially in the case of shadows, or permanently in the case of 'Z-stacks'.

Okay, good. We're making progress. But still the question remains of how and when do the bounds of an element get set? We prescribe the invariant that each element is responsible for the positioning of its children and nothing more. We additionally say that whenever the bounds of an element is changed, it must be sent a message to notify it the change has occurred. First, we add MSG_LAYOUT to the Message enumeration. The chain of events will look something like this:

And in this manner when the window is resized the hierarchy of elements is updated.

We create a little helper function to move an element. This automatically sets the bounds, clip, and sends the MSG_LAYOUT message only if needed.

void ElementMove(Element *element, Rectangle bounds, bool alwaysLayout) {
	Rectangle oldClip = element->clip;
	element->clip = RectangleIntersection(element->parent->clip, bounds);

	if (!RectangleEquals(element->bounds, bounds) 
			|| !RectangleEquals(element->clip, oldClip) 
			|| alwaysLayout) {
		element->bounds = bounds;
		ElementMessage(element, MSG_LAYOUT, 0, 0);
	}
}

We also have to decide what the window element should do in response to the MSG_LAYOUT message. I spent a lot of time trying to work out what the best thing to do here is, and ended up deciding that the window element should have exactly one child element, which will take up the full bounds of the window. This way, merely creating a window does not make any assumptionns about how the layout will start. It is up to the user (of the library) to attach some element to the window that will provide the layout they want. This will probably make more sense once we've talked about layout panels, later on in the tutorial.

Let's add this behaviour to the window element. In its message handler we simply put:

if (message == MSG_LAYOUT && element->childCount) {
	// Move the first child to fill the bounds of the window.
	ElementMove(element->children[0], element->bounds, false);
}

Finally, we need to actually send the MSG_LAYOUT message to the window when the window is resized. In the correct spot for each platform layer, we add:

window->e.bounds = RectangleMake(0, window->width, 0, window->height);
window->e.clip = RectangleMake(0, window->width, 0, window->height);
ElementMessage(&window->e, MSG_LAYOUT, 0, 0);

Awesome! We've got the infrastructure for element layouting. The test usage code demonstrates it all working. Again, I advise you to step through the code in a debugger if you're perplexed.

part5.c

Part 4: Messaging infrastructure.

Part 6: Painting infrastructure.