In this article we'll finish off our toy UI library by adding a method to destroy elements.

We're going to make a ElementDestroy function which will mark an element for deletion. Then we're going to actually delete the element in _Update. The reason for this is that we don't want to deallocate the element while we're still dealing with it. The call to ElementDestroy could happen at any point while processing an input message. We could be several calls stacks deep into working with the element we're about to destroy! Delaying the actual destruction and deallocation code to _Update works well for our purposes.

Careful readers might be wondering why we defer element destruction and rendering to _Update but not layout. In fact, there is no good reason for this. It is just a quirk of how I designed this particular toy UI library. Deferring layout to _Update would probably be a good idea, as just like with rendering we'd rather not repeat the calculations more than necessary, and queueing up all the things in the hierarchy that needs relayouting and then only layouting only those would be more efficient. Luckily layout is pretty quick so it won't matter for our purposes here. You could also make it so that elements are able to queue additional post-layout tasks to _Update. For example, with a wrapped tile view you can't scroll a specific item into view unless you know the dimensions of the view element. This is complicated though, and very much out of scope for this tutorial.

When the element is actually destroyed we will send it a new message: MSG_DESTROY. You could also add another message that is sent at the time of the call to ElementDestroy. Deallocating things safely and in the right order can get pretty complicated with some elements. But again, this is all out of scope here. Let's implement the MSG_DESTROY message for our existing elements.

For buttons and labels, we just need to free the text buffer we allocated:

// ...
if (message == MSG_DESTROY) {
	free(label->text);
}
// ...
if (message == MSG_DESTROY) {
	free(button->text);
}
// ...

For windows, the destruction code is platform layer specific, so I won't go through it. For panels, there's nothing to do, so we won't handle the message.

Let's write ElementDestroy. We'll need to mark the element with something to indicate it needs to be destroyed, so let's introduce the flag #define ELEMENT_DESTROY (1 << 30). We'll also need to mark all its ancestors with a flag so that the element can be found in the hierarchy during _Update, so we introduce the flag #define ELEMENT_DESTROY_DESCENDENT (1 << 31). The implementation of ElementDestroy then becomes fairly straightforward.

void ElementDestroy(Element *element) {
	// If the element is already marked for destruction, there's nothing to do.
	if (element->flags & ELEMENT_DESTROY) {
		return;
	}

	// Set the flag indicating the element needs to be destroyed in the next `_Update`,
	// that is, once the input event is finished processing.
	element->flags |= ELEMENT_DESTROY;

	// Mark the ancestors of this element with a flag so the we can find 
	// this element in `_Update` when traversing the hierarchy.
	Element *ancestor = element->parent;
	while (ancestor) {
		ancestor->flags |= ELEMENT_DESTROY_DESCENDENT;
		ancestor = ancestor->parent;
	}

	// Recurse to destroy all the descendents.
	for (uintptr_t i = 0; i < element->childCount; i++) {
		ElementDestroy(element->children[i]);
	}
}

There's a few places we need to update to check for the ELEMENT_DESTROY flag that could otherwise get confused between the call to ElementDestroy and the actual removal of the element in _Update. First of all, the loops in _PanelMeasure and _PanelLayout should skip past such elements.

int _PanelMeasure(Panel *panel) {
	// ...
	for (uintptr_t i = 0; i < panel->e.childCount; i++) {
		if (panel->e.children[i]->flags & ELEMENT_DESTROY) continue; // NEW!
		// ...
	}
	// ...
}
// ...
int _PanelLayout(Panel *panel, Rectangle bounds, bool measure) {
	// ...
	for (uintptr_t i = 0; i < panel->e.childCount; i++) {
		if (panel->e.children[i]->flags & ELEMENT_DESTROY) continue;
		// ...
	}
	// ...
	for (uintptr_t i = 0; i < panel->e.childCount; i++) {
		Element *child = panel->e.children[i];
		if (child->flags & ELEMENT_DESTROY) continue;
		// ...
	}
	// ...
}

We also don't allow elements marked for destruction to receive messages other than MSG_DESTROY (which will be sent during _Update).

int ElementMessage(Element *element, Message message, int di, void *dp) {
	if (message != MSG_DESTROY && (element->flags & ELEMENT_DESTROY)) {
		return 0;
	}
	// ...
}

Okay, we're ready to actually implementing destroying elements. We add the bool _ElementDestroyNow(Element *element) function. If the element has the ELEMENT_DESTROY_DESCENDENT flag set, then it recurses into its children; the flag is also cleared. Then, if the element has the ELEMENT_DESTROY flag set it will be destroyed and true is returned; otherwise, false is returned. The caller of _ElementDestroyNow can use this return value to tell when it should remove the element from its list of elements.

bool _ElementDestroyNow(Element *element) {
	// Is there some descendent of this element that needs to be destroyed?
	if (element->flags & ELEMENT_DESTROY_DESCENDENT) {
		// Clear the flag, ready for the next update cycle.
		element->flags &= ~ELEMENT_DESTROY_DESCENDENT;

		// For each child,
		for (uintptr_t i = 0; i < element->childCount; i++) { // Exercise to the reader: how can this specific line of code be optimized?
			// Recurse. Was this specific element destroyed?
			if (_ElementDestroyNow(element->children[i])) {
				// Yes, so remove it from our list of children.
				memmove(&element->children[i], &element->children[i + 1], sizeof(Element *) * (element->childCount - i - 1));
				element->childCount--;
				i--;
			}
		}
	}

	// Does this element need to be destroyed?
	if (element->flags & ELEMENT_DESTROY) {
		// Sent it the MSG_DESTROY message.
		ElementMessage(element, MSG_DESTROY, 0, 0);

		// If this element is being pressed, clear the pressed field in the Window.
		if (element->window->pressed == element) {
			_WindowSetPressed(element->window, NULL, 0);
		}

		// If this element is being hovered, reset the hovered field in the Window to the default.
		if (element->window->hovered == element) {
			element->window->hovered = &element->window->e;
		}

		// Free the element's children list, and the element structure itself.
		free(element->children);
		free(element);
		return true;
	} else {
		// The element was not destroyed.
		return false;
	}
}

All that remains is to actually call this function in _Update.

void _Update() {
	for (uintptr_t i = 0; i < global.windowCount; i++) {
		Window *window = global.windows[i];

		if (_ElementDestroyNow(&window->e)) {
			// The whole window has been destroyed, so removed it from our list.
			global.windows[i] = global.windows[global.windowCount - 1];
			global.windowCount--, i--;
		} else if (RectangleValid(window->updateRegion)) {
			// ...
		}
	}
}

And that's it! We have a toy UI library =) The test usage code for this article gives a simple demonstration of destroying elements working.

A window with 4 buttons in it, labelled 1, 2, 3 and 5. The 4th button was destroyed.

That said, we're not finished yet! The tutorial series will continue as I detail my favourite strategy for implementing undo, redo, saving and loading in an application, while keeping the UI in sync with everything. Hopefully it will be as interesting as I think it is ;)

part12.c

Part 11: Layout panels.

Part 13: Introduction to state synchronization.