It seems it's been a little while since I made this tutorial series. Let's review it and see if I've changed my mind about anything.

Part 1: Having the global state here is a bit unnecessary. This GlobalState structure could be passed by pointer to WindowCreate, Initialise and MessageLoop, and then we wouldn't need a global. But does it matter? For most platform layers, you'll only be able to have one such state in a process anyway. For example, look at Windows's Initialise; it registers a window class with a fixed name. At the very least though, it should be marked as static, as should all the internal functions.

Part 2: This is all platform code so there's little to say. I note that in the X11 code, you don't need to call XCreateImage and XDestroyImage. You can simply fill this structure out on the stack.

Part 3: Having a public flags field for all elements is questionable. It gets used in this tutorial for three separate things: (1) common flags, e.g. ELEMENT_V_FILL; (2) element class specific flags, e.g. LABEL_CENTER; and (3) internal state, e.g. ELEMENT_DESTROY. As I'll cover later in this review, use case (1) isn't necessary. This leaves (2) and (3), which should just be separate fields. And in the case of (2), elements that don't need it shouldn't have it.

Part 4: The function signature int (*)(Element *, Message, int, void *) for message handlers is cute. I still think it's the best you can do in C for this sort of problem. But for other languages, please don't make the user crack messages themselves, and don't make them manually cast the Element pointer or Element.cp. The messageClass and messageUser distinction is again cute, but I'm a bit sceptical of it now. It lets the user intercept messages they have no right to. For example, what does it mean to override the MSG_LEFT_DOWN of a button? It's not a good idea for a library to expose this. The solution is to have a separate interface per element that exposes a precise set of messages meaningful for its customization and hooking its events, and then leave it to the messageClass implementation to call it. You'll always want to forward MSG_DESTROY and keyboard focus messages though. Despite all that, defining multiple interfaces like this remains a pain in C. So maybe I wouldn't bother.

Part 5: To be honest, not supporting overlapping sibling elements isn't a big deal. I very rarely need it. ElementMove should let the parent influence the calculation of the child's clip. Typically, you don't want clipping, but when you do (e.g. scrolling) you want precise control of it. Don't forget to modify _ElementPaint and ElementFindByPoint if you allow elements not clipped to their parents. Finally, I urge you to be very careful about UI scaling in your code. This tutorial doesn't cover it, but if you want to do it, make sure all variables storing sizes are clearly documented as being in pixels or scaled units.

Parts 6 and 7: Not supporting "alpha" was an unintentionally good decision. I did it for simplicity's sake in the tutorial, but you don't want alpha in a UI library. You can't guarantee color contrast and it wreaks havoc with gamma stuff. Always use a fixed palette of opaque colors. Furthermore, I suggest not passing colors around as uint32_ts where possible. It'll be annoying if you want to support color spaces like P3 later.

Parts 8 and 9: Mouse input events should "bubble up" the element hierarchy, and elements should be able to consume the events. For example, if you have a label element inside a button element (which we don't do in this tutorial, but you might want this), then even if the mouse hovers over the label the button is still notified of mouse events. And events should keep bubbling even after they're consumed; just make it a rule to always check if the event has been consumed. This way ancestor elements still get notified of the event and respond to it, even if a descendant has consumed it. Consider bringing a tool panel to the foreground if a click happens anywhere inside it.

Part 10: This code is mostly fine. Concerning "proper styling", I simply advise you to not bother trying. Make one button that looks good, and tell any user that wants to customize it that you don't care. With regards to text labels, eventually you'll want rich text. I have lots of ideas on how to do this, but sadly none of them'll work in C. Furthermore, the label element shouldn't use a fixed text color. It should ask its closest opaque ancestor what text color to use, ensuring sufficient contrast with the background on which it is drawn. With rich text you may even want to be able to query the ancestor for a whole palette of usable colors, each with a different semantic purpose.

Part 11: This algorithm works well for 99% of UI; I still recommend it. There's probably an edge case or two not properly handled by the code I wrote here, but I can't be bothered to review it. I do however want to address how the element hierarchy is built up. When creating any element, you pass it a pointer to the parent element. You also pass some common flags, like ELEMENT_V_FILL. But this flag only makes sense if the parent is a Panel! It's ignored for the element added to the Window. There's also nothing to stop you from adding more than one element to a window, even though it only supports one. And there's nothing to stop you from adding elements to a button or label, even though they don't support any at all. And finally, there's no way to specify more per-child data relevant to the parent's layout algorithm than what will fit in 16 bits, the definitions of which are shared between all element types! For example, what if you wanted to specific a fixed height of an element? I think the best solution is to pass an ElementSlot when creating an element, and then write functions that return ElementSlots for elements which allow adding children. For example, you could add ElementSlot WindowGetRootSlot(Window *) and ElementSlot PanelAdd(Panel *, int width /* -1 = default, -2 = fill */, int height /* similar */). Or something like that.

Part 12: I don't have anything to say about destroying elements. This code works fine. Although, since we're modifying the _Update function anyway, I would like to mention that you should probably add another call to ElementFindByPoint to update the hovered element before painting. That way, even if the layout shifts around through processing the input event, the hovered element is always correct at time of painting. Unless changing the hovered element also affects the layout. But you shouldn't want to do that.

Parts 13 through 24: I still really like this method of storing application state. Trying to be any smarter than this will likely result in failure. Don't be afraid to customize it to match your application's specific needs. Some care is needed if you have multiple windows into the same document. Keep document state firmly separate from view state like "which objects are selected".

In the end, I'm still proud of this tutorial. I have nitpicks here and there, but the fundamental ideas still hold strong. It's surprising really, given how many years have passed. Thank you for reading!

I was going to put a "human content disclaimer" here to parody the "AI content disclaimers" you see popping up these days, but it seems I can't think of anything funny to say.

Part 24: Saving and loading.