Make SFML 3 shapes listen mouse event when cursor is in their boundary.
[Sample: ManyConvexes]
Screen.Recording.2023-02-07.at.5.22.13.PM.mov
This library is based on CMake. You can use FetchContent
CMake script to add the library.
include(FetchContent)
FetchContent_Declare(SFML_MouseEventListener
GIT_REPOSITORY https://github.com/stripe2933/SFML.MouseEventListener.git)
FetchContent_MakeAvailable(SFML_MouseEventListener)
target_link_libraries(${PROJECT_NAME} PUBLIC SFML_MouseEventListener)
Because the library has direct dependency from SFML, SFML would be automatically included to your project via find_package(SFML).
You can set SFML_DIR
CMake variable manually if the CMake system cannot find SFML.
It supports sf::CircleShape
, sf::RectangleShape
, sf::ConvexShape
and any custom combined boundary consists of circle, rectangle and convex polygon.
For example, if you want to print message when clikcing sf::CircleShape
,
- Make
sf::CircleShape
object.
sf::CircleShape circle { 24.f }; // Make a circle with radius=24.
// You can apply any affine transformation to the object.
circle.setPosition({ 200.f, 100.f });
circle.setScale({ 1.2f, 2.0f });
- Create
MouseEventSystem
which accepts mouse event from window and execute registeredMouseEventListener
.
// MouseEventSystem accept integer-vector(sf::Vector2f) from sf::Event.
// Since your object is not only positioned by float-vector, but also may inside your own created sf::View,
// the accepted int vector and real position of object may different.
// Therefore, You should register MouseEventSystem with coordination converter, which converts mouse position
// to actual object-coordinate position.
MouseEventSystem system { [&](sf::Vector2i position) { return window.mapPixelToCoords(position); } };
// Now you can register listener from target object. Create an ObjectBoundary with factory method `createBoundaryFrom`,
// and unique_ptr of MouseEventListener.
// The constructor of MouseEventListener has optional argument, z_index, which indicates the hit-test priority (higher
// z-index means the object is in front of other objects), sets by 0. You can manually set it as what you want.
auto listener = std::make_unique<MouseEventListener>(circle, ObjectBoundary::createBoundaryFrom(circle));
// And register callback for sf::MouseButtonPressed. It will be executed when press mouse button.
listener->on_mouse_button_pressed = [&](auto &sender, const auto &event){
// sender: MouseEventListener whose callback is executed.
// event: sf::Event::MouseButtonEvent, which has button and cursor position.
std::cout << "Mouse pressed on " << static_cast<void*>(&sender) << " at (" << event.x << ", " << event.y << ")\n";
};
// Register listener to system.
system.addListener(std::move(listener));
- Make
MouseEventSystem
listenssf::Event
polled by window. Since only callback for mouse pressing is registered, it is only necessary for listensf::Event::MouseButtonPressed
event.
for (sf::Event event { }; window.pollEvent(event);) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::MouseButtonPressed:
system.executeMouseEvent<sf::Event::MouseButtonPressed>(event.mouseButton);
break;
// and any other event...
}
}
- Now draw your objects and test if they print messages when clicking them. If you want to render objects by its z-index (so higher z-indexed object is in front of other), you can iterate system's listeners and render them. Listeners are always sorted with ascending z-index.
for (auto &listener : system.getListeners()){
window.draw(*dynamic_cast<const sf::Drawable*>(&listener->target));
}
You can find more sample from ManyConvexes
(make objects pannable using MouseEventListener
) and Shapes
(create an ObjectBoundary
by combining multiple ObjectBoundary
s to make non-regular shaped target be able to listen.)