The Battle for Wesnoth  1.19.0-dev
distributor.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "gui/core/log.hpp"
21 #include "gui/core/timer.hpp"
22 #include "gui/widgets/settings.hpp"
24 #include "gui/widgets/widget.hpp"
25 #include "sdl/input.hpp" // get_mouse_button_mask
26 
27 #include <array>
28 #include <functional>
29 
30 namespace gui2::event
31 {
32 /**
33  * Small helper to keep a resource (boolean) locked.
34  *
35  * Some of the event handling routines can't be called recursively, this due to
36  * the fact that they are attached to the pre queue and when the forward an
37  * event the pre queue event gets triggered recursively causing infinite
38  * recursion.
39  *
40  * To prevent that those functions check the lock and exit when the lock is
41  * held otherwise grab the lock here.
42  */
44 {
45 public:
46  resource_locker(bool& locked) : locked_(locked)
47  {
48  assert(!locked_);
49  locked_ = true;
50  }
51 
53  {
54  assert(locked_);
55  locked_ = false;
56  }
57 
58 private:
59  bool& locked_;
60 };
61 
62 
63 /***** ***** ***** ***** mouse_motion ***** ***** ***** ***** *****/
64 
65 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
66 
68  : mouse_focus_(nullptr)
69  , mouse_captured_(false)
70  , owner_(owner)
71  , hover_timer_(0)
72  , hover_widget_(nullptr)
73  , hover_position_(0, 0)
74  , hover_shown_(true)
75  , signal_handler_sdl_mouse_motion_entered_(false)
76 {
79  this,
80  std::placeholders::_2,
81  std::placeholders::_3,
82  std::placeholders::_5),
83  queue_position);
84 
87  this,
88  std::placeholders::_2,
89  std::placeholders::_3,
90  std::placeholders::_5,
91  std::placeholders::_6),
92  queue_position);
93 
96  this,
97  std::placeholders::_2,
98  std::placeholders::_3,
99  std::placeholders::_5
100  ));
101 
104  this,
105  std::placeholders::_2,
106  std::placeholders::_3,
107  std::placeholders::_5
108  ));
109 
112  this,
113  std::placeholders::_2,
114  std::placeholders::_3,
115  std::placeholders::_5
116  ));
117 
120  this,
121  std::placeholders::_2,
122  std::placeholders::_3,
123  std::placeholders::_5
124  ));
125 
128  this,
129  std::placeholders::_2,
130  std::placeholders::_3,
131  std::placeholders::_5),
132  queue_position);
133 }
134 
136 {
138 }
139 
140 void mouse_motion::capture_mouse(const bool capture)
141 {
142  assert(mouse_focus_);
143  mouse_captured_ = capture;
144 }
145 
147 {
149  return;
150  }
152 
153  DBG_GUI_E << LOG_HEADER << event << ".";
154 
155  if(mouse_captured_) {
156  assert(mouse_focus_);
157  if(!owner_.fire(event, *mouse_focus_, coordinate)) {
159  }
160  } else {
161  widget* mouse_over = owner_.find_at(coordinate, true);
162  while(mouse_over && !mouse_over->can_mouse_focus() && mouse_over->parent()) {
163  mouse_over = mouse_over->parent();
164  }
165 
166  if(mouse_over) {
167  DBG_GUI_E << LOG_HEADER << "Firing: " << event << ".";
168  if(owner_.fire(event, *mouse_over, coordinate)) {
169  return;
170  }
171  }
172 
173  if(!mouse_focus_ && mouse_over) {
174  mouse_enter(mouse_over);
175  } else if(mouse_focus_ && !mouse_over) {
176  mouse_leave();
177  } else if(mouse_focus_ && mouse_focus_ == mouse_over) {
178  mouse_hover(mouse_over, coordinate);
179  } else if(mouse_focus_ && mouse_over) {
180  // moved from one widget to the next
181  mouse_leave();
182  mouse_enter(mouse_over);
183  } else {
184  assert(!mouse_focus_ && !mouse_over);
185  }
186  }
187  handled = true;
188 }
189 
191  const event::ui_event event, bool& handled, const point& coordinate, const point& distance)
192 {
193  DBG_GUI_E << LOG_HEADER << event << ".";
194 
195  if(mouse_captured_) {
196  assert(mouse_focus_);
197  owner_.fire(event, *mouse_focus_, coordinate, distance);
198  } else {
199  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
200  owner_.fire(event, *mouse_over, coordinate, distance);
201  }
202  }
203  handled = true;
204 }
205 
207 {
208  DBG_GUI_E << LOG_HEADER << event << ".";
209 
210  if(mouse_captured_) {
211  assert(mouse_focus_);
213  } else {
214  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
215  owner_.fire(event, *mouse_over, coordinate);
216  }
217  }
218  handled = true;
219 }
220 
222 {
223  DBG_GUI_E << LOG_HEADER << event << ".";
224 
225  if(mouse_captured_) {
226  assert(mouse_focus_);
227  if(owner_.fire(event, *mouse_focus_, coordinate)) {
229  }
230  } else {
231  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
232  DBG_GUI_E << LOG_HEADER << "Firing: " << event << ".";
233  if(owner_.fire(event, *mouse_over, coordinate)) {
235  }
236  }
237  }
238 
239  handled = true;
240 }
241 
243 {
244  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_ENTER << ".";
245 
246  assert(mouse_over);
247 
248  mouse_focus_ = mouse_over;
249  owner_.fire(event::MOUSE_ENTER, *mouse_over);
250 
251  hover_shown_ = false;
252  start_hover_timer(mouse_over, get_mouse_position());
253 }
254 
256 {
257  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_MOTION << ".";
258 
259  assert(mouse_over);
260 
261  owner_.fire(event::MOUSE_MOTION, *mouse_over, coordinate);
262 
263  if(hover_timer_) {
264  if((std::abs(hover_position_.x - coordinate.x) > 5) || (std::abs(hover_position_.y - coordinate.y) > 5)) {
266  start_hover_timer(mouse_over, coordinate);
267  }
268  }
269 }
270 
272 {
273  DBG_GUI_E << LOG_HEADER << "Firing: " << event::SHOW_TOOLTIP << ".";
274 
275  if(!hover_widget_) {
276  // See mouse_motion::stop_hover_timer.
277  ERR_GUI_E << LOG_HEADER << event::SHOW_TOOLTIP << " bailing out, no hover widget.";
278  return;
279  }
280 
281  /*
282  * Ignore the result of the event, always mark the tooltip as shown. If
283  * there was no handler, there is no reason to assume there will be one
284  * next time.
285  */
287 
288  hover_shown_ = true;
289 
290  hover_timer_ = 0;
291  hover_widget_ = nullptr;
293 }
294 
296 {
297  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_LEAVE << ".";
298 
299  styled_widget* control = dynamic_cast<styled_widget*>(mouse_focus_);
300  if(!control || control->get_active()) {
302  }
303 
305 
306  mouse_focus_ = nullptr;
307 
309 }
310 
312 {
313  assert(widget);
314 
315 #ifdef __IPHONEOS__
316  // Guessing a crash location in a nasty stack in gui2::execute_timer.
317  // Either this or a long-touch menu.
318  // Remove this when the crash in gui2::execute_timer() and gui2::timer_callback() is gone and try again.
319  return;
320 #endif
321 
323 
325  return;
326  }
327 
328  DBG_GUI_E << LOG_HEADER << "Start hover timer for widget '" << widget->id()
329  << "' at address " << widget << ".";
330 
331  hover_timer_ = add_timer(50, std::bind(&mouse_motion::show_tooltip, this));
332 
333  if(hover_timer_) {
336  } else {
337  ERR_GUI_E << LOG_HEADER << "Failed to add hover timer.";
338  }
339 }
340 
342 {
343  if(hover_timer_) {
344  assert(hover_widget_);
345  DBG_GUI_E << LOG_HEADER << "Stop hover timer for widget '"
346  << hover_widget_->id() << "' at address " << hover_widget_
347  << ".";
348 
349  if(!remove_timer(hover_timer_)) {
350  ERR_GUI_E << LOG_HEADER << "Failed to remove hover timer.";
351  }
352 
353  hover_timer_ = 0;
354  hover_widget_ = nullptr;
356  }
357 }
358 
359 /***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/
360 
361 #undef LOG_HEADER
362 #define LOG_HEADER "distributor mouse button " << I << " [" << owner_.id() << "]: "
363 
364 namespace
365 {
366 struct data_pod
367 {
374 };
375 
376 constexpr std::array mouse_data{
377  data_pod{
384  },
385  data_pod{
392  },
393  data_pod{
400  },
401 };
402 
403 } // namespace
404 
405 template<std::size_t I>
407  : mouse_motion(owner, queue_position)
408  , last_click_stamp_(0)
409  , last_clicked_widget_(nullptr)
410  , focus_(nullptr)
411  , is_down_(false)
412  , signal_handler_sdl_button_down_entered_(false)
413  , signal_handler_sdl_button_up_entered_(false)
414 {
415  static_assert(I < mouse_data.size(), "Out-of-bounds mouse_button template index");
416 
417  owner_.connect_signal<mouse_data[I].sdl_button_down_event>(
419  this,
420  std::placeholders::_2,
421  std::placeholders::_3,
422  std::placeholders::_5),
423  queue_position);
424 
425  owner_.connect_signal<mouse_data[I].sdl_button_up_event>(
427  this,
428  std::placeholders::_2,
429  std::placeholders::_3,
430  std::placeholders::_5),
431  queue_position);
432 }
433 
434 template<std::size_t I>
435 void mouse_button<I>::initialize_state(int32_t button_state)
436 {
437  last_click_stamp_ = 0;
438  last_clicked_widget_ = nullptr;
439  focus_ = 0;
440  // SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, and SDL_BUTTON_RIGHT correspond to 1,2,3
441  is_down_ = button_state & SDL_BUTTON(I + 1);
442 }
443 
444 template<std::size_t I>
446  const event::ui_event event, bool& handled, const point& coordinate)
447 {
448  if(signal_handler_sdl_button_down_entered_) {
449  return;
450  }
451  resource_locker lock{signal_handler_sdl_button_down_entered_};
452 
453  DBG_GUI_E << LOG_HEADER << event << ".";
454 
455  if(is_down_) {
456 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
457  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already down, we missed an event.";
458 #endif
459  return;
460  }
461  is_down_ = true;
462 
463  if(mouse_captured_) {
464  assert(mouse_focus_);
465  focus_ = mouse_focus_;
466  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
467  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
468  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
469  owner_.fire(mouse_data[I].button_down_event, *mouse_focus_);
470  }
471  } else {
472  widget* mouse_over = owner_.find_at(coordinate, true);
473  if(!mouse_over) {
474  return;
475  }
476 
477  if(mouse_over != mouse_focus_) {
478 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
479  WRN_GUI_E << LOG_HEADER << ". Mouse down on non focused widget "
480  << "and mouse not captured, we missed events.";
481 #endif
482  mouse_focus_ = mouse_over;
483  }
484 
485  focus_ = mouse_over;
486  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
487  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
488  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
489  owner_.fire(mouse_data[I].button_down_event, *focus_);
490  }
491  }
492  handled = true;
493 }
494 
495 template<std::size_t I>
497  const event::ui_event event, bool& handled, const point& coordinate)
498 {
499  if(signal_handler_sdl_button_up_entered_) {
500  return;
501  }
502  resource_locker lock{signal_handler_sdl_button_up_entered_};
503 
504  DBG_GUI_E << LOG_HEADER << event << ".";
505 
506  if(!is_down_) {
507 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
508  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already up, we missed an event.";
509 #endif
510  return;
511  }
512  is_down_ = false;
513 
514  if(focus_) {
515  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_up_event << ".";
516  if(!owner_.fire(mouse_data[I].sdl_button_up_event, *focus_, coordinate)) {
517  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_up_event << ".";
518  owner_.fire(mouse_data[I].button_up_event, *focus_);
519  }
520  }
521 
522  // FIXME: The block below is strange diamond inheritance - it's code that could be in
523  // mouse_motion which applies to all three buttons, but it will be run in one of the
524  // three mouse_button<T> subclasses, and then the other two mouse_button<T> subclasses
525  // will reach here with mouse_captured_ == false.
526  widget* mouse_over = owner_.find_at(coordinate, true);
527  if(mouse_captured_) {
528  const unsigned mask = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
529 
530  if((sdl::get_mouse_button_mask() & mask) == 0) {
531  mouse_captured_ = false;
532  }
533 
534  if(mouse_focus_ == mouse_over) {
535  mouse_button_click(mouse_focus_);
536  } else if(!mouse_captured_) {
537  mouse_leave();
538 
539  if(mouse_over) {
540  mouse_enter(mouse_over);
541  }
542  }
543  } else if(focus_ && focus_ == mouse_over) {
544  mouse_button_click(focus_);
545  }
546 
547  focus_ = nullptr;
548  handled = true;
549 }
550 
551 template<std::size_t I>
553 {
554  uint32_t stamp = SDL_GetTicks();
555  if(last_click_stamp_ + settings::double_click_time >= stamp && last_clicked_widget_ == widget) {
556  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_double_click_event << ".";
557 
558  owner_.fire(mouse_data[I].button_double_click_event, *widget);
559  last_click_stamp_ = 0;
560  last_clicked_widget_ = nullptr;
561 
562  } else {
563  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_click_event << ".";
564  owner_.fire(mouse_data[I].button_click_event, *widget);
565  last_click_stamp_ = stamp;
566  last_clicked_widget_ = widget;
567  }
568 }
569 
570 /***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
571 
572 #undef LOG_HEADER
573 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
574 
575 /**
576  * @todo Test whether the state is properly tracked when an input blocker is
577  * used.
578  */
580  : mouse_motion(owner, queue_position)
581  , mouse_button_left(owner, queue_position)
582  , mouse_button_middle(owner, queue_position)
583  , mouse_button_right(owner, queue_position)
584  , keyboard_focus_(nullptr)
585  , keyboard_focus_chain_()
586 {
587  if(SDL_WasInit(SDL_INIT_TIMER) == 0) {
588  if(SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
589  assert(false);
590  }
591  }
592 
595  this,
596  std::placeholders::_5,
597  std::placeholders::_6,
598  std::placeholders::_7
599  ));
600 
603  this,
604  std::placeholders::_5,
605  std::placeholders::_6,
606  std::placeholders::_7
607  ));
608 
611  this,
612  std::placeholders::_5,
613  std::placeholders::_6,
614  std::placeholders::_7
615  ));
616 
619  this,
620  std::placeholders::_1,
621  std::placeholders::_2
622  ));
623 
625 }
626 
628 {
631  this,
632  std::placeholders::_5,
633  std::placeholders::_6,
634  std::placeholders::_7
635  ));
636 
639  this,
640  std::placeholders::_5,
641  std::placeholders::_6,
642  std::placeholders::_7
643  ));
644 
647  this,
648  std::placeholders::_5,
649  std::placeholders::_6,
650  std::placeholders::_7
651  ));
652 
655  this,
656  std::placeholders::_1,
657  std::placeholders::_2
658  ));
659 }
660 
662 {
663  const uint32_t button_state = sdl::get_mouse_button_mask();
664 
668 
670 }
671 
673 {
674  return keyboard_focus_;
675 }
676 
678 {
679  if(keyboard_focus_) {
680  DBG_GUI_E << LOG_HEADER << "Firing: " << event::LOSE_KEYBOARD_FOCUS << ".";
682  }
683 
685 
686  if(keyboard_focus_) {
687  DBG_GUI_E << LOG_HEADER << "Firing: " << event::RECEIVE_KEYBOARD_FOCUS << ".";
689  }
690 }
691 
693 {
694  assert(widget);
695  assert(std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), widget) == keyboard_focus_chain_.end());
696  keyboard_focus_chain_.push_back(widget);
697 }
698 
700 {
701  assert(w);
702  auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), w);
703 
704  if(itor != keyboard_focus_chain_.end()) {
705  keyboard_focus_chain_.erase(itor);
706  }
707 }
708 
709 template<typename Fcn, typename P1, typename P2, typename P3>
711 {
712  /** @todo Test whether recursion protection is needed. */
713 
714  DBG_GUI_E << LOG_HEADER << evt << ".";
715 
716  if(keyboard_focus_) {
717  // Attempt to cast to styled_widget, to avoid sending events if the
718  // widget is disabled. If the cast fails, we assume the widget
719  // is enabled and ready to receive events.
720  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
721  if(!control || control->get_active()) {
722  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
723  if(owner_.fire(evt, *keyboard_focus_, p1, p2, p3)) {
724  return;
725  }
726  }
727 
728  if(text_box_base* tb = dynamic_cast<text_box_base*>(keyboard_focus_)) {
729  if(tb->is_composing()) {
730  return; // Skip the keyboard chain if composition is in progress.
731  }
732  }
733  }
734 
735  for(auto ritor = keyboard_focus_chain_.rbegin(); ritor != keyboard_focus_chain_.rend(); ++ritor) {
736  if(*ritor == keyboard_focus_) {
737  continue;
738  }
739 
740  if(*ritor == &owner_) {
741  /**
742  * @todo Make sure we're not in the event chain.
743  *
744  * No idea why we're here, but needs to be fixed, otherwise we keep
745  * calling this function recursively upon unhandled events...
746  *
747  * Probably added to make sure the window can grab the events and
748  * handle + block them when needed, this is no longer needed with
749  * the chain.
750  */
751  continue;
752  }
753 
754  // Attempt to cast to styled_widget, to avoid sending events if the
755  // widget is disabled. If the cast fails, we assume the widget
756  // is enabled and ready to receive events.
757  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
758  if(control != nullptr && !control->get_active()) {
759  continue;
760  }
761 
762  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
763  if(owner_.fire(evt, **ritor, p1, p2, p3)) {
764  return;
765  }
766  }
767 }
768 
769 void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const std::string& unicode)
770 {
771  signal_handler_keyboard_internal<signal_keyboard>(event::SDL_KEY_DOWN, key, modifier, unicode);
772 }
773 
774 void distributor::signal_handler_sdl_text_input(const std::string& unicode, int32_t start, int32_t end)
775 {
776  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_INPUT, unicode, start, end);
777 }
778 
779 void distributor::signal_handler_sdl_text_editing(const std::string& unicode, int32_t start, int32_t end)
780 {
781  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_EDITING, unicode, start, end);
782 }
783 
785 {
786  DBG_GUI_E << LOG_HEADER << event << ".";
787 
788  /**
789  * @todo Evaluate whether moving the cleanup parts in the subclasses.
790  *
791  * It might be cleaner to do it that way, but creates extra small
792  * functions...
793  */
794 
795  if(hover_widget_ == &w) {
797  }
798 
801  }
802  if(mouse_button_left::focus_ == &w) {
803  mouse_button_left::focus_ = nullptr;
804  }
805 
808  }
810  mouse_button_middle::focus_ = nullptr;
811  }
812 
815  }
816  if(mouse_button_right::focus_ == &w) {
817  mouse_button_right::focus_ = nullptr;
818  }
819 
820  if(mouse_focus_ == &w) {
821  mouse_focus_ = nullptr;
822  }
823 
824  if(keyboard_focus_ == &w) {
825  keyboard_focus_ = nullptr;
826  }
827 
828  const auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), &w);
829  if(itor != keyboard_focus_chain_.end()) {
830  keyboard_focus_chain_.erase(itor);
831  }
832 }
833 
834 } // namespace gui2::event
Base class for event handling.
Definition: dispatcher.hpp:150
void connect_signal(const F &func, const queue_position position=back_child)
Adds a callback to the appropriate queue based on event type.
Definition: dispatcher.hpp:351
queue_position
The position where to add a new callback in the signal handler.
Definition: dispatcher.hpp:331
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
void disconnect_signal(const F &func, const queue_position position=back_child)
Removes a callback from the appropriate queue based on event type.
Definition: dispatcher.hpp:370
widget * keyboard_focus_
The widget that holds the keyboard focus_.
void keyboard_add_to_chain(widget *widget)
Adds the widget to the keyboard chain.
void keyboard_capture(widget *widget)
Captures the keyboard input.
std::vector< widget * > keyboard_focus_chain_
Fall back keyboard focus_ items.
void signal_handler_keyboard_internal(event::ui_event evt, P1 &&p1, P2 &&p2, P3 &&p3)
void keyboard_remove_from_chain(widget *widget)
Remove the widget from the keyboard chain.
void signal_handler_sdl_text_editing(const std::string &unicode, int32_t start, int32_t len)
void signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const std::string &unicode)
Set of functions that handle certain events and sends them to the proper widget.
void initialize_state()
Initializes the state of the keyboard and mouse.
distributor(widget &owner, const dispatcher::queue_position queue_position)
widget * keyboard_focus() const
Return the widget currently capturing keyboard input.
void signal_handler_notify_removal(dispatcher &widget, const ui_event event)
void signal_handler_sdl_text_input(const std::string &unicode, int32_t start, int32_t len)
void signal_handler_sdl_button_down(const event::ui_event event, bool &handled, const point &coordinate)
widget * last_clicked_widget_
The widget the last click was on, used for double clicking.
mouse_button(widget &owner, const dispatcher::queue_position queue_position)
void initialize_state(int32_t button_state)
Initializes the state of the button.
void mouse_button_click(widget *widget)
void signal_handler_sdl_button_up(const event::ui_event event, bool &handled, const point &coordinate)
widget * focus_
If the mouse isn't captured we need to verify the up is on the same widget as the down so we send a p...
bool mouse_captured_
Did the current widget capture the focus_?
Definition: distributor.hpp:78
void mouse_hover(widget *mouse_over, const point &coordinate)
Called when the mouse moves over a widget.
widget * hover_widget_
The widget which should get the hover event.
Definition: distributor.hpp:87
void signal_handler_sdl_mouse_motion(const event::ui_event event, bool &handled, const point &coordinate)
mouse_motion(widget &owner, const dispatcher::queue_position queue_position)
Definition: distributor.cpp:67
void mouse_enter(widget *mouse_over)
Called when the mouse enters a widget.
bool hover_shown_
Has the hover been shown for the widget?
Definition: distributor.hpp:99
void signal_handler_sdl_touch_motion(const event::ui_event event, bool &handled, const point &coordinate, const point &distance)
void capture_mouse(const bool capture=true)
Captures the mouse input.
void signal_handler_show_helptip(const event::ui_event event, bool &handled, const point &coordinate)
void mouse_leave()
Called when the mouse leaves the current widget.
void signal_handler_sdl_wheel(const event::ui_event event, bool &handled, const point &coordinate)
point hover_position_
The anchor point of the hover event.
Definition: distributor.hpp:90
void stop_hover_timer()
Stops the current hover timer.
void start_hover_timer(widget *widget, const point &coordinate)
Starts the hover timer.
std::size_t hover_timer_
The timer for the hover event.
Definition: distributor.hpp:84
widget * mouse_focus_
The widget that currently has the mouse focus_.
Definition: distributor.hpp:75
widget & owner_
The widget that owns us.
Definition: distributor.hpp:81
void show_tooltip()
Called when the mouse wants the widget to show its tooltip.
Small helper to keep a resource (boolean) locked.
Definition: distributor.cpp:44
bool wants_mouse_hover() const
Base class for all visible items.
virtual bool get_active() const =0
Gets the active state of the styled_widget.
Abstract base class for text items.
Base class for all widgets.
Definition: widget.hpp:53
virtual bool can_mouse_focus() const
Whether the mouse move/click event go 'through' this widget.
Definition: widget.hpp:331
const std::string & id() const
Definition: widget.cpp:110
virtual widget * find_at(const point &coordinate, const bool must_be_active)
Returns the widget at the wanted coordinates.
Definition: widget.cpp:544
widget * parent()
Definition: widget.cpp:160
const ui_event button_click_event
const ui_event button_double_click_event
#define LOG_HEADER
const ui_event sdl_button_up_event
const ui_event button_up_event
const ui_event sdl_button_down_event
const ui_event button_down_event
Contains the event distributor.
int w
Define the common log macros for the gui toolkit.
#define WRN_GUI_E
Definition: log.hpp:37
#define ERR_GUI_E
Definition: log.hpp:38
#define DBG_GUI_E
Definition: log.hpp:35
Contains functions for cleanly handling SDL input.
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ MIDDLE_BUTTON_DOWN
Definition: handler.hpp:124
@ NOTIFY_REMOVE_TOOLTIP
Definition: handler.hpp:159
@ RIGHT_BUTTON_UP
Definition: handler.hpp:129
@ SDL_RIGHT_BUTTON_DOWN
Definition: handler.hpp:140
@ MIDDLE_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:127
@ MIDDLE_BUTTON_CLICK
Definition: handler.hpp:126
@ SDL_LEFT_BUTTON_DOWN
Definition: handler.hpp:136
@ SDL_TEXT_INPUT
An SDL text input (commit) event.
Definition: handler.hpp:153
@ SDL_WHEEL_DOWN
Definition: handler.hpp:145
@ LEFT_BUTTON_UP
Definition: handler.hpp:121
@ MIDDLE_BUTTON_UP
Definition: handler.hpp:125
@ RIGHT_BUTTON_CLICK
Definition: handler.hpp:130
@ LOSE_KEYBOARD_FOCUS
Definition: handler.hpp:161
@ SDL_MOUSE_MOTION
Definition: handler.hpp:134
@ SDL_TEXT_EDITING
An SDL text editing (IME) event.
Definition: handler.hpp:154
@ LEFT_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:123
@ RIGHT_BUTTON_DOWN
Definition: handler.hpp:128
@ NOTIFY_REMOVAL
Definition: handler.hpp:157
@ RECEIVE_KEYBOARD_FOCUS
Definition: handler.hpp:160
@ SDL_TOUCH_MOTION
Definition: handler.hpp:167
@ SDL_WHEEL_RIGHT
Definition: handler.hpp:143
@ LEFT_BUTTON_CLICK
Definition: handler.hpp:122
@ SDL_WHEEL_LEFT
Definition: handler.hpp:142
@ SDL_LEFT_BUTTON_UP
Definition: handler.hpp:137
@ SDL_MIDDLE_BUTTON_UP
Definition: handler.hpp:139
@ LEFT_BUTTON_DOWN
Definition: handler.hpp:120
@ SDL_MIDDLE_BUTTON_DOWN
Definition: handler.hpp:138
@ SDL_RIGHT_BUTTON_UP
Definition: handler.hpp:141
@ RIGHT_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:131
void init_mouse_location()
Initializes the location of the mouse.
Definition: handler.cpp:880
unsigned double_click_time
Definition: settings.cpp:45
std::size_t add_timer(const uint32_t interval, const std::function< void(std::size_t id)> &callback, const bool repeat)
Adds a new timer.
Definition: timer.cpp:127
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:112
bool remove_timer(const std::size_t id)
Removes a timer.
Definition: timer.cpp:168
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
Definition: input.cpp:49
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
This file contains the settings handling of the widget library.
Holds a 2D point.
Definition: point.hpp:25
Contains the gui2 timer routines.