diff --git a/src/gui/auxiliary/event/distributor.cpp b/src/gui/auxiliary/event/distributor.cpp index 2d59836f6b4..df42844997f 100644 --- a/src/gui/auxiliary/event/distributor.cpp +++ b/src/gui/auxiliary/event/distributor.cpp @@ -16,23 +16,26 @@ #include "gui/auxiliary/event/distributor.hpp" +#include "events.hpp" #include "gui/auxiliary/log.hpp" #include "gui/widgets/settings.hpp" #include "gui/widgets/widget.hpp" +#include "gui/widgets/window.hpp" #include namespace gui2{ namespace event { -#if 0 + /** * SDL_AddTimer() callback for the hover event. * * When this callback is called it pushes a new hover event in the event queue. * * @param interval The time parameter of SDL_AddTimer. - * @param param Pointer to parameter structure. + * @param param Pointer to a widget that's able to show the + * tooltip (will be used as a dispatcher). * * @returns The new timer interval, 0 to stop. */ @@ -46,7 +49,7 @@ static Uint32 hover_callback(Uint32 /*interval*/, void *param) data.type = HOVER_EVENT; data.code = 0; data.data1 = param; - data.data2 = 0; + data.data2 = NULL; event.type = HOVER_EVENT; event.user = data; @@ -54,7 +57,7 @@ static Uint32 hover_callback(Uint32 /*interval*/, void *param) SDL_PushEvent(&event); return 0; } - +#if 0 /** * SDL_AddTimer() callback for the popup event. * @@ -125,12 +128,26 @@ tmouse_motion::tmouse_motion(twidget& owner : mouse_focus_(NULL) , mouse_captured_(false) , owner_(owner) + , hover_timer_(NULL) + , hover_widget_(NULL) + , hover_position_(0, 0) + , hover_shown_(true) , signal_handler_sdl_mouse_motion_entered_(false) { owner.connect_signal( boost::bind(&tmouse_motion::signal_handler_sdl_mouse_motion , this, _2, _3, _5) , queue_position); + + owner.connect_signal( + boost::bind(&tmouse_motion::signal_handler_show_hover_tooltip + , this, _2) + , queue_position); +} + +tmouse_motion::~tmouse_motion() +{ + stop_hover_timer(); } void tmouse_motion::capture_mouse(//twidget* widget) @@ -191,6 +208,9 @@ void tmouse_motion::mouse_enter(twidget* mouse_over) mouse_focus_ = mouse_over; owner_.fire(event::MOUSE_ENTER, *mouse_over); + + hover_shown_ = false; + start_hover_timer(mouse_over, get_mouse_position()); } void tmouse_motion::mouse_motion(twidget* mouse_over, const tpoint& coordinate) @@ -200,6 +220,15 @@ void tmouse_motion::mouse_motion(twidget* mouse_over, const tpoint& coordinate) assert(mouse_over); owner_.fire(event::MOUSE_MOTION, *mouse_over, coordinate); + + if(hover_timer_) { + if((abs(hover_position_.x - coordinate.x) > 5) + || (abs(hover_position_.y - coordinate.y) > 5)) { + + stop_hover_timer(); + start_hover_timer(mouse_over, coordinate); + } + } } void tmouse_motion::mouse_leave() @@ -209,6 +238,73 @@ void tmouse_motion::mouse_leave() owner_.fire(event::MOUSE_LEAVE, *mouse_focus_); mouse_focus_ = NULL; + + /** @todo also a bit ugly. */ + owner_.get_window()->do_remove_tooltip(); + stop_hover_timer(); +} + +void tmouse_motion::start_hover_timer(twidget* widget, const tpoint& coordinate) +{ + assert(widget); + stop_hover_timer(); + + if(hover_shown_ || !widget->wants_mouse_hover()) { + return; + } + + DBG_GUI_E << LOG_HEADER << "Start hover timer for widget '" + << widget->id() << "' at address " << widget << ".\n"; + + + hover_timer_ = SDL_AddTimer(50, hover_callback, &owner_); + if(hover_timer_) { + hover_widget_ = widget; + hover_position_ = coordinate; + } else { + ERR_GUI_E << LOG_HEADER << "Failed to add hover timer.\n"; + } +} + +void tmouse_motion::stop_hover_timer() +{ + if(hover_timer_) { + assert(hover_widget_); + DBG_GUI_E << LOG_HEADER << "Stop hover timer for widget '" + << hover_widget_->id() << "' at address " + << hover_widget_ << ".\n"; + + if(SDL_RemoveTimer(hover_timer_) == SDL_FALSE) { + ERR_GUI_E << LOG_HEADER << "Failed to remove hover timer.\n"; + } + + hover_timer_ = NULL; + hover_widget_ = NULL; + hover_position_ = tpoint(0, 0); + } +} + +void tmouse_motion::signal_handler_show_hover_tooltip(const event::tevent event) +{ + DBG_GUI_E << LOG_HEADER << event << ".\n"; + + if(!hover_widget_) { + ERR_GUI_E << LOG_HEADER << event << " bailing out, no hover widget.\n"; + } + + /** + * @todo See whether this code can be cleanup a bit. + * + * It now feels a bit hacky with all the casts. It should work but just is + * a bit ugly. + */ + owner_.get_window()->do_show_tooltip(hover_position_ + , dynamic_cast(*hover_widget_).tooltip()); + hover_shown_ = true; + + hover_timer_ = NULL; + hover_widget_ = NULL; + hover_position_ = tpoint(0, 0); } /***** ***** ***** ***** tmouse_button ***** ***** ***** ***** *****/ @@ -759,6 +855,10 @@ void tdistributor::signal_handler_notify_removal( * functions... */ + if(hover_widget_ == &widget) { + stop_hover_timer(); + } + if(tmouse_button_left::last_clicked_widget_ == &widget) { tmouse_button_left::last_clicked_widget_ = NULL; } diff --git a/src/gui/auxiliary/event/distributor.hpp b/src/gui/auxiliary/event/distributor.hpp index fade5d25683..c64bd67c5e8 100644 --- a/src/gui/auxiliary/event/distributor.hpp +++ b/src/gui/auxiliary/event/distributor.hpp @@ -45,12 +45,12 @@ #include "gui/auxiliary/event/dispatcher.hpp" #include "gui/widgets/event_executor.hpp" +#include "gui/widgets/helper.hpp" class t_string; namespace gui2{ -struct tpoint; class twidget; namespace event { @@ -63,6 +63,8 @@ public: tmouse_motion(twidget& owner, const tdispatcher::tposition queue_position); + ~tmouse_motion(); + /** * Captures the mouse input. * @@ -83,6 +85,35 @@ protected: /** The widget that owns us. */ twidget& owner_; + /** The timer for the hover event. */ + SDL_TimerID hover_timer_; + + /** The widget which should get the hover event. */ + twidget* hover_widget_; + + /** The anchor point of the hover event. */ + tpoint hover_position_; + + /** + * Has the hover been shown for the widget? + * + * A widget won't get a second hover event after the tooltip has been + * triggered. Only after (shortly) entering another widget it will be shown + * again for this widget. + */ + bool hover_shown_; + + /** + * Starts the hover timer. + * + * @param widget The widget that wants the tooltip. + * @param coordinate The anchor coordinate. + */ + void start_hover_timer(twidget* widget, const tpoint& coordinate); + + /** Stops the current hover timer. */ + void stop_hover_timer(); + /** * Called when the mouse enters a widget. * @@ -94,6 +125,7 @@ protected: void mouse_leave(); private: + /** * Called when the mouse moves over a widget. * @@ -108,6 +140,8 @@ private: , bool& handled , const tpoint& coordinate); + void signal_handler_show_hover_tooltip(const event::tevent event); + }; /***** ***** ***** ***** tmouse_button ***** ***** ***** ***** *****/ diff --git a/src/gui/auxiliary/event/handler.cpp b/src/gui/auxiliary/event/handler.cpp index 507eba7245d..bfaad618a2f 100644 --- a/src/gui/auxiliary/event/handler.cpp +++ b/src/gui/auxiliary/event/handler.cpp @@ -27,6 +27,18 @@ #include +/** + * @todo The items below are not implemented yet. + * + * - Tooltips have a fixed short time until showing up. + * - Tooltips are shown until the widget is exited. + * - Help messages aren't shown yet. + * + * @note it might be that tooltips will be shown independent of a window and in + * their own window, therefore the code will be cleaned up after that has been + * determined. + */ + /* * At some point in the future this event handler should become the main event * handler. This switch controls the experimental switch for that change. @@ -134,6 +146,14 @@ private: /***** Handlers *****/ + /** + * Fires a hover event. + * + * @param caller The distributor for which to fire the + * event. + */ + void hover(void* caller); + /** Fires a draw event. */ void draw(); @@ -257,7 +277,7 @@ void thandler::handle_event(const SDL_Event& event) break; case HOVER_EVENT: -// hover(); + hover(event.user.data1); break; case HOVER_REMOVE_POPUP_EVENT: @@ -356,6 +376,31 @@ void thandler::disconnect(tdispatcher* dispatcher) #endif } +void thandler::hover(void* caller) +{ + DBG_GUI_E << "Firing: " << SHOW_HOVER_TOOLTIP << ".\n"; + + /* + * Although the caller should be a valid widget, we won't blindly trust + * this to be true. Instead test whether it's a valid widget before firing. + * Since the parameter is a twidget* we need to cast the dispatcher to a + * widget. + */ + + foreach(tdispatcher* dispatcher, dispatchers_) { + twidget* widget = dynamic_cast(dispatcher); + if(widget == caller) { + assert(widget); + dispatcher->fire(SHOW_HOVER_TOOLTIP, *widget, NULL); + return; + } + } + + ERR_GUI_E << "While firing " << SHOW_HOVER_TOOLTIP + << " encountered an invalid dispatcher address " + << caller << ".\n"; +} + void thandler::draw() { // Don't display this event since it floods the screen @@ -624,6 +669,8 @@ std::ostream& operator<<(std::ostream& stream, const tevent event) case NOTIFY_REMOVAL : stream << "notify removal"; break; case RECEIVE_KEYBOARD_FOCUS : stream << "receive keyboard focus"; break; case LOSE_KEYBOARD_FOCUS : stream << "lose keyboard focus"; break; + case SHOW_HOVER_TOOLTIP : stream << "show hover tooltip"; break; + case REMOVE_TOOLTIP : stream << "remove tooltip"; break; } return stream; diff --git a/src/gui/auxiliary/event/handler.hpp b/src/gui/auxiliary/event/handler.hpp index 90c87924de8..5653e182055 100644 --- a/src/gui/auxiliary/event/handler.hpp +++ b/src/gui/auxiliary/event/handler.hpp @@ -99,6 +99,8 @@ enum tevent { */ , RECEIVE_KEYBOARD_FOCUS /**< Widget gets keyboard focus. */ , LOSE_KEYBOARD_FOCUS /**< Widget loses keyboard focus. */ + , SHOW_HOVER_TOOLTIP /**< Request to show the hover tooltip. */ + , REMOVE_TOOLTIP /**< Request to remove a tooltip. */ }; /** @@ -180,6 +182,8 @@ typedef boost::mpl::int_ , boost::mpl::int_ , boost::mpl::int_ + , boost::mpl::int_ + , boost::mpl::int_ > tset_event_notification; diff --git a/src/gui/widgets/window.hpp b/src/gui/widgets/window.hpp index 431127ad90c..8bee21d5fa0 100644 --- a/src/gui/widgets/window.hpp +++ b/src/gui/widgets/window.hpp @@ -528,11 +528,13 @@ private: */ void layout_linked_widgets(); +public: /** Inherited from tevent_handler. */ void do_show_tooltip(const tpoint& location, const t_string& tooltip); /** Inherited from tevent_handler. */ void do_remove_tooltip(); +private: /** Inherited from tevent_handler. */ void do_show_help_popup(const tpoint& location, const t_string& help_popup);