0099: SFX - Button Interactions

This post came about as the result of a request from Joel Christensen who asked me to cook up a demo wherein clicking one GTK Button would change the appearance of another.

Anyway, on with the show…

Changing Another Button’s Text Label

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

But let’s get both buttons to react, each to the other. This isn’t really an Observer pattern, although it could be written as one. Instead, you might think of it as a Cooperator pattern if, indeed, it warrants being called a pattern at all. (Note: in an upcoming post, we’ll look at using the Observer pattern to get a Button to affect changes in whatever types of Widget we point it at.)

For this demonstration, the most straightforward (and immediately noticeable) change that can be made in the partner Button is to switch out the text label, so that’s what we’ll do.

And since the text label changes will go back and forth between the two Buttons, let’s call this class PingPongButton.

The PingPongButton Preamble

Here’s what gets declared and defined in the preamble:

int labelNumber = 0;
string labelText;
string[] nameSuffixes = [" Tra", " La", " Li"];
PingPongButton partnerButton;

And here’s what they mean:

  • int labelNumber: an index into the nameSuffixes array,
  • string labelText: the base name for the button (which is passed in from outside),
  • string nameSuffixes: an array of suffixes for the button names that we can switch between each time a PingPongButton is clicked, and
  • PingPongButton partnerButton: a reference to a companion button that will interact with this one.

The PingPongButton Constructor

This is fairly plain and simple despite the extra functionality we’re stuffing into it:

this(string buttonLabel)
{
	super(buttonLabel ~ nameSuffixes[0]);
	labelText = buttonLabel;
		
	addOnButtonPress(&onButtonPress);
		
} // this()

As mentioned above, we’re passing in a string to use as Button label text. That gets concatenated to the first element of the nameSuffixes array on start-up, but we’ll be switching that up in a moment.

And, of course, the only other thing happening here is hooking up the onButtonPress signal.

Moving right along…

The Callback

There’s nothing mysterious here:

bool onButtonPress(Event e, Widget w)
{
	string newLabel;
	
	labelNumber++;
		
	if(labelNumber == nameSuffixes.length)
	{
		labelNumber = 0;
	}
		
	newLabel = labelText ~ nameSuffixes[labelNumber];
	partnerButton.setLabel(newLabel);
		
	writeln("Partner button label has changed to: ", newLabel);
		
	return(true);
		
} // onButtonPress()

After setting up a string to hold the new label text we’re about to build, we increment the index we use for digging into the nameSuffixes array.

Then we check to make sure we aren’t running off the end of the nameSuffixes string array by testing for the array length and rewinding our index to 0.

From there, it’s all rather predictable. We concatenate the new label text together and slap it onto the companion button.

But we haven’t mentioned how to get the reference to the companion Button. Well, like most things GtkD, it’s not complicated once you’ve seen it done…

The addPartner() Function

Here’s the last piece of the puzzle:

void addPartner(PingPongButton newPartnerButton)
{
	writeln("New partner button for ", getLabel(), ": ", newPartnerButton.getLabel());
	partnerButton = newPartnerButton;
		
} // addPartner()

This function is called from the AppBox class right after both PingPongButtons are instantiated. One call for each PingPongButton and we’re all set up.

And since we’re talking about it, here’s…

The AppBox Class

Let’s just look at the whole thing, shall we?

Preamble

Meet Ralph and George, our two Buttons, each with the power to change the other:

string ralph = "Ralph", george = "George";
PingPongButton pingButton, pongButton;
int globalPadding = 10, localPadding = 5;

Constructor

And here is how we set them up:

this()
{
	super(Orientation.HORIZONTAL, globalPadding);
		
	pingButton = new PingPongButton(ralph);
	packStart(pingButton, false, false, localPadding);

	pongButton = new PingPongButton(george);
	packStart(pongButton, false, false, localPadding);

	// partner up the buttons
	pingButton.addPartner(pongButton);
	pongButton.addPartner(pingButton);
		
} // this()

The most important bit here is that both buttons need to be instantiated before we make the calls to addPartner(), one call for each button. Obviously, addPartner() can only be called at a time when both buttons exist.

Conclusion

And that’s how to affect the appearance of one Button with another. But we’re not done with this because it occurred to me that we might also want to change other things besides the text labels. What about the color of the Button? Or the text label’s font?

Tune in next time when we’ll take a look at this. Here’s a hint: it involves CSS.

See you then.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant