diff --git a/src/gui/core/event/distributor.cpp b/src/gui/core/event/distributor.cpp index 972087f95054..f372fb49b6e4 100644 --- a/src/gui/core/event/distributor.cpp +++ b/src/gui/core/event/distributor.cpp @@ -357,47 +357,92 @@ void mouse_motion::stop_hover_timer() #undef LOG_HEADER #define LOG_HEADER \ - "distributor mouse button " << name_ << " [" << owner_.id() << "]: " + "distributor mouse button " << events_.name << " [" << owner_.id() << "]: " -template -mouse_button::mouse_button(const std::string& name_, widget& owner, +mouse_button::mouse_button(const mouse_button_event_types& events, widget& owner, const dispatcher::queue_position queue_position) : mouse_motion(owner, queue_position) , last_click_stamp_(0) , last_clicked_widget_(nullptr) , focus_(nullptr) - , name_(name_) + , events_(events) , is_down_(false) , signal_handler_sdl_button_down_entered_(false) , signal_handler_sdl_button_up_entered_(false) { - owner_.connect_signal( - std::bind(&mouse_button::signal_handler_sdl_button_down, - this, - _2, - _3, - _5), - queue_position); - owner_.connect_signal( - std::bind(&mouse_button::signal_handler_sdl_button_up, - this, - _2, - _3, - _5), - queue_position); + // The connect_signal framework is currently using SFINAE checking to ensure that we only + // register mouse button signal handlers for mouse buttons. That causes us to need this + // hardcoded (either directly or by making mouse_button a templated class), the manual handling + // of the three cases here is the current progress on refactoring. + switch(events_.sdl_button_down_event) { + case event::SDL_LEFT_BUTTON_DOWN: { + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_down, + this, + _2, + _3, + _5), + queue_position); + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_up, + this, + _2, + _3, + _5), + queue_position); + break; + } + case event::SDL_MIDDLE_BUTTON_DOWN: { + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_down, + this, + _2, + _3, + _5), + queue_position); + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_up, + this, + _2, + _3, + _5), + queue_position); + break; + } + case event::SDL_RIGHT_BUTTON_DOWN: { + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_down, + this, + _2, + _3, + _5), + queue_position); + owner_.connect_signal( + std::bind(&mouse_button::signal_handler_sdl_button_up, + this, + _2, + _3, + _5), + queue_position); + break; + } + default: { + // There's exactly three instances of this class per instance of distributor, so this assert + // will be caught during the build-time-tests. + assert(!"Hardcoded assumption about button being LEFT / MIDDLE / RIGHT failed"); + } + } } -template -void mouse_button::initialize_state(const bool is_down) +void mouse_button::initialize_state(int32_t button_state) { last_click_stamp_ = 0; last_clicked_widget_ = nullptr; focus_ = 0; - is_down_ = is_down; + is_down_ = button_state & events_.mask; } -template -void mouse_button::signal_handler_sdl_button_down(const event::ui_event event, bool& handled, +void mouse_button::signal_handler_sdl_button_down(const event::ui_event event, bool& handled, const point& coordinate) { if(signal_handler_sdl_button_down_entered_) { @@ -420,10 +465,10 @@ void mouse_button::signal_handler_sdl_button_down(const event::ui_event event if(mouse_captured_) { assert(mouse_focus_); focus_ = mouse_focus_; - DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_down_event << ".\n"; - if(!owner_.fire(T::sdl_button_down_event, *focus_, coordinate)) { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_down_event << ".\n"; - owner_.fire(T::button_down_event, *mouse_focus_); + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_down_event << ".\n"; + if(!owner_.fire(events_.sdl_button_down_event, *focus_, coordinate)) { + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_down_event << ".\n"; + owner_.fire(events_.button_down_event, *mouse_focus_); } } else { widget* mouse_over = owner_.find_at(coordinate, true); @@ -440,17 +485,16 @@ void mouse_button::signal_handler_sdl_button_down(const event::ui_event event } focus_ = mouse_over; - DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_down_event << ".\n"; - if(!owner_.fire(T::sdl_button_down_event, *focus_, coordinate)) { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_down_event << ".\n"; - owner_.fire(T::button_down_event, *focus_); + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_down_event << ".\n"; + if(!owner_.fire(events_.sdl_button_down_event, *focus_, coordinate)) { + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_down_event << ".\n"; + owner_.fire(events_.button_down_event, *focus_); } } handled = true; } -template -void mouse_button::signal_handler_sdl_button_up(const event::ui_event event, bool& handled, +void mouse_button::signal_handler_sdl_button_up(const event::ui_event event, bool& handled, const point& coordinate) { if(signal_handler_sdl_button_up_entered_) { @@ -470,13 +514,17 @@ void mouse_button::signal_handler_sdl_button_up(const event::ui_event event, is_down_ = false; if(focus_) { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_up_event << ".\n"; - if(!owner_.fire(T::sdl_button_up_event, *focus_, coordinate)) { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_up_event << ".\n"; - owner_.fire(T::button_up_event, *focus_); + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_up_event << ".\n"; + if(!owner_.fire(events_.sdl_button_up_event, *focus_, coordinate)) { + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_up_event << ".\n"; + owner_.fire(events_.button_up_event, *focus_); } } + // FIXME: The block below is strange diamond inheritance - it's code that could be in + // mouse_motion which applies to all three buttons, but it will be run in one of the + // three mouse_button subclasses, and then the other two mouse_button subclasses + // will reach here with mouse_captured_ == false. widget* mouse_over = owner_.find_at(coordinate, true); if(mouse_captured_) { const unsigned mask = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK @@ -503,23 +551,22 @@ void mouse_button::signal_handler_sdl_button_up(const event::ui_event event, handled = true; } -template -void mouse_button::mouse_button_click(widget* widget) +void mouse_button::mouse_button_click(widget* widget) { uint32_t stamp = SDL_GetTicks(); if(last_click_stamp_ + settings::double_click_time >= stamp && last_clicked_widget_ == widget) { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_double_click_event << ".\n"; + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_double_click_event << ".\n"; - owner_.fire(T::button_double_click_event, *widget); + owner_.fire(events_.button_double_click_event, *widget); last_click_stamp_ = 0; last_clicked_widget_ = nullptr; } else { - DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_click_event << ".\n"; - owner_.fire(T::button_click_event, *widget); + DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_click_event << ".\n"; + owner_.fire(events_.button_click_event, *widget); last_click_stamp_ = stamp; last_clicked_widget_ = widget; } @@ -530,6 +577,40 @@ void mouse_button::mouse_button_click(widget* widget) #undef LOG_HEADER #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: " +namespace +{ + +const auto mouse_button_left_events = mouse_button_event_types { + SDL_LEFT_BUTTON_DOWN, + SDL_LEFT_BUTTON_UP, + LEFT_BUTTON_DOWN, + LEFT_BUTTON_UP, + LEFT_BUTTON_CLICK, + LEFT_BUTTON_DOUBLE_CLICK, + SDL_BUTTON_LMASK, + "left"}; + +const auto mouse_button_middle_events = mouse_button_event_types { + SDL_MIDDLE_BUTTON_DOWN, + SDL_MIDDLE_BUTTON_UP, + MIDDLE_BUTTON_DOWN, + MIDDLE_BUTTON_UP, + MIDDLE_BUTTON_CLICK, + MIDDLE_BUTTON_DOUBLE_CLICK, + SDL_BUTTON_MMASK, + "middle"}; + +const auto mouse_button_right_events = mouse_button_event_types { + SDL_RIGHT_BUTTON_DOWN, + SDL_RIGHT_BUTTON_UP, + RIGHT_BUTTON_DOWN, + RIGHT_BUTTON_UP, + RIGHT_BUTTON_CLICK, + RIGHT_BUTTON_DOUBLE_CLICK, + SDL_BUTTON_RMASK, + "right"}; +} // anonymous namespace + /** * @todo Test whether the state is properly tracked when an input blocker is * used. @@ -537,9 +618,9 @@ void mouse_button::mouse_button_click(widget* widget) distributor::distributor(widget& owner, const dispatcher::queue_position queue_position) : mouse_motion(owner, queue_position) - , mouse_button_left("left", owner, queue_position) - , mouse_button_middle("middle", owner, queue_position) - , mouse_button_right("right", owner, queue_position) + , mouse_button_left(mouse_button_left_events, owner, queue_position) + , mouse_button_middle(mouse_button_middle_events, owner, queue_position) + , mouse_button_right(mouse_button_right_events, owner, queue_position) , keyboard_focus_(nullptr) , keyboard_focus_chain_() { @@ -581,11 +662,11 @@ distributor::~distributor() void distributor::initialize_state() { - const uint8_t button_state = SDL_GetMouseState(nullptr, nullptr); + const uint32_t button_state = SDL_GetMouseState(nullptr, nullptr); - mouse_button_left::initialize_state((button_state & SDL_BUTTON(1)) != 0); - mouse_button_middle::initialize_state((button_state & SDL_BUTTON(2)) != 0); - mouse_button_right::initialize_state((button_state & SDL_BUTTON(3)) != 0); + mouse_button_left::initialize_state(button_state); + mouse_button_middle::initialize_state(button_state); + mouse_button_right::initialize_state(button_state); init_mouse_location(); } diff --git a/src/gui/core/event/distributor.hpp b/src/gui/core/event/distributor.hpp index 31fb5530e4d0..4461d5c02457 100644 --- a/src/gui/core/event/distributor.hpp +++ b/src/gui/core/event/distributor.hpp @@ -155,41 +155,36 @@ class mouse_motion /***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/ /** - * Small helper metastruct to specialize mouse_button with and provide ui_event type - * aliases without needing to make mouse_button take a million template types. + * Small helper metastruct to configure instances of mouse_button. */ -template< - ui_event sdl_button_down, - ui_event sdl_button_up, - ui_event button_down, - ui_event button_up, - ui_event button_click, - ui_event button_double_click> -struct mouse_button_event_types_wrapper +struct mouse_button_event_types { - static const ui_event sdl_button_down_event = sdl_button_down; - static const ui_event sdl_button_up_event = sdl_button_up; - static const ui_event button_down_event = button_down; - static const ui_event button_up_event = button_up; - static const ui_event button_click_event = button_click; - static const ui_event button_double_click_event = button_double_click; + ui_event sdl_button_down_event; + ui_event sdl_button_up_event; + ui_event button_down_event; + ui_event button_up_event; + ui_event button_click_event; + ui_event button_double_click_event; + /** Bitmask corresponding to this button's bit in SDL_GetMouseState's return value */ + int32_t mask; + /** used for debug messages. */ + std::string name; }; -template class mouse_button : public virtual mouse_motion { public: - mouse_button(const std::string& name_, + mouse_button(const mouse_button_event_types& events, widget& owner, const dispatcher::queue_position queue_position); /** * Initializes the state of the button. * - * @param is_down The initial state of the button, if true down - * else initialized as up. + * @param button_state The initial state of all buttons, in which the bit corresponding to + mouse_button_event_types.mask will be set if the button is down, or unset if it is up. */ - void initialize_state(const bool is_down); + void initialize_state(int32_t button_state); protected: /** The time of the last click used for double clicking. */ @@ -206,8 +201,8 @@ class mouse_button : public virtual mouse_motion widget* focus_; private: - /** used for debug messages. */ - const std::string name_; + /** Which set of SDL events correspond to this button. */ + const mouse_button_event_types events_; /** Is the button down? */ bool is_down_; @@ -226,37 +221,47 @@ class mouse_button : public virtual mouse_motion void mouse_button_click(widget* widget); }; -/***** ***** ***** ***** distributor ***** ***** ***** ***** *****/ +/** + * Three subclasses of mouse_button, so that the distributor class can inherit from them; + * C++ doesn't allow multiple inheritance to directly use more than one instance of a + * superclass. + * + * It's a diamond inheritance, as all of these have virtual base class mouse_motion; + * refactoring that would allow these multiple classes to be replaced with a simple + * (distributor has-a std::array) relationship. + */ +struct mouse_button_left : public mouse_button +{ + mouse_button_left(const mouse_button_event_types& events, + widget& owner, + const dispatcher::queue_position queue_position) + : mouse_motion(owner, queue_position) + , mouse_button(events, owner, queue_position) + { + } +}; +struct mouse_button_middle : public mouse_button +{ + mouse_button_middle(const mouse_button_event_types& events, + widget& owner, + const dispatcher::queue_position queue_position) + : mouse_motion(owner, queue_position) + , mouse_button(events, owner, queue_position) + { + } +}; +struct mouse_button_right : public mouse_button +{ + mouse_button_right(const mouse_button_event_types& events, + widget& owner, + const dispatcher::queue_position queue_position) + : mouse_motion(owner, queue_position) + , mouse_button(events, owner, queue_position) + { + } +}; -using mouse_button_left = mouse_button< - mouse_button_event_types_wrapper< - SDL_LEFT_BUTTON_DOWN, - SDL_LEFT_BUTTON_UP, - LEFT_BUTTON_DOWN, - LEFT_BUTTON_UP, - LEFT_BUTTON_CLICK, - LEFT_BUTTON_DOUBLE_CLICK> - >; - -using mouse_button_middle = mouse_button< - mouse_button_event_types_wrapper< - SDL_MIDDLE_BUTTON_DOWN, - SDL_MIDDLE_BUTTON_UP, - MIDDLE_BUTTON_DOWN, - MIDDLE_BUTTON_UP, - MIDDLE_BUTTON_CLICK, - MIDDLE_BUTTON_DOUBLE_CLICK> - >; - -using mouse_button_right = mouse_button< - mouse_button_event_types_wrapper< - SDL_RIGHT_BUTTON_DOWN, - SDL_RIGHT_BUTTON_UP, - RIGHT_BUTTON_DOWN, - RIGHT_BUTTON_UP, - RIGHT_BUTTON_CLICK, - RIGHT_BUTTON_DOUBLE_CLICK> - >; +/***** ***** ***** ***** distributor ***** ***** ***** ***** *****/ /** The event handler class for the widget library. */