mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Added visitation support to Event and WindowBase via handleEvents.
This commit is contained in:
parent
5873a7a157
commit
41c48a84bc
@ -299,6 +299,17 @@ public:
|
|||||||
template <typename TEventSubtype>
|
template <typename TEventSubtype>
|
||||||
[[nodiscard]] const TEventSubtype* getIf() const;
|
[[nodiscard]] const TEventSubtype* getIf() const;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Apply a visitor to the event
|
||||||
|
///
|
||||||
|
/// \param visitor The visitor to apply
|
||||||
|
///
|
||||||
|
/// \return The result of applying the visitor to the event
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) visit(T&& visitor) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Member data
|
// Member data
|
||||||
|
@ -67,4 +67,12 @@ const TEventSubtype* Event::getIf() const
|
|||||||
return std::get_if<TEventSubtype>(&m_data);
|
return std::get_if<TEventSubtype>(&m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) Event::visit(T&& visitor) const
|
||||||
|
{
|
||||||
|
return std::visit(std::forward<T>(visitor), m_data);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
@ -206,7 +206,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// \return The event, otherwise `std::nullopt` if no events are pending
|
/// \return The event, otherwise `std::nullopt` if no events are pending
|
||||||
///
|
///
|
||||||
/// \see waitEvent
|
/// \see waitEvent, handleEvents
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] std::optional<Event> pollEvent();
|
[[nodiscard]] std::optional<Event> pollEvent();
|
||||||
@ -232,11 +232,89 @@ public:
|
|||||||
///
|
///
|
||||||
/// \return The event, otherwise `std::nullopt` on timeout or if window was closed
|
/// \return The event, otherwise `std::nullopt` on timeout or if window was closed
|
||||||
///
|
///
|
||||||
/// \see pollEvent
|
/// \see pollEvent, handleEvents
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] std::optional<Event> waitEvent(Time timeout = Time::Zero);
|
[[nodiscard]] std::optional<Event> waitEvent(Time timeout = Time::Zero);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Handle all pending events
|
||||||
|
///
|
||||||
|
/// This function is not blocking: if there's no pending event then
|
||||||
|
/// it will return without calling any of the handlers.
|
||||||
|
///
|
||||||
|
/// This function can take a variadic list of event handlers that
|
||||||
|
/// each take a concrete event type as a single parameter. The event
|
||||||
|
/// handlers can be any kind of callable object that has an
|
||||||
|
/// operator() defined for a specific event type. Additionally a
|
||||||
|
/// generic callable can also be provided that will be invoked for
|
||||||
|
/// every event type. If both types of callables are provided, the
|
||||||
|
/// callables taking concrete event types will be prefered over the
|
||||||
|
/// generic callable by overload resolution. Generic callables can
|
||||||
|
/// be used to customize handler dispatching based on the deduced
|
||||||
|
/// type of the event and other information available at compile
|
||||||
|
/// time.
|
||||||
|
///
|
||||||
|
/// Examples of callables:
|
||||||
|
/// - Lambda expressions: `[&](const sf::Event::KeyPressed) { ... }`
|
||||||
|
/// - Free functions: `void handler(const sf::Event::KeyPressed&) { ... }`
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// // Only provide handlers for concrete event types
|
||||||
|
/// window.handleEvents(
|
||||||
|
/// [&](const sf::Event::Closed&) { /* handle event */ },
|
||||||
|
/// [&](const sf::Event::KeyPressed& keyPress) { /* handle event */ }
|
||||||
|
/// );
|
||||||
|
/// \endcode
|
||||||
|
/// \code
|
||||||
|
/// // Provide a generic event handler
|
||||||
|
/// window.handleEvents(
|
||||||
|
/// [&](const auto& event)
|
||||||
|
/// {
|
||||||
|
/// if constexpr (std::is_same_v<std::decay_t<decltype(event)>, sf::Event::Closed>)
|
||||||
|
/// {
|
||||||
|
/// // Handle Closed
|
||||||
|
/// handleClosed();
|
||||||
|
/// }
|
||||||
|
/// else if constexpr (std::is_same_v<std::decay_t<decltype(event)>, sf::Event::KeyPressed>)
|
||||||
|
/// {
|
||||||
|
/// // Handle KeyPressed
|
||||||
|
/// handleKeyPressed(event);
|
||||||
|
/// }
|
||||||
|
/// else
|
||||||
|
/// {
|
||||||
|
/// // Handle non-KeyPressed
|
||||||
|
/// handleOtherEvents(event);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
|
/// \endcode
|
||||||
|
/// \code
|
||||||
|
/// // Provide handlers for concrete types and fall back to generic handler
|
||||||
|
/// window.handleEvents(
|
||||||
|
/// [&](const sf::Event::Closed&) { /* handle event */ },
|
||||||
|
/// [&](const sf::Event::KeyPressed& keyPress) { /* handle event */ },
|
||||||
|
/// [&](const auto& event) { /* handle all other events */ }
|
||||||
|
/// );
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// Calling member functions is supported through lambda
|
||||||
|
/// expressions.
|
||||||
|
/// \code
|
||||||
|
/// // Provide a generic event handler
|
||||||
|
/// window.handleEvents(
|
||||||
|
/// [this](const auto& event) { handle(event); }
|
||||||
|
/// );
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// \param handlers A variadic list of callables that take a specific event as their only parameter
|
||||||
|
///
|
||||||
|
/// \see waitEvent, pollEvent
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
template <typename... Ts>
|
||||||
|
void handleEvents(Ts&&... handlers);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the position of the window
|
/// \brief Get the position of the window
|
||||||
///
|
///
|
||||||
@ -520,6 +598,7 @@ private:
|
|||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
|
||||||
|
#include <SFML/Window/WindowBase.inl>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \class sf::WindowBase
|
/// \class sf::WindowBase
|
||||||
|
77
include/SFML/Window/WindowBase.inl
Normal file
77
include/SFML/Window/WindowBase.inl
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// SFML - Simple and Fast Multimedia Library
|
||||||
|
// Copyright (C) 2007-2024 Laurent Gomila (laurent@sfml-dev.org)
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
//
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
// subject to the following restrictions:
|
||||||
|
//
|
||||||
|
// 1. The origin of this software must not be misrepresented;
|
||||||
|
// you must not claim that you wrote the original software.
|
||||||
|
// If you use this software in a product, an acknowledgment
|
||||||
|
// in the product documentation would be appreciated but is not required.
|
||||||
|
//
|
||||||
|
// 2. Altered source versions must be plainly marked as such,
|
||||||
|
// and must not be misrepresented as being the original software.
|
||||||
|
//
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Headers
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
#include <SFML/Window/Event.hpp>
|
||||||
|
#include <SFML/Window/WindowBase.hpp> // NOLINT(misc-header-include-cycle)
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
||||||
|
namespace sf
|
||||||
|
{
|
||||||
|
namespace priv
|
||||||
|
{
|
||||||
|
template <typename... Ts>
|
||||||
|
struct OverloadSet : Ts...
|
||||||
|
{
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
template <typename... Ts>
|
||||||
|
OverloadSet(Ts...) -> OverloadSet<Ts...>;
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct OverloadSetWithDefault : OverloadSet<Ts...>
|
||||||
|
{
|
||||||
|
// By providing our own operator() and forwarding based
|
||||||
|
// on invocability of OverloadSet<Ts...> on the concrete type
|
||||||
|
// of the value, we save the user from having to provide
|
||||||
|
// their own catch-all overload if they don't want to
|
||||||
|
template <typename T>
|
||||||
|
void operator()([[maybe_unused]] T&& value) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_invocable_v<OverloadSet<Ts...>, T>)
|
||||||
|
OverloadSet<Ts...>::operator()(std::forward<T>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename... Ts>
|
||||||
|
OverloadSetWithDefault(Ts...) -> OverloadSetWithDefault<Ts...>;
|
||||||
|
} // namespace priv
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
template <typename... Ts>
|
||||||
|
void WindowBase::handleEvents(Ts&&... handlers) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
|
{
|
||||||
|
// Disable misc-const-correctness for this line since clang-tidy
|
||||||
|
// complains about it even though the code would become uncompilable
|
||||||
|
priv::OverloadSetWithDefault overloadSet{std::forward<Ts>(handlers)...}; // NOLINT(misc-const-correctness)
|
||||||
|
|
||||||
|
while (const std::optional event = pollEvent())
|
||||||
|
event->visit(overloadSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sf
|
@ -46,6 +46,7 @@ set(SRC
|
|||||||
${INCROOT}/Window.hpp
|
${INCROOT}/Window.hpp
|
||||||
${SRCROOT}/WindowBase.cpp
|
${SRCROOT}/WindowBase.cpp
|
||||||
${INCROOT}/WindowBase.hpp
|
${INCROOT}/WindowBase.hpp
|
||||||
|
${INCROOT}/WindowBase.inl
|
||||||
${INCROOT}/WindowEnums.hpp
|
${INCROOT}/WindowEnums.hpp
|
||||||
${INCROOT}/WindowHandle.hpp
|
${INCROOT}/WindowHandle.hpp
|
||||||
${SRCROOT}/WindowImpl.cpp
|
${SRCROOT}/WindowImpl.cpp
|
||||||
|
@ -2,8 +2,36 @@
|
|||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::string_view operator()(const sf::Event::Closed&) const
|
||||||
|
{
|
||||||
|
return "Closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view operator()(const sf::Event::Resized&) const
|
||||||
|
{
|
||||||
|
return "Resized";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view operator()(const sf::Event::KeyPressed&) const
|
||||||
|
{
|
||||||
|
return "KeyPressed";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string_view operator()(const T&) const
|
||||||
|
{
|
||||||
|
return "Other";
|
||||||
|
}
|
||||||
|
} visitor;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("[Window] sf::Event")
|
TEST_CASE("[Window] sf::Event")
|
||||||
{
|
{
|
||||||
SECTION("Type traits")
|
SECTION("Type traits")
|
||||||
@ -266,4 +294,13 @@ TEST_CASE("[Window] sf::Event")
|
|||||||
CHECK(sensorChanged.type == sf::Sensor::Type{});
|
CHECK(sensorChanged.type == sf::Sensor::Type{});
|
||||||
CHECK(sensorChanged.value == sf::Vector3f());
|
CHECK(sensorChanged.value == sf::Vector3f());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("visit()")
|
||||||
|
{
|
||||||
|
CHECK(sf::Event(sf::Event::Closed{}).visit(visitor) == "Closed");
|
||||||
|
CHECK(sf::Event(sf::Event::Resized{}).visit(visitor) == "Resized");
|
||||||
|
CHECK(sf::Event(sf::Event::FocusLost{}).visit(visitor) == "Other");
|
||||||
|
CHECK(sf::Event(sf::Event::FocusGained{}).visit(visitor) == "Other");
|
||||||
|
CHECK(sf::Event(sf::Event::KeyPressed{}).visit(visitor) == "KeyPressed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user