The Battle for Wesnoth  1.19.10+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 };
405 
406 } // namespace
407 
408 template<std::size_t I>
410  : mouse_motion(owner, queue_position)
411  , last_click_stamp_()
412  , last_clicked_widget_(nullptr)
413  , focus_(nullptr)
414  , is_down_(false)
415  , signal_handler_sdl_button_down_entered_(false)
416  , signal_handler_sdl_button_up_entered_(false)
417 {
418  static_assert(I < mouse_data.size(), "Out-of-bounds mouse_button template index");
419 
420  owner_.connect_signal<mouse_data[I].sdl_button_down_event>(
422  this,
423  std::placeholders::_2,
424  std::placeholders::_3,
425  std::placeholders::_5),
426  queue_position);
427 
428  owner_.connect_signal<mouse_data[I].sdl_button_up_event>(
430  this,
431  std::placeholders::_2,
432  std::placeholders::_3,
433  std::placeholders::_5),
434  queue_position);
435 }
436 
437 template<std::size_t I>
438 void mouse_button<I>::initialize_state(int32_t button_state)
439 {
440  last_click_stamp_ = {};
441  last_clicked_widget_ = nullptr;
442  focus_ = nullptr;
443  // SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, and SDL_BUTTON_RIGHT correspond to 1,2,3
444  is_down_ = button_state & SDL_BUTTON(I + 1);
445 }
446 
447 template<std::size_t I>
449  const event::ui_event event, bool& handled, const point& coordinate)
450 {
451  if(signal_handler_sdl_button_down_entered_) {
452  return;
453  }
454  resource_locker lock{signal_handler_sdl_button_down_entered_};
455 
456  DBG_GUI_E << LOG_HEADER << event << ".";
457 
458  if(is_down_) {
459 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
460  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already down, we missed an event.";
461 #endif
462  return;
463  }
464  is_down_ = true;
465 
466  if(mouse_captured_) {
467  assert(mouse_focus_);
468  focus_ = mouse_focus_;
469  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
470  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
471  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
472  owner_.fire(mouse_data[I].button_down_event, *mouse_focus_);
473  }
474  } else {
475  widget* mouse_over = owner_.find_at(coordinate, true);
476  if(!mouse_over) {
477  return;
478  }
479 
480  if(mouse_over != mouse_focus_) {
481 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
482  WRN_GUI_E << LOG_HEADER << ". Mouse down on non focused widget "
483  << "and mouse not captured, we missed events.";
484 #endif
485  mouse_focus_ = mouse_over;
486  }
487 
488  focus_ = mouse_over;
489  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_down_event << ".";
490  if(!owner_.fire(mouse_data[I].sdl_button_down_event, *focus_, coordinate)) {
491  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_down_event << ".";
492  owner_.fire(mouse_data[I].button_down_event, *focus_);
493  }
494  }
495  handled = true;
496 }
497 
498 template<std::size_t I>
500  const event::ui_event event, bool& handled, const point& coordinate)
501 {
502  if(signal_handler_sdl_button_up_entered_) {
503  return;
504  }
505  resource_locker lock{signal_handler_sdl_button_up_entered_};
506 
507  DBG_GUI_E << LOG_HEADER << event << ".";
508 
509  if(!is_down_) {
510 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
511  WRN_GUI_E << LOG_HEADER << event << ". The mouse button is already up, we missed an event.";
512 #endif
513  return;
514  }
515  is_down_ = false;
516 
517  if(focus_) {
518  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].sdl_button_up_event << ".";
519  if(!owner_.fire(mouse_data[I].sdl_button_up_event, *focus_, coordinate)) {
520  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_up_event << ".";
521  owner_.fire(mouse_data[I].button_up_event, *focus_);
522  }
523  }
524 
525  // FIXME: The block below is strange diamond inheritance - it's code that could be in
526  // mouse_motion which applies to all three buttons, but it will be run in one of the
527  // three mouse_button<T> subclasses, and then the other two mouse_button<T> subclasses
528  // will reach here with mouse_captured_ == false.
529  widget* mouse_over = owner_.find_at(coordinate, true);
530  if(mouse_captured_) {
531  const unsigned mask = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
532 
533  if((sdl::get_mouse_button_mask() & mask) == 0) {
534  mouse_captured_ = false;
535  }
536 
537  if(mouse_focus_ == mouse_over) {
538  mouse_button_click(mouse_focus_);
539  } else if(!mouse_captured_) {
540  mouse_leave();
541 
542  if(mouse_over) {
543  mouse_enter(mouse_over);
544  }
545  }
546  } else if(focus_ && focus_ == mouse_over) {
547  mouse_button_click(focus_);
548  }
549 
550  focus_ = nullptr;
551  handled = true;
552 }
553 
554 template<std::size_t I>
556 {
557  auto stamp = std::chrono::steady_clock::now();
558  if(last_click_stamp_ + settings::double_click_time >= stamp && last_clicked_widget_ == widget) {
559  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_double_click_event << ".";
560 
561  owner_.fire(mouse_data[I].button_double_click_event, *widget);
562  last_click_stamp_ = {};
563  last_clicked_widget_ = nullptr;
564 
565  } else {
566  DBG_GUI_E << LOG_HEADER << "Firing: " << mouse_data[I].button_click_event << ".";
567  owner_.fire(mouse_data[I].button_click_event, *widget);
568  last_click_stamp_ = stamp;
569  last_clicked_widget_ = widget;
570  }
571 }
572 
573 /***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
574 
575 #undef LOG_HEADER
576 #define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
577 
578 /**
579  * @todo Test whether the state is properly tracked when an input blocker is
580  * used.
581  */
583  : mouse_motion(owner, queue_position)
584  , mouse_button_left(owner, queue_position)
585  , mouse_button_middle(owner, queue_position)
586  , mouse_button_right(owner, queue_position)
587  , keyboard_focus_(nullptr)
588  , keyboard_focus_chain_()
589 {
590  if(SDL_WasInit(SDL_INIT_TIMER) == 0) {
591  if(SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
592  assert(false);
593  }
594  }
595 
598  this,
599  std::placeholders::_5,
600  std::placeholders::_6,
601  std::placeholders::_7
602  ));
603 
606  this,
607  std::placeholders::_5,
608  std::placeholders::_6,
609  std::placeholders::_7
610  ));
611 
614  this,
615  std::placeholders::_5,
616  std::placeholders::_6,
617  std::placeholders::_7
618  ));
619 
622  this,
623  std::placeholders::_1,
624  std::placeholders::_2
625  ));
626 
628 }
629 
631 {
634  this,
635  std::placeholders::_5,
636  std::placeholders::_6,
637  std::placeholders::_7
638  ));
639 
642  this,
643  std::placeholders::_5,
644  std::placeholders::_6,
645  std::placeholders::_7
646  ));
647 
650  this,
651  std::placeholders::_5,
652  std::placeholders::_6,
653  std::placeholders::_7
654  ));
655 
658  this,
659  std::placeholders::_1,
660  std::placeholders::_2
661  ));
662 }
663 
665 {
666  const uint32_t button_state = sdl::get_mouse_button_mask();
667 
671 
673 }
674 
676 {
677  return keyboard_focus_;
678 }
679 
681 {
682  if(keyboard_focus_) {
683  DBG_GUI_E << LOG_HEADER << "Firing: " << event::LOSE_KEYBOARD_FOCUS << ".";
685  }
686 
688 
689  if(keyboard_focus_) {
690  DBG_GUI_E << LOG_HEADER << "Firing: " << event::RECEIVE_KEYBOARD_FOCUS << ".";
692  }
693 }
694 
696 {
697  assert(widget);
699  keyboard_focus_chain_.push_back(widget);
700 }
701 
703 {
704  assert(w);
705  auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), w);
706 
707  if(itor != keyboard_focus_chain_.end()) {
708  keyboard_focus_chain_.erase(itor);
709  }
710 }
711 
712 template<typename Fcn, typename P1, typename P2, typename P3>
714 {
715  /** @todo Test whether recursion protection is needed. */
716 
717  DBG_GUI_E << LOG_HEADER << evt << ".";
718 
719  if(keyboard_focus_) {
720  // Attempt to cast to styled_widget, to avoid sending events if the
721  // widget is disabled. If the cast fails, we assume the widget
722  // is enabled and ready to receive events.
723  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
724  if(!control || control->get_active()) {
725  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
726  if(owner_.fire(evt, *keyboard_focus_, p1, p2, p3)) {
727  return;
728  }
729  }
730 
731  if(text_box_base* tb = dynamic_cast<text_box_base*>(keyboard_focus_)) {
732  if(tb->is_composing()) {
733  return; // Skip the keyboard chain if composition is in progress.
734  }
735  }
736  }
737 
738  for(auto ritor = keyboard_focus_chain_.rbegin(); ritor != keyboard_focus_chain_.rend(); ++ritor) {
739  if(*ritor == keyboard_focus_) {
740  continue;
741  }
742 
743  if(*ritor == &owner_) {
744  /**
745  * @todo Make sure we're not in the event chain.
746  *
747  * No idea why we're here, but needs to be fixed, otherwise we keep
748  * calling this function recursively upon unhandled events...
749  *
750  * Probably added to make sure the window can grab the events and
751  * handle + block them when needed, this is no longer needed with
752  * the chain.
753  */
754  continue;
755  }
756 
757  // Attempt to cast to styled_widget, to avoid sending events if the
758  // widget is disabled. If the cast fails, we assume the widget
759  // is enabled and ready to receive events.
760  styled_widget* control = dynamic_cast<styled_widget*>(keyboard_focus_);
761  if(control != nullptr && !control->get_active()) {
762  continue;
763  }
764 
765  DBG_GUI_E << LOG_HEADER << "Firing: " << evt << ".";
766  if(owner_.fire(evt, **ritor, p1, p2, p3)) {
767  return;
768  }
769  }
770 }
771 
772 void distributor::signal_handler_sdl_key_down(const SDL_Keycode key, const SDL_Keymod modifier, const std::string& unicode)
773 {
774  signal_handler_keyboard_internal<signal_keyboard>(event::SDL_KEY_DOWN, key, modifier, unicode);
775 }
776 
777 void distributor::signal_handler_sdl_text_input(const std::string& unicode, int32_t start, int32_t end)
778 {
779  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_INPUT, unicode, start, end);
780 }
781 
782 void distributor::signal_handler_sdl_text_editing(const std::string& unicode, int32_t start, int32_t end)
783 {
784  signal_handler_keyboard_internal<signal_text_input>(event::SDL_TEXT_EDITING, unicode, start, end);
785 }
786 
788 {
789  DBG_GUI_E << LOG_HEADER << event << ".";
790 
791  /**
792  * @todo Evaluate whether moving the cleanup parts in the subclasses.
793  *
794  * It might be cleaner to do it that way, but creates extra small
795  * functions...
796  */
797 
798  if(hover_widget_ == &w) {
800  }
801 
804  }
805  if(mouse_button_left::focus_ == &w) {
806  mouse_button_left::focus_ = nullptr;
807  }
808 
811  }
813  mouse_button_middle::focus_ = nullptr;
814  }
815 
818  }
819  if(mouse_button_right::focus_ == &w) {
820  mouse_button_right::focus_ = nullptr;
821  }
822 
823  if(mouse_focus_ == &w) {
824  mouse_focus_ = nullptr;
825  }
826 
827  if(keyboard_focus_ == &w) {
828  keyboard_focus_ = nullptr;
829  }
830 
831  const auto itor = std::find(keyboard_focus_chain_.begin(), keyboard_focus_chain_.end(), &w);
832  if(itor != keyboard_focus_chain_.end()) {
833  keyboard_focus_chain_.erase(itor);
834  }
835 }
836 
837 } // 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
@ 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:881
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.