Let's expand the application by adding two buttons, one to decrement the count and one to increment it. The count will be restricted between 0 and 10. When the count reaches an endpoint of its range the corresponding button will be grayed.

We begin by adding two global variables for the buttons:

Button *buttonIncrement;
Button *buttonDecrement;

Then we create the buttons, putting them in the row panel.

row = PanelCreate(container, PANEL_HORIZONTAL | ELEMENT_H_FILL);

// NEW!
buttonDecrement = ButtonCreate(&row->e, 0, "-", -1); 
buttonDecrement->e.messageUser = ButtonDecrementMessage;

char buffer[64];
snprintf(buffer, sizeof(buffer), "Count: %d", count);
labelCount = LabelCreate(&row->e, ELEMENT_H_FILL | LABEL_CENTER, buffer, -1);

// NEW!
buttonIncrement = ButtonCreate(&row->e, 0, "+", -1);
buttonIncrement->e.messageUser = ButtonIncrementMessage;

return MessageLoop();

Now we have to write their message handlers. Let's go through the increment button. The decrement button is very similar. First, the logic for the determining whether the button should be grayed.

int ButtonIncrementMessage(Element *element, Message message, int di, void *dp) {
	if (message == MSG_BUTTON_GET_COLOR) {
		// _ButtonMessage is asking for the background color of the button.
		if (count == 10) {
			// We can't increment the count anymore, so gray the button.
			*(uint32_t *) dp = 0xCCCCCC; 
		} else {
			// Use the default button color (white).
		}
	}

	return 0;
}

Now we need to respond to clicking the button.

if (message == MSG_CLICKED) {
	if (count < 10) {
		count++;
	}
}

However, this is insufficient. We also need to update the label. And since the buttons could change color when the count changes, we also have to repaint the buttons.

if (message == MSG_CLICKED) {
	if (count < 10) {
		count++;

		char buffer[64];
		snprintf(buffer, sizeof(buffer), "Count: %d", count);
		LabelSetContent(labelCount, buffer, -1);

		ElementRepaint(&buttonIncrement->e, NULL);
		ElementRepaint(&buttonDecrement->e, NULL);
		ElementRepaint(&labelCount->e, NULL);
	}
}

And it this works! But there's a lot of code duplication; setting the label's text is done in 3 different places. If we were writing a more complicated application with many different variables and state this would become quickly unmanagable. So we're going to have to find something smart to do.

A window showing the counter at 10, with the increment button grayed and the decrement button not grayed.

part14.c

Part 13: Introduction to state synchronization.

Part 15: Refactoring out population.