In this article we're going to implement mouse motion input. We'll write 2 new functions, _WindowInputEvent and ElementFindByPoint.

The control flow will look something like this:

  1. The platform layer receives an input event from the operating system telling it that the mouse has been moved over one of our windows.
  2. The platform layer sets cursorX and cursorY in the Window structure with the new position of the cursor.
  3. It calls _WindowInputEvent with details of a MSG_MOUSE_MOVE message.
  4. _WindowInputEvent finds the element over which the mouse cursor is hovering using ElementFindByPoint
  5. _WindowInputEvent sends the MSG_MOUSE_MOVE message to this element.
  6. _WindowInputEvent calls _Update so that anything that was queued for repainting in step 5. will be repainted.

Let's start by implementing ElementFindByPoint. This function takes an element and a point. It looks for the deepest descendent element takes contains the point (recall that sibling elements cannot overlap so such element will be unique if it exists), and returns it, or if no descendent contains the point, it returns the pointer to the original element. Unsurprisingly, as we are operating on the element hierarchy, this function works recursively.

Element *ElementFindByPoint(Element *element, int x, int y) {
	for (uintptr_t i = 0; i < element->childCount; i++) {
		if (RectangleContains(element->children[i]->clip, x, y)) {
			return ElementFindByPoint(element->children[i], x, y);
		}
	}

	return element;
}

Now, we add MSG_MOUSE_MOVE to the Message enumeration, and we add int cursorX, cursorY; to the Window structure. And thus we're ready to write _WindowInputEvent. This function will receive all input events from the platform layer, but for this article it only has to deal with MSG_MOUSE_MOVE.

void _WindowInputEvent(Window *window, Message message, int di, void *dp) {
	// Find the element the mouse cursor is hovering over.
	Element *hovered = ElementFindByPoint(&window->e, window->cursorX, window->cursorY);

	// If this is a mouse move message, send the message to the hovered element.
	if (message == MSG_MOUSE_MOVE) {
		ElementMessage(hovered, MSG_MOUSE_MOVE, di, dp);
	}

	// Process any queued repaints.
	_Update();
}

In the platform layers we now add the code to update cursorX and cursorY and then call _WindowInputEvent. In typical fashion, I'm not going to go through this code.

Okay, this will all work as-is, but there's one little extra feature I want to add. Typically, it is nice to be able to check quickly which element the mouse cursor is hovering over, so we'll store a pointer to such element in the Window structure. Furthermore, when this element changes (that is, when the mouse cursor moves from one element to another), we'll send a MSG_UPDATE message to the old and new hovered elements. In the Message enumeration, we add MSG_UPDATE, and in the Window structure we add Element *hovered;. We also define the constant #define UPDATE_HOVERED (1). This will be passed in the int di parameter of the MSG_UPDATE message, indicating that it was the hovered status of the element that was modified. We'll define more constants as needed as we use the MSG_UPDATE message to inform elements of more things. Finally, we add the logic of processing the hovered element into _WindowInputEvent:

void _WindowInputEvent(Window *window, Message message, int di, void *dp) {
	Element *hovered = ElementFindByPoint(&window->e, window->cursorX, window->cursorY);

	if (message == MSG_MOUSE_MOVE) {
		ElementMessage(hovered, MSG_MOUSE_MOVE, di, dp);
	}

	if (hovered != window->hovered) {
		Element *previous = window->hovered;
		window->hovered = hovered;
		ElementMessage(previous, MSG_UPDATE, UPDATE_HOVERED, 0);
		ElementMessage(window->hovered, MSG_UPDATE, UPDATE_HOVERED, 0);
	}

	_Update();
}

The test usage code for this article creates two elements, both of which print out all the MSG_MOUSE_MOVE and MSG_UPDATE messages they receive. Observe how when you move your cursor from one element to the other they are both sent MSG_MOUSE_MOVE messages, and how when you move your cursor outside the window the Window element at the root of hierarchy becomes the hovered element.

part8.c

Part 7: Text and boxes.

Part 9: Mouse buttons!?