The Battle for Wesnoth  1.19.13+dev
distributor.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2025
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 <SDL2/SDL.h>
28 
29 #include <array>
30 #include <functional>
31 
32 namespace gui2::event
33 {
34 /**
35  * Small helper to keep a resource (boolean) locked.
36  *
37  * Some of the event handling routines can't be called recursively, this due to
38  * the fact that they are attached to the pre queue and when the forward an
39  * event the pre queue event gets triggered recursively causing infinite
40  * recursion.
41  *
42  * To prevent that those functions check the lock and exit when the lock is
43  * held otherwise grab the lock here.
44  */
46 {
47 public:
48  resource_locker(bool& locked) : locked_(locked)
49  {
50  assert(!locked_);
51  locked_ = true;
52  }
53 
55  {
56  assert(locked_);
57  locked_ = false;
58  }
59 
60 private:
61  bool& locked_;
62 };
63 
64 
65 /***** ***** ***** ***** mouse_motion ***** ***** ***** ***** *****/
66 
67 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
68 
70  : mouse_focus_(nullptr)
71  , mouse_captured_(false)
72  , owner_(owner)
73  , hover_timer_(0)
74  , hover_widget_(nullptr)
75  , hover_position_(0, 0)
76  , hover_shown_(true)
77  , signal_handler_sdl_mouse_motion_entered_(false)
78 {
81  this,
82  std::placeholders::_2,
83  std::placeholders::_3,
84  std::placeholders::_5),
85  queue_position);
86 
89  this,
90  std::placeholders::_2,
91  std::placeholders::_3,
92  std::placeholders::_5,
93  std::placeholders::_6),
94  queue_position);
95 
98  this,
99  std::placeholders::_2,
100  std::placeholders::_3,
101  std::placeholders::_5
102  ));
103 
106  this,
107  std::placeholders::_2,
108  std::placeholders::_3,
109  std::placeholders::_5
110  ));
111 
114  this,
115  std::placeholders::_2,
116  std::placeholders::_3,
117  std::placeholders::_5
118  ));
119 
122  this,
123  std::placeholders::_2,
124  std::placeholders::_3,
125  std::placeholders::_5
126  ));
127 
130  this,
131  std::placeholders::_2,
132  std::placeholders::_3,
133  std::placeholders::_5),
134  queue_position);
135 }
136 
138 {
140 }
141 
142 void mouse_motion::capture_mouse(const bool capture)
143 {
144  assert(mouse_focus_);
145  mouse_captured_ = capture;
146 }
147 
149 {
151  return;
152  }
154 
155  DBG_GUI_E << LOG_HEADER << event << ".";
156 
157  if(mouse_captured_) {
158  assert(mouse_focus_);
159  if(!owner_.fire(event, *mouse_focus_, coordinate)) {
161  }
162  } else {
163  widget* mouse_over = owner_.find_at(coordinate, true);
164  while(mouse_over && !mouse_over->can_mouse_focus() && mouse_over->parent()) {
165  mouse_over = mouse_over->parent();
166  }
167 
168  if(mouse_over) {
169  DBG_GUI_E << LOG_HEADER << "Firing: " << event << ".";
170  if(owner_.fire(event, *mouse_over, coordinate)) {
171  return;
172  }
173  }
174 
175  if(!mouse_focus_ && mouse_over) {
176  mouse_enter(mouse_over);
177  } else if(mouse_focus_ && !mouse_over) {
178  mouse_leave();
179  } else if(mouse_focus_ && mouse_focus_ == mouse_over) {
180  mouse_hover(mouse_over, coordinate);
181  } else if(mouse_focus_ && mouse_over) {
182  // moved from one widget to the next
183  mouse_leave();
184  mouse_enter(mouse_over);
185  } else {
186  assert(!mouse_focus_ && !mouse_over);
187  }
188  }
189  handled = true;
190 }
191 
193  const event::ui_event event, bool& handled, const point& coordinate, const point& distance)
194 {
195  DBG_GUI_E << LOG_HEADER << event << ".";
196 
197  if(mouse_captured_) {
198  assert(mouse_focus_);
199  owner_.fire(event, *mouse_focus_, coordinate, distance);
200  } else {
201  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
202  owner_.fire(event, *mouse_over, coordinate, distance);
203  }
204  }
205  handled = true;
206 }
207 
209 {
210  DBG_GUI_E << LOG_HEADER << event << ".";
211 
212  if(mouse_captured_) {
213  assert(mouse_focus_);
215  } else {
216  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
217  owner_.fire(event, *mouse_over, coordinate);
218  }
219  }
220  handled = true;
221 }
222 
224 {
225  DBG_GUI_E << LOG_HEADER << event << ".";
226 
227  if(mouse_captured_) {
228  assert(mouse_focus_);
229  if(owner_.fire(event, *mouse_focus_, coordinate)) {
231  }
232  } else {
233  if(widget* mouse_over = owner_.find_at(coordinate, true)) {
234  DBG_GUI_E << LOG_HEADER << "Firing: " << event << ".";
235  if(owner_.fire(event, *mouse_over, coordinate)) {
237  }
238  }
239  }
240 
241  handled = true;
242 }
243 
245 {
246  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_ENTER << ".";
247 
248  assert(mouse_over);
249 
250  mouse_focus_ = mouse_over;
251  owner_.fire(event::MOUSE_ENTER, *mouse_over);
252 
253  hover_shown_ = false;
254  start_hover_timer(mouse_over, get_mouse_position());
255 }
256 
258 {
259  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_MOTION << ".";
260 
261  assert(mouse_over);
262 
263  owner_.fire(event::MOUSE_MOTION, *mouse_over, coordinate);
264 
265  if(hover_timer_) {
266  if((std::abs(hover_position_.x - coordinate.x) > 5) || (std::abs(hover_position_.y - coordinate.y) > 5)) {
268  start_hover_timer(mouse_over, coordinate);
269  }
270  }
271 }
272 
274 {
275  DBG_GUI_E << LOG_HEADER << "Firing: " << event::SHOW_TOOLTIP << ".";
276 
277  if(!hover_widget_) {
278  // See mouse_motion::stop_hover_timer.
279  ERR_GUI_E << LOG_HEADER << event::SHOW_TOOLTIP << " bailing out, no hover widget.";
280  return;
281  }
282 
283  /*
284  * Ignore the result of the event, always mark the tooltip as shown. If
285  * there was no handler, there is no reason to assume there will be one
286  * next time.
287  */
289 
290  hover_shown_ = true;
291 
292  hover_timer_ = 0;
293  hover_widget_ = nullptr;
295 }
296 
298 {
299  DBG_GUI_E << LOG_HEADER << "Firing: " << event::MOUSE_LEAVE << ".";
300 
301  styled_widget* control = dynamic_cast<styled_widget*>(mouse_focus_);
302  if(!control || control->get_active()) {
304  }
305 
307 
308  mouse_focus_ = nullptr;
309 
311 }
312 
314 {
315  assert(widget);
316 
317 #ifdef __IPHONEOS__
318  // Guessing a crash location in a nasty stack in gui2::execute_timer.
319  // Either this or a long-touch menu.
320  // Remove this when the crash in gui2::execute_timer() and gui2::timer_callback() is gone and try again.
321  return;
322 #endif
323 
325 
327  return;
328  }
329 
330  DBG_GUI_E << LOG_HEADER << "Start hover timer for widget '" << widget->id()
331  << "' at address " << widget << ".";
332 
333  using namespace std::chrono_literals;
334  hover_timer_ = add_timer(50ms, std::bind(&mouse_motion::show_tooltip, this));
335 
336  if(hover_timer_) {
339  } else {
340  ERR_GUI_E << LOG_HEADER << "Failed to add hover timer.";
341  }
342 }
343 
345 {
346  if(hover_timer_) {
347  assert(hover_widget_);
348  DBG_GUI_E << LOG_HEADER << "Stop hover timer for widget '"
349  << hover_widget_->id() << "' at address " << hover_widget_
350  << ".";
351 
352  if(!remove_timer(hover_timer_)) {
353  ERR_GUI_E << LOG_HEADER << "Failed to remove hover timer.";
354  }
355 
356  hover_timer_ = 0;
357  hover_widget_ = nullptr;
359  }
360 }
361 
362 /***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/
363 
364 #undef LOG_HEADER
365 #define LOG_HEADER "distributor mouse button " << I << " [" << owner_.id() << "]: "
366 
367 namespace
368 {
369 struct data_pod
370 {
377 };
378 
379 constexpr std::array mouse_data{
380  data_pod{
387  },
388  data_pod{
395  },
396  data_pod{
403  },
404  data_pod{
411  },
412  data_pod{
419  },
420 };
421 
422 } // namespace
423 
424 template<std::size_t I>
426  : mouse_motion(owner, queue_position)
427  , last_click_stamp_()
428  , last_clicked_widget_(nullptr)
429  , focus_(nullptr)
430  , is_down_(false)
431  , signal_handler_sdl_button_down_entered_(false)
432  , signal_handler_sdl_button_up_entered_(false)
433 {
434  static_assert(I < mouse_data.size(), "Out-of-bounds mouse_button template index");
435 
436  owner_.connect_signal<mouse_data[I].sdl_button_down_event>(
438  this,
439  std::placeholders::_2,
440  std::placeholders::_3,
441  std::placeholders::_5),
442  queue_position);
443 
444  owner_.connect_signal<mouse_data[I].sdl_button_up_event>(
446  this,
447  std::placeholders::_2,
448  std::placeholders::_3,
449  std::placeholders::_5),
450  queue_position);
451 }
452 
453 template<std::size_t I>
454 void mouse_button<I>::initialize_state(int32_t button_state)
455 {
456  last_click_stamp_ = {};
457  last_clicked_widget_ = nullptr;
458  focus_ = nullptr;
459  // SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, and SDL_BUTTON_RIGHT correspond to 1,2,3
460  is_down_ = button_state & SDL_BUTTON(I + 1);
461 }
462 
463 template<std::size_t I>
465  const event::ui_event event, bool& handled, const point& coordinate)
466 {
467  if(signal_handler_sdl_button_down_entered_) {
468  return;
469  }
470  resource_locker lock{signal_handler_sdl_button_down_entered_};
471 
472  DBG_GUI_E << LOG_HEADER << event << ".";
473 
474  if(is_down_) {
475 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
476  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already down, we missed an event.";
477 #endif
478  return;
479  }
480  is_down_ = true;
481 
482  if(mouse_captured_) {
483  assert(mouse_focus_);
484  focus_ = mouse_focus_;
485  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
486  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
487  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
488  owner_.fire(mouse_data[I].button_down_event, *mouse_focus_);
489  }
490  } else {
491  widget* mouse_over = owner_.find_at(coordinate, true);
492  if(!mouse_over) {
493  return;
494  }
495 
496  if(mouse_over != mouse_focus_) {
497 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
498  WRN_GUI_E << LOG_HEADER << ". Mouse down on non focused widget "
499  << "and mouse not captured, we missed events.";
500 #endif
501  mouse_focus_ = mouse_over;
502  }
503 
504  focus_ = mouse_over;
505  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
506  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
507  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
508  owner_.fire(mouse_data[I].button_down_event, *focus_);
509  }
510  }
511  handled = true;
512 }
513 
514 template<std::size_t I>
516  const event::ui_event event, bool& handled, const point& coordinate)
517 {
518  if(signal_handler_sdl_button_up_entered_) {
519  return;
520  }
521  resource_locker lock{signal_handler_sdl_button_up_entered_};
522 
523  DBG_GUI_E << LOG_HEADER << event << ".";
524 
525  if(!is_down_) {
526 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
527  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already up, we missed an event.";
528 #endif
529  return;
530  }
531  is_down_ = false;
532 
533  if(focus_) {
534  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_up_event << ".";
535  if(!owner_.fire(mouse_data[I].sdl_button_up_event, *focus_, coordinate)) {
536  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_up_event << ".";
537  owner_.fire(mouse_data[I].button_up_event, *focus_);
538  }
539  }
540 
541  // FIXME: The block below is strange diamond inheritance - it's code that could be in
542  // mouse_motion which applies to all three buttons, but it will be run in one of the
543  // three mouse_button<T> subclasses, and then the other two mouse_button<T> subclasses
544  // will reach here with mouse_captured_ == false.
545  widget* mouse_over = owner_.find_at(coordinate, true);
546  if(mouse_captured_) {
547  const unsigned mask
548  = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK | SDL_BUTTON_RMASK | SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK;
549 
550  if((sdl::get_mouse_button_mask() & mask) == 0) {
551  mouse_captured_ = false;
552  }
553 
554  if(mouse_focus_ == mouse_over) {
555  mouse_button_click(mouse_focus_);
556  } else if(!mouse_captured_) {
557  mouse_leave();
558 
559  if(mouse_over) {
560  mouse_enter(mouse_over);
561  }
562  }
563  } else if(focus_ && focus_ == mouse_over) {
564  mouse_button_click(focus_);
565  }
566 
567  focus_ = nullptr;
568  handled = true;
569 }
570 
571 template<std::size_t I>
573 {
574  auto stamp = std::chrono::steady_clock::now();
575  if(last_click_stamp_ + settings::double_click_time >= stamp && last_clicked_widget_ == widget) {
576  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_double_click_event << ".";
577 
578  owner_.fire(mouse_data[I].button_double_click_event, *widget);
579  last_click_stamp_ = {};
580  last_clicked_widget_ = nullptr;
581 
582  } else {
583  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_click_event << ".";
584  owner_.fire(mouse_data[I].button_click_event, *widget);
585  last_click_stamp_ = stamp;
586  last_clicked_widget_ = widget;
587  }
588 }
589 
590 /***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
591 
592 #undef LOG_HEADER
593 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
594 
595 /**
596  * @todo Test whether the state is properly tracked when an input blocker is
597  * used.
598  */
600  : mouse_motion(owner, queue_position)
601  , mouse_button_left(owner, queue_position)
602  , mouse_button_middle(owner, queue_position)
603  , mouse_button_right(owner, queue_position)
604  , mouse_button_back(owner, queue_position)
605  , mouse_button_forward(owner, queue_position)
606  , keyboard_focus_(nullptr)
607  , keyboard_focus_chain_()
608 {
609  if(SDL_WasInit(SDL_INIT_TIMER) == 0) {
610  if(SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
611  assert(false);
612  }
613  }
614 
617  this,
618  std::placeholders::_5,
619  std::placeholders::_6,
620  std::placeholders::_7
621  ));
622 
625  this,
626  std::placeholders::_5,
627  std::placeholders::_6,
628  std::placeholders::_7
629  ));
630 
633  this,
634  std::placeholders::_5,
635  std::placeholders::_6,
636  std::placeholders::_7
637  ));
638 
641  this,
642  std::placeholders::_1,
643  std::placeholders::_2
644  ));
645 
647 }
648 
650 {
653  this,
654  std::placeholders::_5,
655  std::placeholders::_6,
656  std::placeholders::_7
657  ));
658 
661  this,
662  std::placeholders::_5,
663  std::placeholders::_6,
664  std::placeholders::_7
665  ));
666 
669  this,
670  std::placeholders::_5,
671  std::placeholders::_6,
672  std::placeholders::_7
673  ));
674 
677  this,
678  std::placeholders::_1,
679  std::placeholders::_2
680  ));
681 }
682 
684 {
685  const uint32_t button_state = sdl::get_mouse_button_mask();
686 
692 
694 }
695 
697 {
698  return keyboard_focus_;
699 }
700 
702 {
703  if(keyboard_focus_) {
704  DBG_GUI_E << LOG_HEADER << "Firing: " << event::LOSE_KEYBOARD_FOCUS << ".";
706  }
707 
709 
710  if(keyboard_focus_) {
711  DBG_GUI_E << LOG_HEADER << "Firing: " << event::RECEIVE_KEYBOARD_FOCUS << ".";
713  }
714 }
715 
717 {
718  assert(widget);
720  keyboard_focus_chain_.push_back(widget);
721 }
722 
724 {
725  assert(w);
726  auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), w);
727 
728  if(itor != keyboard_focus_chain_.end()) {
729  keyboard_focus_chain_.erase(itor);
730  }
731 }
732 
733 template<typename Fcn, typename P1, typename P2, typename P3>
735 {
736  /** @todo Test whether recursion protection is needed. */
737 
738  DBG_GUI_E << LOG_HEADER << evt << ".";
739 
740  if(keyboard_focus_) {
741  // Attempt to cast to styled_widget, to avoid sending events if the
742  // widget is disabled. If the cast fails, we assume the widget
743  // is enabled and ready to receive events.
744  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
745  if(!control || control->get_active()) {
746  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
747  if(owner_.fire(evt, *keyboard_focus_, p1, p2, p3)) {
748  return;
749  }
750  }
751 
752  if(text_box_base* tb = dynamic_cast<text_box_base*>(keyboard_focus_)) {
753  if(tb->is_composing()) {
754  return; // Skip the keyboard chain if composition is in progress.
755  }
756  }
757  }
758 
759  for(auto ritor = keyboard_focus_chain_.rbegin(); ritor != keyboard_focus_chain_.rend(); ++ritor) {
760  if(*ritor == keyboard_focus_) {
761  continue;
762  }
763 
764  if(*ritor == &owner_) {
765  /**
766  * @todo Make sure we're not in the event chain.
767  *
768  * No idea why we're here, but needs to be fixed, otherwise we keep
769  * calling this function recursively upon unhandled events...
770  *
771  * Probably added to make sure the window can grab the events and
772  * handle + block them when needed, this is no longer needed with
773  * the chain.
774  */
775  continue;
776  }
777 
778  // Attempt to cast to styled_widget, to avoid sending events if the
779  // widget is disabled. If the cast fails, we assume the widget
780  // is enabled and ready to receive events.
781  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
782  if(control != nullptr && !control->get_active()) {
783  continue;
784  }
785 
786  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
787  if(owner_.fire(evt, **ritor, p1, p2, p3)) {
788  return;
789  }
790  }
791 }
792 
793 void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const std::string& unicode)
794 {
795  signal_handler_keyboard_internal<signal_keyboard>(event::SDL_KEY_DOWN, key, modifier, unicode);
796 }
797 
798 void distributor::signal_handler_sdl_text_input(const std::string& unicode, int32_t start, int32_t end)
799 {
800  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_INPUT, unicode, start, end);
801 }
802 
803 void distributor::signal_handler_sdl_text_editing(const std::string& unicode, int32_t start, int32_t end)
804 {
805  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_EDITING, unicode, start, end);
806 }
807 
809 {
810  DBG_GUI_E << LOG_HEADER << event << ".";
811 
812  /**
813  * @todo Evaluate whether moving the cleanup parts in the subclasses.
814  *
815  * It might be cleaner to do it that way, but creates extra small
816  * functions...
817  */
818 
819  if(hover_widget_ == &w) {
821  }
822 
825  }
826  if(mouse_button_left::focus_ == &w) {
827  mouse_button_left::focus_ = nullptr;
828  }
829 
832  }
834  mouse_button_middle::focus_ = nullptr;
835  }
836 
839  }
840  if(mouse_button_right::focus_ == &w) {
841  mouse_button_right::focus_ = nullptr;
842  }
843 
846  }
847  if(mouse_button_back::focus_ == &w) {
848  mouse_button_back::focus_ = nullptr;
849  }
850 
853  }
856  }
857 
858  if(mouse_focus_ == &w) {
859  mouse_focus_ = nullptr;
860  }
861 
862  if(keyboard_focus_ == &w) {
863  keyboard_focus_ = nullptr;
864  }
865 
866  const auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), &w);
867  if(itor != keyboard_focus_chain_.end()) {
868  keyboard_focus_chain_.erase(itor);
869  }
870 }
871 
872 } // 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:69
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:46
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:211
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
@ SDL_FORWARD_BUTTON_DOWN
Definition: handler.hpp:156
@ NOTIFY_REMOVE_TOOLTIP
Definition: handler.hpp:176
@ FORWARD_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:141
@ FORWARD_BUTTON_CLICK
Definition: handler.hpp:140
@ RIGHT_BUTTON_UP
Definition: handler.hpp:129
@ SDL_RIGHT_BUTTON_DOWN
Definition: handler.hpp:150
@ MIDDLE_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:127
@ BACK_BUTTON_DOWN
Definition: handler.hpp:134
@ FORWARD_BUTTON_DOWN
Definition: handler.hpp:138
@ BACK_BUTTON_CLICK
Definition: handler.hpp:136
@ MIDDLE_BUTTON_CLICK
Definition: handler.hpp:126
@ BACK_BUTTON_UP
Definition: handler.hpp:135
@ SDL_LEFT_BUTTON_DOWN
Definition: handler.hpp:146
@ SDL_TEXT_INPUT
An SDL text input (commit) event.
Definition: handler.hpp:170
@ SDL_WHEEL_DOWN
Definition: handler.hpp:162
@ 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:178
@ SDL_MOUSE_MOTION
Definition: handler.hpp:144
@ SDL_TEXT_EDITING
An SDL text editing (IME) event.
Definition: handler.hpp:171
@ LEFT_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:123
@ RIGHT_BUTTON_DOWN
Definition: handler.hpp:128
@ NOTIFY_REMOVAL
Definition: handler.hpp:174
@ RECEIVE_KEYBOARD_FOCUS
Definition: handler.hpp:177
@ SDL_TOUCH_MOTION
Definition: handler.hpp:184
@ SDL_WHEEL_RIGHT
Definition: handler.hpp:160
@ BACK_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:137
@ LEFT_BUTTON_CLICK
Definition: handler.hpp:122
@ SDL_WHEEL_LEFT
Definition: handler.hpp:159
@ SDL_LEFT_BUTTON_UP
Definition: handler.hpp:147
@ SDL_MIDDLE_BUTTON_UP
Definition: handler.hpp:149
@ SDL_BACK_BUTTON_DOWN
Definition: handler.hpp:154
@ SDL_BACK_BUTTON_UP
Definition: handler.hpp:155
@ FORWARD_BUTTON_UP
Definition: handler.hpp:139
@ LEFT_BUTTON_DOWN
Definition: handler.hpp:120
@ SDL_MIDDLE_BUTTON_DOWN
Definition: handler.hpp:148
@ SDL_RIGHT_BUTTON_UP
Definition: handler.hpp:151
@ RIGHT_BUTTON_DOUBLE_CLICK
Definition: handler.hpp:131
@ SDL_FORWARD_BUTTON_UP
Definition: handler.hpp:157
void init_mouse_location()
Initializes the location of the mouse.
Definition: handler.cpp:893
std::chrono::milliseconds double_click_time
Definition: settings.cpp:37
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:168
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.
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
This file contains the settings handling of the widget library.
Holds a 2D point.
Definition: point.hpp:25
Contains the gui2 timer routines.