GUI2: reimplemented [dis]connect_signal SFINAE using constexpr functions

This replaces the use of the boost::mpl::set lists. It replaces those damn things with simple
constexpr functions that check the template parameter against an accepted list for each event
type. This is a *lot* simpler.

These helpers are also used in the runtime checks in dispatcher::fire.

I've had to leave the mpl sets in, though, since I can't figure out how to convert the last
place where they're used. The presence of a type is required for SFINAE in
dispatcher_implementation::event_signal, and I can't figure out a new design that avoids the
need for the template parameters.
This commit is contained in:
Charles Dang 2018-03-23 18:45:51 +11:00
parent 958c82d086
commit 555d8b667d
2 changed files with 207 additions and 116 deletions

View file

@ -91,9 +91,9 @@ bool dispatcher::has_event(const ui_event event, const event_queue_type event_ty
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_text_input>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_touch_motion>(
|| find<set_event_touch_motion>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_touch_gesture>(
|| find<set_event_touch_gesture>(
event, dispatcher_implementation::has_handler(event_type, *this))
|| find<set_event_notification>(
event, dispatcher_implementation::has_handler(event_type, *this))
@ -103,33 +103,9 @@ bool dispatcher::has_event(const ui_event event, const event_queue_type event_ty
event, dispatcher_implementation::has_handler(event_type, *this));
}
/**
* Helper class to do a runtime test whether an event is in a set.
*
* The class is supposed to be used in combination with find function. This
* function is used in the fire functions to make sure an event is send to the
* proper handler. If not there will be a run-time assertion failure. This
* makes developing and testing the code easier, a wrong handler terminates
* Wesnoth instead of silently not working.
*/
class event_in_set
{
public:
/**
* If found we get executed to set the result.
*
* Since we need to return true if found we always return true.
*/
template <class T>
bool oper(ui_event)
{
return true;
}
};
bool dispatcher::fire(const ui_event event, widget& target)
{
assert(find<set_event>(event, event_in_set()));
assert(is_general_event(event));
switch(event) {
case LEFT_BUTTON_DOUBLE_CLICK:
return fire_event_double_click<LEFT_BUTTON_CLICK, LEFT_BUTTON_DOUBLE_CLICK,
@ -150,7 +126,7 @@ bool dispatcher::fire(const ui_event event, widget& target)
bool dispatcher::fire(const ui_event event, widget& target, const point& coordinate)
{
assert(find<set_event_mouse>(event, event_in_set()));
assert(is_mouse_event(event));
return fire_event<signal_mouse_function>(event, this, &target, coordinate);
}
@ -160,43 +136,43 @@ bool dispatcher::fire(const ui_event event,
const SDL_Keymod modifier,
const std::string& unicode)
{
assert(find<set_event_keyboard>(event, event_in_set()));
assert(is_keyboard_event(event));
return fire_event<signal_keyboard_function>(event, this, &target, key, modifier, unicode);
}
bool dispatcher::fire(const ui_event event, widget& target, const point& pos, const point& distance)
{
assert(find<set_event_touch_motion>(event, event_in_set()));
assert(is_touch_motion_event(event));
return fire_event<signal_touch_motion_function>(event, this, &target, pos, distance);
}
bool dispatcher::fire(const ui_event event, widget& target, const point& center, float dTheta, float dDist, Uint8 numFingers)
{
assert(find<set_event_touch_gesture>(event, event_in_set()));
assert(is_touch_gesture_event(event));
return fire_event<signal_touch_gesture_function>(event, this, &target, center, dTheta, dDist, numFingers);
}
bool dispatcher::fire(const ui_event event, widget& target, const SDL_Event& sdlevent)
{
assert(find<set_event_raw_event>(event, event_in_set()));
assert(is_raw_event(event));
return fire_event<signal_raw_event_function>(event, this, &target, sdlevent);
}
bool dispatcher::fire(const ui_event event, widget& target, const std::string& text, int32_t start, int32_t len)
{
assert(find<set_event_text_input>(event, event_in_set()));
assert(is_text_input_event(event));
return fire_event<signal_text_input_function>(event, this, &target, text, start, len);
}
bool dispatcher::fire(const ui_event event, widget& target, void*)
{
assert(find<set_event_notification>(event, event_in_set()));
assert(is_notification_event(event));
return fire_event<signal_notification_function>(event, this, &target, nullptr);
}
bool dispatcher::fire(const ui_event event, widget& target, const message& msg)
{
assert(find<set_event_message>(event, event_in_set()));
assert(is_message_event(event));
return fire_event<signal_message_function>(event, this, &target, msg);
}

View file

@ -16,13 +16,10 @@
#include "gui/core/event/handler.hpp"
#include "hotkey/hotkey_command.hpp"
#include "utils/functional.hpp"
#include <SDL2/SDL_events.h>
#include <boost/mpl/int.hpp>
#include <list>
#include <map>
#include <type_traits>
@ -35,9 +32,145 @@ class widget;
namespace event
{
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This helper is needed as a user can't supply the wrong kind of callback
* functions to dispatcher::connect_signal. If a wrong callback would be send
* it will never get called.
*
* This version is for callbacks without extra parameters.
* NOTE some mouse functions like MOUSE_ENTER don't send the mouse coordinates
* to the callback function so they are also in this category.
*/
constexpr bool is_general_event(const ui_event event)
{
return event == DRAW
|| event == CLOSE_WINDOW
|| event == MOUSE_ENTER
|| event == MOUSE_LEAVE
|| event == LEFT_BUTTON_DOWN
|| event == LEFT_BUTTON_UP
|| event == LEFT_BUTTON_CLICK
|| event == LEFT_BUTTON_DOUBLE_CLICK
|| event == MIDDLE_BUTTON_DOWN
|| event == MIDDLE_BUTTON_UP
|| event == MIDDLE_BUTTON_CLICK
|| event == MIDDLE_BUTTON_DOUBLE_CLICK
|| event == RIGHT_BUTTON_DOWN
|| event == RIGHT_BUTTON_UP
|| event == RIGHT_BUTTON_CLICK
|| event == RIGHT_BUTTON_DOUBLE_CLICK;
}
template<typename K, ui_event E>
using has_key = boost::mpl::has_key<K, boost::mpl::int_<E>>;
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks with a coordinate as extra parameter.
*/
constexpr bool is_mouse_event(const ui_event event)
{
return event == SDL_VIDEO_RESIZE
|| event == SDL_MOUSE_MOTION
|| event == MOUSE_MOTION
|| event == SDL_LEFT_BUTTON_DOWN
|| event == SDL_LEFT_BUTTON_UP
|| event == SDL_MIDDLE_BUTTON_DOWN
|| event == SDL_MIDDLE_BUTTON_UP
|| event == SDL_RIGHT_BUTTON_DOWN
|| event == SDL_RIGHT_BUTTON_UP
|| event == SHOW_TOOLTIP
|| event == SHOW_HELPTIP
|| event == SDL_WHEEL_UP
|| event == SDL_WHEEL_DOWN
|| event == SDL_WHEEL_LEFT
|| event == SDL_WHEEL_RIGHT
|| event == SDL_TOUCH_UP
|| event == SDL_TOUCH_DOWN;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks with the keyboard values (these haven't been
* determined yet).
*/
constexpr bool is_keyboard_event(const ui_event event)
{
return event == SDL_KEY_DOWN;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks of touch motion events.
*/
constexpr bool is_touch_motion_event(const ui_event event)
{
return event == SDL_TOUCH_MOTION;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks of touch gesture events.
*/
constexpr bool is_touch_gesture_event(const ui_event event)
{
return event == SDL_TOUCH_MULTI_GESTURE;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks with a sender aka notification messages. Like the
* ones in set_event it has no extra parameters, but this version is only
* send to the target and not using the pre and post queue.
*/
constexpr bool is_notification_event(const ui_event event)
{
return event == NOTIFY_REMOVAL
|| event == NOTIFY_MODIFIED
|| event == RECEIVE_KEYBOARD_FOCUS
|| event == LOSE_KEYBOARD_FOCUS
|| event == NOTIFY_REMOVE_TOOLTIP
|| event == SDL_ACTIVATE;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks with a sender aka notification messages.
* Unlike the notifications this message is send through the chain. The event
* is send from a widget all the way up to the window, who always is the
* receiver of the message (unless somebody grabbed it before).
*/
constexpr bool is_message_event(const ui_event event)
{
return event == MESSAGE_SHOW_TOOLTIP
|| event == MESSAGE_SHOW_HELPTIP
|| event == REQUEST_PLACEMENT;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks of raw events.
*/
constexpr bool is_raw_event(const ui_event event)
{
return event == SDL_RAW_EVENT;
}
/**
* Helper for catching use error of @ref dispatcher::connect_signal.
*
* This version is for callbacks of text input events.
*/
constexpr bool is_text_input_event(const ui_event event)
{
return event == SDL_TEXT_INPUT || event == SDL_TEXT_EDITING;
}
struct message;
@ -388,10 +521,9 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event, E>::value>
connect_signal(const signal_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_general_event(E)>
connect_signal(const signal_function& signal, const queue_position position = back_child)
{
signal_queue_.connect_signal(E, position, signal);
}
@ -406,10 +538,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event, E>::value>
disconnect_signal(const signal_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_general_event(E)>
disconnect_signal(const signal_function& signal, const queue_position position = back_child)
{
signal_queue_.disconnect_signal(E, position, signal);
}
@ -421,10 +552,9 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_mouse, E>::value>
connect_signal(const signal_mouse_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_mouse_event(E)>
connect_signal(const signal_mouse_function& signal, const queue_position position = back_child)
{
signal_mouse_queue_.connect_signal(E, position, signal);
}
@ -439,10 +569,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_mouse, E>::value>
disconnect_signal(const signal_mouse_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_mouse_event(E)>
disconnect_signal(const signal_mouse_function& signal, const queue_position position = back_child)
{
signal_mouse_queue_.disconnect_signal(E, position, signal);
}
@ -454,10 +583,9 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_keyboard, E>::value>
connect_signal(const signal_keyboard_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_keyboard_event(E)>
connect_signal(const signal_keyboard_function& signal, const queue_position position = back_child)
{
signal_keyboard_queue_.connect_signal(E, position, signal);
}
@ -472,10 +600,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_keyboard, E>::value>
disconnect_signal(const signal_keyboard_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_keyboard_event(E)>
disconnect_signal(const signal_keyboard_function& signal, const queue_position position = back_child)
{
signal_keyboard_queue_.disconnect_signal(E, position, signal);
}
@ -487,16 +614,15 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_touch_motion, E>::value>
connect_signal(const signal_touch_motion_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_touch_motion_event(E)>
connect_signal(const signal_touch_motion_function& signal, const queue_position position = back_child)
{
signal_touch_motion_queue_.connect_signal(E, position, signal);
}
/**
* Disconnect a signal for callback in set_event_touch.
* Disconnect a signal for callback in set_event_touch_motion.
*
* @tparam E The event the callback was used for.
* @param signal The callback function.
@ -505,10 +631,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_touch_motion, E>::value>
disconnect_signal(const signal_touch_motion_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_touch_motion_event(E)>
disconnect_signal(const signal_touch_motion_function& signal, const queue_position position = back_child)
{
signal_touch_motion_queue_.disconnect_signal(E, position, signal);
}
@ -520,16 +645,15 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_touch_gesture, E>::value>
connect_signal(const signal_touch_gesture_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_touch_gesture_event(E)>
connect_signal(const signal_touch_gesture_function& signal, const queue_position position = back_child)
{
signal_touch_gesture_queue_.connect_signal(E, position, signal);
}
/**
* Disconnect a signal for callback in set_event_touch.
* Disconnect a signal for callback in set_event_touch_gesture.
*
* @tparam E The event the callback was used for.
* @param signal The callback function.
@ -538,10 +662,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_touch_gesture, E>::value>
disconnect_signal(const signal_touch_gesture_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_touch_gesture_event(E)>
disconnect_signal(const signal_touch_gesture_function& signal, const queue_position position = back_child)
{
signal_touch_gesture_queue_.disconnect_signal(E, position, signal);
}
@ -556,10 +679,9 @@ public:
* the pre and post positions make no sense
* and shouldn't be used.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_notification, E>::value>
connect_signal(const signal_notification_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_notification_event(E)>
connect_signal(const signal_notification_function& signal, const queue_position position = back_child)
{
signal_notification_queue_.connect_signal(E, position, signal);
}
@ -579,10 +701,9 @@ public:
* front_child and remove with
* front_pre_child)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_notification, E>::value>
disconnect_signal(const signal_notification_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_notification_event(E)>
disconnect_signal(const signal_notification_function& signal, const queue_position position = back_child)
{
signal_notification_queue_.disconnect_signal(E, position, signal);
}
@ -597,10 +718,9 @@ public:
* the pre and post positions make no sense
* and shouldn't be used.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_message, E>::value>
connect_signal(const signal_message_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_message_event(E)>
connect_signal(const signal_message_function& signal, const queue_position position = back_child)
{
signal_message_queue_.connect_signal(E, position, signal);
}
@ -620,10 +740,9 @@ public:
* front_child and remove with
* front_pre_child)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_message, E>::value>
disconnect_signal(const signal_message_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_message_event(E)>
disconnect_signal(const signal_message_function& signal, const queue_position position = back_child)
{
signal_message_queue_.disconnect_signal(E, position, signal);
}
@ -635,10 +754,9 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_raw_event, E>::value>
connect_signal(const signal_raw_event_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_raw_event(E)>
connect_signal(const signal_raw_event_function& signal, const queue_position position = back_child)
{
signal_raw_event_queue_.connect_signal(E, position, signal);
}
@ -653,10 +771,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_raw_event, E>::value>
disconnect_signal(const signal_raw_event_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_raw_event(E)>
disconnect_signal(const signal_raw_event_function& signal, const queue_position position = back_child)
{
signal_raw_event_queue_.disconnect_signal(E, position, signal);
}
@ -668,10 +785,9 @@ public:
* @param signal The callback function.
* @param position The position to place the callback.
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_text_input, E>::value>
connect_signal(const signal_text_input_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_text_input_event(E)>
connect_signal(const signal_text_input_function& signal, const queue_position position = back_child)
{
signal_text_input_queue_.connect_signal(E, position, signal);
}
@ -686,10 +802,9 @@ public:
* place. (The function doesn't care whether
* was added in front or back.)
*/
template <ui_event E>
std::enable_if_t<has_key<set_event_text_input, E>::value>
disconnect_signal(const signal_text_input_function& signal,
const queue_position position = back_child)
template<ui_event E>
std::enable_if_t<is_text_input_event(E)>
disconnect_signal(const signal_text_input_function& signal, const queue_position position = back_child)
{
signal_text_input_queue_.disconnect_signal(E, position, signal);
}
@ -752,7 +867,7 @@ public:
}
/** Helper struct to generate the various signal types. */
template <class T>
template<class T>
struct signal_type
{
signal_type() : pre_child(), child(), post_child()
@ -765,7 +880,7 @@ public:
};
/** Helper struct to generate the various event queues. */
template <class T>
template<class T>
struct signal_queue
{
signal_queue() : queue()