The Battle for Wesnoth  1.19.5+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  using namespace std::chrono_literals;
332  hover_timer_ = add_timer(50ms, std::bind(&mouse_motion::show_tooltip, this));
333 
334  if(hover_timer_) {
337  } else {
338  ERR_GUI_E << LOG_HEADER << "Failed to add hover timer.";
339  }
340 }
341 
343 {
344  if(hover_timer_) {
345  assert(hover_widget_);
346  DBG_GUI_E << LOG_HEADER << "Stop hover timer for widget '"
347  << hover_widget_->id() << "' at address " << hover_widget_
348  << ".";
349 
350  if(!remove_timer(hover_timer_)) {
351  ERR_GUI_E << LOG_HEADER << "Failed to remove hover timer.";
352  }
353 
354  hover_timer_ = 0;
355  hover_widget_ = nullptr;
357  }
358 }
359 
360 /***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/
361 
362 #undef LOG_HEADER
363 #define LOG_HEADER "distributor mouse button " << I << " [" << owner_.id() << "]: "
364 
365 namespace
366 {
367 struct data_pod
368 {
375 };
376 
377 constexpr std::array mouse_data{
378  data_pod{
385  },
386  data_pod{
393  },
394  data_pod{
401  },
402 };
403 
404 } // namespace
405 
406 template<std::size_t I>
408  : mouse_motion(owner, queue_position)
409  , last_click_stamp_()
410  , last_clicked_widget_(nullptr)
411  , focus_(nullptr)
412  , is_down_(false)
413  , signal_handler_sdl_button_down_entered_(false)
414  , signal_handler_sdl_button_up_entered_(false)
415 {
416  static_assert(I < mouse_data.size(), "Out-of-bounds mouse_button template index");
417 
418  owner_.connect_signal<mouse_data[I].sdl_button_down_event>(
420  this,
421  std::placeholders::_2,
422  std::placeholders::_3,
423  std::placeholders::_5),
424  queue_position);
425 
426  owner_.connect_signal<mouse_data[I].sdl_button_up_event>(
428  this,
429  std::placeholders::_2,
430  std::placeholders::_3,
431  std::placeholders::_5),
432  queue_position);
433 }
434 
435 template<std::size_t I>
436 void mouse_button<I>::initialize_state(int32_t button_state)
437 {
438  last_click_stamp_ = {};
439  last_clicked_widget_ = nullptr;
440  focus_ = 0;
441  // SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, and SDL_BUTTON_RIGHT correspond to 1,2,3
442  is_down_ = button_state & SDL_BUTTON(I + 1);
443 }
444 
445 template<std::size_t I>
447  const event::ui_event event, bool& handled, const point& coordinate)
448 {
449  if(signal_handler_sdl_button_down_entered_) {
450  return;
451  }
452  resource_locker lock{signal_handler_sdl_button_down_entered_};
453 
454  DBG_GUI_E << LOG_HEADER << event << ".";
455 
456  if(is_down_) {
457 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
458  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already down, we missed an event.";
459 #endif
460  return;
461  }
462  is_down_ = true;
463 
464  if(mouse_captured_) {
465  assert(mouse_focus_);
466  focus_ = mouse_focus_;
467  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
468  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
469  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
470  owner_.fire(mouse_data[I].button_down_event, *mouse_focus_);
471  }
472  } else {
473  widget* mouse_over = owner_.find_at(coordinate, true);
474  if(!mouse_over) {
475  return;
476  }
477 
478  if(mouse_over != mouse_focus_) {
479 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
480  WRN_GUI_E << LOG_HEADER << ". Mouse down on non focused widget "
481  << "and mouse not captured, we missed events.";
482 #endif
483  mouse_focus_ = mouse_over;
484  }
485 
486  focus_ = mouse_over;
487  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
488  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
489  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
490  owner_.fire(mouse_data[I].button_down_event, *focus_);
491  }
492  }
493  handled = true;
494 }
495 
496 template<std::size_t I>
498  const event::ui_event event, bool& handled, const point& coordinate)
499 {
500  if(signal_handler_sdl_button_up_entered_) {
501  return;
502  }
503  resource_locker lock{signal_handler_sdl_button_up_entered_};
504 
505  DBG_GUI_E << LOG_HEADER << event << ".";
506 
507  if(!is_down_) {
508 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
509  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already up, we missed an event.";
510 #endif
511  return;
512  }
513  is_down_ = false;
514 
515  if(focus_) {
516  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_up_event << ".";
517  if(!owner_.fire(mouse_data[I].sdl_button_up_event, *focus_, coordinate)) {
518  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_up_event << ".";
519  owner_.fire(mouse_data[I].button_up_event, *focus_);
520  }
521  }
522 
523  // FIXME: The block below is strange diamond inheritance - it's code that could be in
524  // mouse_motion which applies to all three buttons, but it will be run in one of the
525  // three mouse_button<T> subclasses, and then the other two mouse_button<T> subclasses
526  // will reach here with mouse_captured_ == false.
527  widget* mouse_over = owner_.find_at(coordinate, true);
528  if(mouse_captured_) {
529  const unsigned mask = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
530 
531  if((sdl::get_mouse_button_mask() & mask) == 0) {
532  mouse_captured_ = false;
533  }
534 
535  if(mouse_focus_ == mouse_over) {
536  mouse_button_click(mouse_focus_);
537  } else if(!mouse_captured_) {
538  mouse_leave();
539 
540  if(mouse_over) {
541  mouse_enter(mouse_over);
542  }
543  }
544  } else if(focus_ && focus_ == mouse_over) {
545  mouse_button_click(focus_);
546  }
547 
548  focus_ = nullptr;
549  handled = true;
550 }
551 
552 template<std::size_t I>
554 {
555  auto stamp = std::chrono::steady_clock::now();
556  if(last_click_stamp_ + settings::double_click_time >= stamp && last_clicked_widget_ == widget) {
557  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_double_click_event << ".";
558 
559  owner_.fire(mouse_data[I].button_double_click_event, *widget);
560  last_click_stamp_ = {};
561  last_clicked_widget_ = nullptr;
562 
563  } else {
564  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_click_event << ".";
565  owner_.fire(mouse_data[I].button_click_event, *widget);
566  last_click_stamp_ = stamp;
567  last_clicked_widget_ = widget;
568  }
569 }
570 
571 /***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
572 
573 #undef LOG_HEADER
574 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
575 
576 /**
577  * @todo Test whether the state is properly tracked when an input blocker is
578  * used.
579  */
581  : mouse_motion(owner, queue_position)
582  , mouse_button_left(owner, queue_position)
583  , mouse_button_middle(owner, queue_position)
584  , mouse_button_right(owner, queue_position)
585  , keyboard_focus_(nullptr)
586  , keyboard_focus_chain_()
587 {
588  if(SDL_WasInit(SDL_INIT_TIMER) == 0) {
589  if(SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
590  assert(false);
591  }
592  }
593 
596  this,
597  std::placeholders::_5,
598  std::placeholders::_6,
599  std::placeholders::_7
600  ));
601 
604  this,
605  std::placeholders::_5,
606  std::placeholders::_6,
607  std::placeholders::_7
608  ));
609 
612  this,
613  std::placeholders::_5,
614  std::placeholders::_6,
615  std::placeholders::_7
616  ));
617 
620  this,
621  std::placeholders::_1,
622  std::placeholders::_2
623  ));
624 
626 }
627 
629 {
632  this,
633  std::placeholders::_5,
634  std::placeholders::_6,
635  std::placeholders::_7
636  ));
637 
640  this,
641  std::placeholders::_5,
642  std::placeholders::_6,
643  std::placeholders::_7
644  ));
645 
648  this,
649  std::placeholders::_5,
650  std::placeholders::_6,
651  std::placeholders::_7
652  ));
653 
656  this,
657  std::placeholders::_1,
658  std::placeholders::_2
659  ));
660 }
661 
663 {
664  const uint32_t button_state = sdl::get_mouse_button_mask();
665 
669 
671 }
672 
674 {
675  return keyboard_focus_;
676 }
677 
679 {
680  if(keyboard_focus_) {
681  DBG_GUI_E << LOG_HEADER << "Firing: " << event::LOSE_KEYBOARD_FOCUS << ".";
683  }
684 
686 
687  if(keyboard_focus_) {
688  DBG_GUI_E << LOG_HEADER << "Firing: " << event::RECEIVE_KEYBOARD_FOCUS << ".";
690  }
691 }
692 
694 {
695  assert(widget);
696  assert(std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), widget) == keyboard_focus_chain_.end());
697  keyboard_focus_chain_.push_back(widget);
698 }
699 
701 {
702  assert(w);
703  auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), w);
704 
705  if(itor != keyboard_focus_chain_.end()) {
706  keyboard_focus_chain_.erase(itor);
707  }
708 }
709 
710 template<typename Fcn, typename P1, typename P2, typename P3>
712 {
713  /** @todo Test whether recursion protection is needed. */
714 
715  DBG_GUI_E << LOG_HEADER << evt << ".";
716 
717  if(keyboard_focus_) {
718  // Attempt to cast to styled_widget, to avoid sending events if the
719  // widget is disabled. If the cast fails, we assume the widget
720  // is enabled and ready to receive events.
721  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
722  if(!control || control->get_active()) {
723  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
724  if(owner_.fire(evt, *keyboard_focus_, p1, p2, p3)) {
725  return;
726  }
727  }
728 
729  if(text_box_base* tb = dynamic_cast<text_box_base*>(keyboard_focus_)) {
730  if(tb->is_composing()) {
731  return; // Skip the keyboard chain if composition is in progress.
732  }
733  }
734  }
735 
736  for(auto ritor = keyboard_focus_chain_.rbegin(); ritor != keyboard_focus_chain_.rend(); ++ritor) {
737  if(*ritor == keyboard_focus_) {
738  continue;
739  }
740 
741  if(*ritor == &owner_) {
742  /**
743  * @todo Make sure we're not in the event chain.
744  *
745  * No idea why we're here, but needs to be fixed, otherwise we keep
746  * calling this function recursively upon unhandled events...
747  *
748  * Probably added to make sure the window can grab the events and
749  * handle + block them when needed, this is no longer needed with
750  * the chain.
751  */
752  continue;
753  }
754 
755  // Attempt to cast to styled_widget, to avoid sending events if the
756  // widget is disabled. If the cast fails, we assume the widget
757  // is enabled and ready to receive events.
758  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
759  if(control != nullptr && !control->get_active()) {
760  continue;
761  }
762 
763  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
764  if(owner_.fire(evt, **ritor, p1, p2, p3)) {
765  return;
766  }
767  }
768 }
769 
770 void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const std::string& unicode)
771 {
772  signal_handler_keyboard_internal<signal_keyboard>(event::SDL_KEY_DOWN, key, modifier, unicode);
773 }
774 
775 void distributor::signal_handler_sdl_text_input(const std::string& unicode, int32_t start, int32_t end)
776 {
777  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_INPUT, unicode, start, end);
778 }
779 
780 void distributor::signal_handler_sdl_text_editing(const std::string& unicode, int32_t start, int32_t end)
781 {
782  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_EDITING, unicode, start, end);
783 }
784 
786 {
787  DBG_GUI_E << LOG_HEADER << event << ".";
788 
789  /**
790  * @todo Evaluate whether moving the cleanup parts in the subclasses.
791  *
792  * It might be cleaner to do it that way, but creates extra small
793  * functions...
794  */
795 
796  if(hover_widget_ == &w) {
798  }
799 
802  }
803  if(mouse_button_left::focus_ == &w) {
804  mouse_button_left::focus_ = nullptr;
805  }
806 
809  }
811  mouse_button_middle::focus_ = nullptr;
812  }
813 
816  }
817  if(mouse_button_right::focus_ == &w) {
818  mouse_button_right::focus_ = nullptr;
819  }
820 
821  if(mouse_focus_ == &w) {
822  mouse_focus_ = nullptr;
823  }
824 
825  if(keyboard_focus_ == &w) {
826  keyboard_focus_ = nullptr;
827  }
828 
829  const auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), &w);
830  if(itor != keyboard_focus_chain_.end()) {
831  keyboard_focus_chain_.erase(itor);
832  }
833 }
834 
835 } // 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:79
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:88
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?
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:91
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:85
widget * mouse_focus_
The widget that currently has the mouse focus_.
Definition: distributor.hpp:76
widget & owner_
The widget that owns us.
Definition: distributor.hpp:82
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
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:55
virtual bool can_mouse_focus() const
Whether the mouse move/click event go 'through' this widget.
Definition: widget.hpp:334
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:549
widget * parent()
Definition: widget.cpp:170
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:879
std::chrono::milliseconds double_click_time
Definition: settings.cpp:45
std::size_t add_timer(const std::chrono::milliseconds &interval, const std::function< void(std::size_t id)> &callback, const bool repeat)
Adds a new timer.
Definition: timer.cpp:123
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:143
bool remove_timer(const std::size_t id)
Removes a timer.
Definition: timer.cpp:164
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.