This tutorial continues our look at working with events on Gum objects. The previous tutorial covered the most basic situation – a clickable button in a screen. We’ll expand upon the previous tutorial by creating a more complicated example – handling events on a popup container which displays a message and allows the user to click OK or Cancel. This tutorial covers the second property for controlling events: ExposeChildrenEvents.
HasEvents and ExposeChildrenEvents
HasEvents and ExposeChildrenEvents decide whether a component and its children are recognized as UI objects by the FlatRedBall engine. Since each value can be independently set, then there are four possible combinations of values.
Creating the Components
This tutorial uses three components:
- Button – a clickable component
- Label – a non-clickable component used to display a message to the user
- Popup – a component containing a label and two buttons
Creating the Button Component
The Button component will need some kind of visual (such as a ColoredRectangle). We will include a Text object for the button, but it is not necessary for this tutorial. When finished your Button should look similar to the following image:
The only important detail for this component is to make sure the HasEvents value is set to true. We will look at ExposeChildrenEvents later.
Creating the Label Component
The label component is only a container with a single Text object. Functionally we could skip creating a Label component and add a Text object directly to the Popup component. We will be creating a Label component to show how to include some components in events (Buttons) and exclude others (Label).
When finished your Label should look similar to the following image:
For this tutorial we want to make sure Label instances are not clickable, so make sure the HasEvents property is set to false.
Creating the Popup Component
Finally, we’ll create our Popup Component. It should contain the following instances:
- Button OkButton
- Button CancelButton
- ColoredRectangle (or other visual objects) for the background
When finished, the popup will appear as shown in the following image:
Let’s consider the event behaviour we want for our popup object:
- We want each of the buttons in the popup to be clickable
- We do not want the label to be clickable
- We do not want the entire popup to raise events when it is clicked – only its buttons
This can be summarised as – the popup itself should not raise events, but its children should. We can get this combination of behaviour by setting the HasEvents value to false and the ExposeChildrenEvents value to true.
Adding a Popup to Glue
Now that we have a fully-functional Popup instance, we can add it to our Glue screen. The high-level steps are as follows:
- Create a Gum screen (using an existing one is fine)
- Add an instance of the Popup component to this screen
- Verify that this Gum screen is part of Glue
- Add the Popup instance to Glue as shown in the previous tutorial for the NineSliceButtonInstance
When finished the popup should be in our Glue screen’s Objects folder.
We can add events on the popup just like we added events to the button in the previous tutorial:
- Drag+drop PopupInstance onto the Events folder
- Use the Event drop-down to pick which event you’d like to add
- Click OK
The events which appear in the in the Event drop-down are controlled by the properties we set earlier in this tutorial. Since the Popup’s HasEvents value was set to false, no events on the popup itself appear in the dropdown. Similarly, since the Label’s HasEvents value was also set to false, it does not appear in the Event list.
Note that currently only the Click event is exposed for contained objects. This may change in future versions of the Gum plugin.
Once the event is added in Glue, a stub is created in the Events file of the Glue screen. For example, we can open the MainMenu.Event.cs file to add event handling code, as shown in the following snippet:
void OnPopupInstanceOkButtonClick (FlatRedBall.Gui.IWindow window)
Verifying in Code
As with all other Glue functionality, all Gum events ultimately become generated code. We can observe which events are available by looking in the generated code for a given runtime object. For example, if we open the PopupRuntime.Generated.cs file, we can see which events are available as shown in the following snippet:
public event FlatRedBall.Gui.WindowEvent OkButtonClick;
public event FlatRedBall.Gui.WindowEvent CancelButtonClick;
Not only does this allow us to easily verify that events are properly created, but we can also write code against these events if we prefer to handle all events in code instead of in Glue. For example, we could add the following code to MainMenu.CustomInitialize :
this.PopupInstance.OkButtonClick += HandleOkClick;
private void HandleOkClick(IWindow window)
// write code here
The previous tutorial showed how to diagnose problems with UI elements not responding to UI events. Displaying the Cursor.WindowOver property can give clues about why events are not firing. As a reminder, the following code can be added to your Glue Screen’s CustomActivity method:
Now that we’ve covered events related to contained objects (a Button inside of a Popup), we can look at other ways to diagnose event problems.
Parent Container Receives Events
One of the most common event-related bugs is when the container of a set of instances receives input events instead of the contained instances.
If the parent of an object does not have its ExposeChildrenEvents set to true, then children will not raise their events. For screens with deep hierarchies, any object in the parent/child, then any object in the chain can break events by having this set to false.
For example, consider a button which is part of a standard Container:
If the Container Standard object does not have its ExposeChildrenEvents value checked, then the Button will not raise events.
This can be fixed by checking the ExposeChildrenEvents value.
If a particular component acts purely as a container but should never receive events itself, then its HasEvents should be set to false.
Children and Parent Bounds
By default a parent will only check events on its children if the children are contained within the bounds of the parent. This restriction exists primarily for performance reasons – it allows parents to perform an initial bounding-box check against the cursor and if the test fails, the parent will not perform “deep” collision (testing every contained child).
However, sometimes children are not contained within their parents’ bounds, and if those children have events (such as click events), they will not raise these events by default.
This can be corrected in one of two ways:
- Expand the bounds of the parent to contain the child – this may make sense conceptually and it’s the easiest and most efficient solution to the problem.
- Change the parent instance’s RaiseChildrenEventsOutsideOfBounds value to true. This will enable “deep” checking against all contained children. Of course, be careful when setting this on a large number of parents as it can increase the amount of time that cursor checks take.
This tutorial has shown how to work with events on Gum component instances which are contained in other instances.