You are on the correct path.
All GUIs are constructed from Primitives (pixel, arc, line, fill, text). And you create layers that you can build on.
By layers I very simply mean, you use 'line()' to create rectangles. rectangles to create windows and buttons, etc. Fill to add color. Text to add titles, and so on.
I mean, all GUIs have basically the same requirements-- graphics ports,windows, menus, controls, events, io.
You need a graphics manager which will manage your pixmaps/bitmaps. The desktop is the master pixmap or root graphics port.
Each window has a grafport (so it has a bitmap to draw itself into). This also allows it to draw into itself when it's not visible (this is important).
int h; /* horizontal coord */
int v; /* vertical coord */
Point topLeft; /* top, left coord */
Point botRight; /* bottom, right coord */
unsigned char *pixmap; /* pixmap/bitmap ram */
Rect bbox; /* logical bound box, global coords */
int rowBytes; /* Physical width of port in bytes */
int depth; /* color depth */
int *clut; /* Color Lookup Table */
You need a way to manage a linked list for your window list, and structures for each of your window types. I would recommend using a function pointer in your window structure for your window definition function.
The window record contains a pointer to the grafport that was allocated when the window was allocated. It also has fields that can contain lists to controls for the window, the window's title string text, sub records for attributes about the window (local .v. global coords, dimensions, colors, etc.) It also has fields that can contain pointers to scratch ram used for updates and clipping regions.
You will need to read the mouse and keyboard so you can create events for key ups, key downs, mouse ups, mouse downs, etc. If a user presses the mouse, you capture time of the click, the position of the click (global coords), and put that into a circular queue of events that the app reads events out of.
If the app sees a hit (mouseDown event), it can check the timestamp of the last mouse event to see if a double-click occurred. Other than that, it can make a call to your api that will tell it what window the hit occured in (convert the local window coords of each window to global and compare with the mouse hit coords). If it occurred, you know which window was hit, and you can then call a similar API routine (that you write) to tell which control, if any, got hit.
See how layerish it is?
It's a lot of fun.
Another example of the layers-- what is a menue? It's just a little window with a special definition. It checks for a hit and sees what item the hit occurred in and returns that as an event you can check for.
Hopefully, this will give you some direction.