Modern GUI libraries often use some sort of callbacks (classical Observer pattern, or function objects in languages that support them).
From the way you ask your question, I assume you want to keep HandleInput()
and Update()
strictly separate, where the latter executes the logic associated with the button. You can achieve this by determining and storing which buttons were clicked in HandleInput()
, and invoke the corresponding callbacks later in Update()
. A map allows you to associate buttons (more precisely, their unique identifiers, e.g. an int
or the text written on them) with callbacks.
In C++, an easy way is to use std::function
. Your class could look as follows:
std::vector<int> pressedButtons;
std::map<int, std::function<void()>> callbackMap;
void HandleInput()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::MouseButtonPressed)
{
for (Button& b : buttons)
{
if (IsOnButton(b, event.mouseButton))
pressedButtons.push_back(button.GetId());
}
}
}
}
void Update()
{
for (int id : pressedButtons)
{
auto itr = callbackMap.find(id);
if (itr != callbackMap.end())
(*itr)();
}
pressedButtons.clear();
}
void RegisterCallback(const Button& b, std::function<void()> f)
{
callbackMap[b.GetId()] = std::move(f);
}
You may ask why I store a container and not a single button, as users are very unlikely to click more than one button per frame. That is partially true, but not for special cases like overlapping buttons. Furthermore, this approach can easily be applied to other scenarios, where a container makes sense.
Since you're using SFML, there are existing solutions on the Wiki or in the Thor library that will help you with this design. For example, Thor allows you to define "actions" that map arbitrary combinations of SFML events to your callbacks.