00001 /* $Id: dispatcher.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */ 00002 /* 00003 Copyright (C) 2009 - 2012 by Mark de Wever <koraq@xs4all.nl> 00004 Part of the Battle for Wesnoth Project http://www.wesnoth.org/ 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY. 00012 00013 See the COPYING file for more details. 00014 */ 00015 00016 #define GETTEXT_DOMAIN "wesnoth-lib" 00017 00018 #include "gui/auxiliary/event/dispatcher_private.hpp" 00019 00020 #include "foreach.hpp" 00021 #include "gui/auxiliary/log.hpp" 00022 00023 namespace gui2 { 00024 00025 namespace event { 00026 00027 /***** tdispatcher class. *****/ 00028 00029 tdispatcher::tdispatcher() 00030 : mouse_behaviour_(all) 00031 , want_keyboard_input_(true) 00032 , signal_queue_() 00033 , signal_mouse_queue_() 00034 , signal_keyboard_queue_() 00035 , signal_notification_queue_() 00036 , signal_message_queue_() 00037 , connected_(false) 00038 , hotkeys_() 00039 { 00040 } 00041 00042 tdispatcher::~tdispatcher() 00043 { 00044 if(connected_) { 00045 disconnect_dispatcher(this); 00046 } 00047 } 00048 00049 void tdispatcher::connect() 00050 { 00051 assert(!connected_); 00052 connected_ = true; 00053 connect_dispatcher(this); 00054 } 00055 00056 bool tdispatcher::has_event(const tevent event 00057 , const tevent_type event_type 00058 ) 00059 { 00060 #if 0 00061 // Debug code to test whether the event is in the right queue. 00062 std::cerr << "Event '" << event 00063 << "' event " 00064 << find<tset_event>(event, tdispatcher_implementation 00065 ::thas_handler(event_type, *this)) 00066 << " mouse " 00067 << find<tset_event_mouse>(event, tdispatcher_implementation 00068 ::thas_handler(event_type, *this)) 00069 << " keyboard " 00070 << find<tset_event_keyboard>(event, tdispatcher_implementation 00071 ::thas_handler(event_type, *this)) 00072 << " notification " 00073 << find<tset_event_notification>(event, tdispatcher_implementation 00074 ::thas_handler(event_type, *this)) 00075 << " message " 00076 << find<tset_event_message>(event, tdispatcher_implementation 00077 ::thas_handler(event_type, *this)) 00078 << ".\n"; 00079 #endif 00080 00081 return find<tset_event>(event, tdispatcher_implementation 00082 ::thas_handler(event_type, *this)) 00083 || find<tset_event_mouse>(event, tdispatcher_implementation 00084 ::thas_handler(event_type, *this)) 00085 || find<tset_event_keyboard>(event, tdispatcher_implementation 00086 ::thas_handler(event_type, *this)) 00087 || find<tset_event_notification>(event, tdispatcher_implementation 00088 ::thas_handler(event_type, *this)) 00089 || find<tset_event_message>(event, tdispatcher_implementation 00090 ::thas_handler(event_type, *this)) 00091 ; 00092 } 00093 00094 /** 00095 * Helper class to do a runtime test whether an event is in a set. 00096 * 00097 * The class is supposed to be used in combination with find function. This 00098 * function is used in the fire functions to make sure an event is send to the 00099 * proper handler. If not there will be a run-time assertion failure. This 00100 * makes developing and testing the code easier, a wrong handler terminates 00101 * Wesnoth instead of silently not working. 00102 */ 00103 class tevent_in_set 00104 { 00105 public: 00106 00107 /** 00108 * If found we get executed to set the result. 00109 * 00110 * Since we need to return true if found we always return true. 00111 */ 00112 template<class T> 00113 bool oper(tevent) 00114 { 00115 return true; 00116 } 00117 }; 00118 00119 /** 00120 * Helper struct to wrap the functor call. 00121 * 00122 * The template function @ref fire_event needs to call a functor with extra 00123 * parameter. In order to facilitate this we send the parameter in the 00124 * constructor of the class and let operator() call the functor with the 00125 * default parameters and the stored parameters. This allows the core part of 00126 * @ref tdispatcher::fire to be generic. 00127 */ 00128 class ttrigger 00129 { 00130 public: 00131 void operator()(tsignal_function functor 00132 , tdispatcher& dispatcher 00133 , const tevent event 00134 , bool& handled 00135 , bool& halt) 00136 { 00137 functor(dispatcher, event, handled, halt); 00138 } 00139 }; 00140 00141 bool tdispatcher::fire(const tevent event, twidget& target) 00142 { 00143 assert(find<tset_event>(event, tevent_in_set())); 00144 switch(event) { 00145 case LEFT_BUTTON_DOUBLE_CLICK : 00146 return fire_event_double_click< 00147 LEFT_BUTTON_CLICK 00148 , LEFT_BUTTON_DOUBLE_CLICK 00149 , &tevent_executor::wants_mouse_left_double_click 00150 , tsignal_function 00151 >( 00152 dynamic_cast<twidget*>(this) 00153 , &target 00154 , ttrigger()); 00155 00156 case MIDDLE_BUTTON_DOUBLE_CLICK : 00157 return fire_event_double_click< 00158 MIDDLE_BUTTON_CLICK 00159 , MIDDLE_BUTTON_DOUBLE_CLICK 00160 , &tevent_executor::wants_mouse_middle_double_click 00161 , tsignal_function 00162 >( 00163 dynamic_cast<twidget*>(this) 00164 , &target 00165 , ttrigger()); 00166 00167 case RIGHT_BUTTON_DOUBLE_CLICK : 00168 return fire_event_double_click< 00169 RIGHT_BUTTON_CLICK 00170 , RIGHT_BUTTON_DOUBLE_CLICK 00171 , &tevent_executor::wants_mouse_right_double_click 00172 , tsignal_function 00173 >( 00174 dynamic_cast<twidget*>(this) 00175 , &target 00176 , ttrigger()); 00177 00178 default : 00179 return fire_event<tsignal_function>(event 00180 , dynamic_cast<twidget*>(this) 00181 , &target 00182 , ttrigger()); 00183 } 00184 } 00185 00186 /** Helper struct to wrap the functor call. */ 00187 class ttrigger_mouse 00188 { 00189 public: 00190 ttrigger_mouse(const tpoint& coordinate) 00191 : coordinate_(coordinate) 00192 { 00193 00194 } 00195 00196 void operator()(tsignal_mouse_function functor 00197 , tdispatcher& dispatcher 00198 , const tevent event 00199 , bool& handled 00200 , bool& halt) 00201 { 00202 functor(dispatcher, event, handled, halt, coordinate_); 00203 } 00204 00205 private: 00206 tpoint coordinate_; 00207 }; 00208 00209 bool tdispatcher::fire(const tevent event 00210 , twidget& target 00211 , const tpoint& coordinate) 00212 { 00213 assert(find<tset_event_mouse>(event, tevent_in_set())); 00214 return fire_event<tsignal_mouse_function>(event 00215 , dynamic_cast<twidget*>(this) 00216 , &target 00217 , ttrigger_mouse(coordinate)); 00218 } 00219 00220 /** Helper struct to wrap the functor call. */ 00221 class ttrigger_keyboard 00222 { 00223 public: 00224 ttrigger_keyboard(const SDLKey key 00225 , const SDLMod modifier 00226 , const Uint16 unicode) 00227 : key_(key) 00228 , modifier_(modifier) 00229 , unicode_(unicode) 00230 { 00231 } 00232 00233 void operator()(tsignal_keyboard_function functor 00234 , tdispatcher& dispatcher 00235 , const tevent event 00236 , bool& handled 00237 , bool& halt) 00238 { 00239 functor(dispatcher, event, handled, halt, key_, modifier_, unicode_); 00240 } 00241 00242 private: 00243 SDLKey key_; 00244 SDLMod modifier_; 00245 Uint16 unicode_; 00246 }; 00247 00248 bool tdispatcher::fire(const tevent event 00249 , twidget& target 00250 , const SDLKey key 00251 , const SDLMod modifier 00252 , const Uint16 unicode) 00253 { 00254 assert(find<tset_event_keyboard>(event, tevent_in_set())); 00255 return fire_event<tsignal_keyboard_function>(event 00256 , dynamic_cast<twidget*>(this) 00257 , &target 00258 , ttrigger_keyboard(key, modifier, unicode)); 00259 } 00260 00261 /** Helper struct to wrap the functor call. */ 00262 class ttrigger_notification 00263 { 00264 public: 00265 00266 void operator()(tsignal_notification_function functor 00267 , tdispatcher& dispatcher 00268 , const tevent event 00269 , bool& handled 00270 , bool& halt) 00271 { 00272 functor(dispatcher, event, handled, halt, NULL); 00273 } 00274 }; 00275 00276 bool tdispatcher::fire(const tevent event 00277 , twidget& target 00278 , void*) 00279 { 00280 assert(find<tset_event_notification>(event, tevent_in_set())); 00281 return fire_event<tsignal_notification_function>(event 00282 , dynamic_cast<twidget*>(this) 00283 , &target 00284 , ttrigger_notification()); 00285 } 00286 00287 /** Helper struct to wrap the functor call. */ 00288 class ttrigger_message 00289 { 00290 public: 00291 ttrigger_message(tmessage& message) 00292 : message_(message) 00293 { 00294 } 00295 00296 void operator()(tsignal_message_function functor 00297 , tdispatcher& dispatcher 00298 , const tevent event 00299 , bool& handled 00300 , bool& halt) 00301 { 00302 functor(dispatcher, event, handled, halt, message_); 00303 } 00304 00305 private: 00306 tmessage& message_; 00307 }; 00308 00309 bool tdispatcher::fire(const tevent event, twidget& target, tmessage& message) 00310 { 00311 assert(find<tset_event_message>(event, tevent_in_set())); 00312 return fire_event<tsignal_message_function>(event 00313 , dynamic_cast<twidget*>(this) 00314 , &target 00315 , ttrigger_message(message)); 00316 } 00317 00318 void tdispatcher::register_hotkey(const hotkey::HOTKEY_COMMAND id 00319 , const thotkey_function& function) 00320 { 00321 hotkeys_[id] = function; 00322 } 00323 00324 bool tdispatcher::execute_hotkey(const hotkey::HOTKEY_COMMAND id) 00325 { 00326 std::map<hotkey::HOTKEY_COMMAND, thotkey_function>::iterator 00327 itor = hotkeys_.find(id); 00328 00329 if(itor == hotkeys_.end()) { 00330 return false; 00331 } 00332 00333 return itor->second(*this, id); 00334 } 00335 00336 } // namespace event 00337 00338 } // namespace gui2 00339 00340 /** 00341 * @page event_dispatching Event dispatching. 00342 * 00343 * @section introduction Introduction 00344 * 00345 * This page describes how the new event handling system works, since the 00346 * system is still work in progress it might be out of date with the actual 00347 * code. It also contains some ideas that might change later on. Some parts are 00348 * explained in the interface and will be integrated in this document later. 00349 * 00350 * Since the event handling code hasn't been cast in stone yet some scenarios 00351 * for solving the problem are discussed first and then the solution that is 00352 * chosen in more detail. 00353 * 00354 * After SDL has generated and event it needs to be turned into an event which 00355 * the widgets can use. 00356 * 00357 * @section handling_solution The implementation solutions. 00358 * 00359 * For the event handling we use a few use case scenarios and show the possible 00360 * solutions. 00361 * 00362 * @subsection sample The sample window 00363 * 00364 * In our samples we use this sample window with the following components: 00365 * - a window W 00366 * - a container C 00367 * - a button B 00368 * 00369 * These are arranged accordingly: 00370 * @code 00371 * 00372 * --------------------- 00373 * |W | 00374 * | | 00375 * | ----------------- | 00376 * | |C |^| | 00377 * | | |-| | 00378 * | | ---------- |#| | 00379 * | | |B | | | | 00380 * | | ---------- | | | 00381 * | | |-| | 00382 * | | |v| | 00383 * | ----------------- | 00384 * | | 00385 * --------------------- 00386 * 00387 * @endcode 00388 * 00389 * @subsection scenarios Possible scenarios 00390 * 00391 * The scenarios are: 00392 * - An event that is wanted by none. 00393 * - A mouse down event that should focus C and set the pressed state in B. 00394 * - A mouse wheel event, which first should be offered to B and if not handled 00395 * by B should be handled by C. 00396 * 00397 * @subsection all_queues Pass the event through all queues 00398 * 00399 * In this solution the event will be passed through all possible queues and 00400 * tries sees where the event sticks. This following sections describe how the 00401 * events are tried for this usage scenario. 00402 * 00403 * @subsubsection unhandled Unhandled event 00404 * 00405 * - W pre child 00406 * - C pre child 00407 * - B pre child 00408 * - W child 00409 * - C child 00410 * - B child 00411 * - W post child 00412 * - C post child 00413 * - B post child 00414 * 00415 * @subsubsection mouse_down Mouse down 00416 * 00417 * - W pre child 00418 * - C pre child -> set focus -> !handled 00419 * - B pre child -> set pressed state -> handled 00420 * 00421 * @subsubsection mouse_wheel Mouse wheel 00422 * 00423 * - W pre child 00424 * - C pre child 00425 * - B pre child -> We can't scroll so ignore 00426 * - W child 00427 * - C child 00428 * - B child 00429 * - W post child 00430 * - C post child -> Scroll -> handled 00431 * 00432 * @subsection chain Pass the events in a chain like fashion 00433 * 00434 * In this solution the events are send to the pre- and post queue of all but 00435 * the last possible widget and to the child of the last widget. The pre queue 00436 * will be send from top to bottom, the post queue from bottom to top. 00437 * 00438 * @subsubsection unhandled Unhandled event 00439 * 00440 * - W pre child 00441 * - C pre child 00442 * - B child 00443 * - C post child 00444 * - W post child 00445 * 00446 * @subsubsection mouse_down Mouse down 00447 * 00448 * - W pre child 00449 * - C pre child -> set focus -> !handled 00450 * - B child -> set pressed state -> handled 00451 * 00452 * @subsubsection mouse_wheel Mouse wheel 00453 * 00454 * - W pre child 00455 * - C pre child 00456 * - B child -> We can't scroll so ignore 00457 * - C post child -> Scroll -> handled 00458 * 00459 * @subsection evaluation Evaluation 00460 * 00461 * When using the first solution it's possible to drop the child queue since 00462 * everything falls in pre or post. But there is a scenario that's a bit ugly 00463 * to solve with the first solution: 00464 * 00465 * Assume there is a listbox with toggle panels and on the panel there are a 00466 * few buttons, the wanted behaviour is: 00467 * - if clicked on the panel it should toggle, which may or may not be allowed. 00468 * - if clicked on a button in the panel, we want to make sure the panel is 00469 * selected, which again may or may not be allowed. 00470 * 00471 * With solution 2 it's rather easy: 00472 * 00473 * Click on panel: 00474 * - W pre child 00475 * - C child -> Test whether we can toggle -> handled, halt = !toggled 00476 * 00477 * Click on button in panel: 00478 * - W pre child 00479 * - C pre child -> Test whether we can select -> handled = halt = !selected 00480 * - B child -> do button stuff -> handled 00481 * 00482 * Since for the different clicks, different queues are triggered it's easy to 00483 * add a different handler there. 00484 * 00485 * With solution 1: 00486 * 00487 * Click on panel: 00488 * - W pre child 00489 * - C pre child -> handler 1 -> if last in queue -> solution 2 C child 00490 * 00491 * Click on button in panel: 00492 * - W pre child 00493 * - C pre child -> handler 2 -> if !last in queue -> solution 2 C pre child 00494 * - B pre child -> do button stuff -> handled 00495 * 00496 * Not that different from solution 2, the two handlers are installed in the C 00497 * pre event. But we need to manually check whether we're really the last, 00498 * which means the code to check whether there are more handlers at a lower 00499 * level is needed for both solutions. In solution 1 this test needs to be done 00500 * twice versus once in solution 2. Also the fact that the queues for the 00501 * events are processed in reverse order on the way back sounds more 00502 * initiative. 00503 * 00504 * @section processing_raw_events Processing the raw events. 00505 * 00506 * This section describes how the events generated by SDL are send as our own 00507 * events to the various widgets. The first step in sending an event is to 00508 * decode it and send it to a registered dispatcher. 00509 * 00510 * - gui2::event::thandler handles the SDL events. 00511 * - gui2::event::tdispatcher has the registered dispatchers. 00512 * 00513 * In general a dispatcher is a window which then needs to send this event to 00514 * the widgets. The dispatcher is just a simple part which fires events and 00515 * finds a handler for the event. This is not to the liking of most widgets, 00516 * they don't want to handle raw events but get a polished and clean event. No 00517 * button up and down and then try to figure out whether it needs to act as if 00518 * it was clicked upon, no simply op and down to change the appearance and a 00519 * click event to do the clicking actions. And don't even try to convince a 00520 * widget to determine whether this up event was a single or double click. 00521 * Widgets like to sleep with nice dreams and not having nightmares where SDL 00522 * events haunt them. 00523 * 00524 * In order to remedy that problem there's the gui2::event::tdistributor 00525 * class, it's the class to do the dirty job of converting the raw event into 00526 * these nice polished events. The distributor is in general linked to a window, 00527 * but a widget can install it's own distributor if it needs to know more of the 00528 * raw events as still left in the polished events. At the time of this writing 00529 * no widget needs this feature, but the toggle panel might need it. 00530 * 00531 * After the distributor has polished the event and send it on its way to the 00532 * widget the dispatcher needs to make sure the event is properly dispatched to 00533 * the widget in question and also notify its parents by means of the previously 00534 * described event chain. 00535 * 00536 * @subsection sdl_event Get the SDL events 00537 * 00538 * The first step in event handling is getting the events in the first place. 00539 * Events are generated by SDL and placed in a queue. The Wesnoth code processes 00540 * this queue and thus handles the events. The part which does the first 00541 * handling isn't described here since it's (secretly) intended to be replaced 00542 * by the @ref gui2::event::thandler class. Instead we directly jump to this 00543 * class and explain what it does. 00544 * 00545 * The main handling function is @ref gui2::event::thandler::handle_event which 00546 * as no real surprise handles the events. The function is a simple multiplexer 00547 * which lets other subfunctions to the handling of specific events. 00548 * 00549 * @todo Describe drawing and resizing once the code is stable and working as 00550 * wanted in these areas. 00551 * 00552 * @subsubsection thandler_mouse Mouse motion events 00553 * 00554 * If a dispatcher has captured the mouse it gets the event, no questions asked. 00555 * If not it goes through all dispatchers and finds the first one willing to 00556 * accept the mouse event. 00557 * 00558 * This means a mouse event is send to one dispatcher. 00559 * 00560 * @subsubsection thandler_mouse_button_down Mouse button down events 00561 * 00562 * Turning the mouse wheel on a mouse generates both an down and up event. It 00563 * has been decided to handle the wheel event in the button up code so wheel 00564 * events are here directly dropped on the floor and forgotten. 00565 * 00566 * The other buttons are handled as if they're normal mouse events but are 00567 * decoded per button so instead of a button_down(id) you get button_down_id. 00568 * 00569 * @subsubsection thandler_mouse_button_up Mouse button up events 00570 * 00571 * The mouse wheel event is handled as if it's a keyboard event and like the 00572 * button_down they are send as wheel_id events. 00573 * 00574 * The other mouse buttons are handled the same as the down buttons. 00575 * 00576 * @subsubsection thandler_keyboard Keyboard events 00577 * 00578 * There are three types of keyboard events, the already mentioned mouse wheel 00579 * events, the key down and key up event. When a key is pressed for a longer 00580 * time several key down events are generated and only one key up, this means 00581 * the key up is rather useless. Guess what, the multiplexer already drops that 00582 * event so we never get it. 00583 * 00584 * If the keyboard event is a mouse wheel event it's directly send to the 00585 * dispachting queue; either the dispatcher that captured the keyboard or the 00586 * last dispatcher in the queue. 00587 * 00588 * If the event is a real keyboard action it's first tried as hotkey. In order 00589 * to do so the target dispatcher is first determined, either the dispatcher 00590 * that captured the keyboard or the last dispatcher in the queue. Then it's 00591 * tried whether a hotkey and whether the hotkey can be processed. If the 00592 * hotkey isn't processed the keyboard event is send to the dispatcher as 00593 * normal keyboard event. 00594 * 00595 * The hotkey processing will have several queues (to be implemented in 1.9): 00596 * - global hotkeys that always work eg toggling fullscreen mode. 00597 * - main screen hotkeys, these work when one of the dialogs is shown without 00598 * other dialogs on top of them. These hotkeys are for example 00599 * preferences. The main screens are: 00600 * - title screen 00601 * - game 00602 * - editor 00603 * - mp lobby 00604 * - map screen hotkeys, these work when a map is shown eg toggle grid. The 00605 * screens are: 00606 * - game 00607 * - editor 00608 * - local hotkeys, these are hotkeys that only work in a specific dialog eg 00609 * recruit unit only works in the game screen. 00610 * 00611 * The queues are processed in from bottom to top in the list above, this 00612 * allows an item to use a hotkey but have another handler function. Eg 00613 * preferences in the editor might open another preferences dialog. 00614 * 00615 * @todo The hotkeys need to be implemented like above in 1.9. 00616 * 00617 * @todo This might change in the near future. 00618 * 00619 * @subsection tdistributor Event polishing and distribution 00620 * 00621 * The event distributor has the job to find the widget that should receive the 00622 * event and which event(s) to send from a single event. In general an event is 00623 * first send to the widget as-is, sending the raw events allows other 00624 * distributors to be nested between this distributor and the intended target 00625 * widget. Or the intended widget might not really be the intended widget but 00626 * another distributor that wants to dispatch the event internally. 00627 * 00628 * However in the common cases this raw event isn't handled and the distributor 00629 * needs to send the polished events. In the following sections the details of 00630 * the conversion from raw to polished is described, it intentionally lacks the 00631 * part of sending the raw events as well since it adds no value. 00632 * 00633 * A widget can capture the mouse, which means all mouse events are send to this 00634 * widget, regardless where the mouse is. This is normally done in a mouse down 00635 * event (for a button) so all events following it are send to that widget. 00636 * 00637 * @subsection mouse_motion Mouse motion 00638 * 00639 * This section describes the conversion from a raw mouse motion to the polished 00640 * events it can generate: 00641 * - @ref gui2::event::MOUSE_ENTER "MOUSE_ENTER" 00642 * - @ref gui2::event::MOUSE_LEAVE "MOUSE_LEAVE" 00643 * - @ref gui2::event::MOUSE_MOTION "MOUSE_MOTION" 00644 * 00645 * When the mouse is captured that widget will only receive motion events. 00646 * 00647 * If not captured the code checks whether the widget underneath the mouse is 00648 * the same widget as at the last motion if event. If so that widget gets a 00649 * motion event. 00650 * If not the widget that before was underneath the mouse pointer (if any) gets 00651 * a leave event and the widget newly underneath the mouse pointer (if any) gets 00652 * an enter event. 00653 * 00654 * @subsection mouse_button Mouse buttons 00655 * 00656 * The mouse button code is a bit more complex and is separated in the various 00657 * events to be send. 00658 * 00659 * @subsubsection mouse_button_down Mouse button down 00660 * 00661 * Some things start simple, so does the event of pressing down a mouse button. 00662 * All it does is send the event to the widget as one of the following events: 00663 * - @ref gui2::event::LEFT_BUTTON_DOWN "LEFT_BUTTON_DOWN" 00664 * - @ref gui2::event::MIDDLE_BUTTON_DOWN "MIDDLE_BUTTON_DOWN" 00665 * - @ref gui2::event::RIGHT_BUTTON_DOWN "RIGHT_BUTTON_DOWN" 00666 * 00667 * @todo Validate the code it seems a down event with a captured mouse doesn't 00668 * really work as wanted. (Rare case but should work properly.) In general the 00669 * mouse event handling needs testing to see whether the proper events are send 00670 * all the time. 00671 * 00672 * @subsubsection mouse_button_up Mouse button up 00673 * 00674 * Simplicity ends here. 00675 * 00676 * @todo Document further. 00677 * 00678 * @subsubsection mouse_click Mouse click 00679 * 00680 * So the button up event has asked for mouse click, now we need to test whether 00681 * the click will be a click or a double click. A double click is generated when 00682 * the same widget is clicked twice in a short time and causes the following 00683 * events: 00684 * - @ref gui2::event::LEFT_BUTTON_DOUBLE_CLICK "LEFT_BUTTON_DOUBLE_CLICK" 00685 * - @ref gui2::event::MIDDLE_BUTTON_DOUBLE_CLICK "MIDDLE_BUTTON_DOUBLE_CLICK" 00686 * - @ref gui2::event::RIGHT_BUTTON_DOUBLE_CLICK "RIGHT_BUTTON_DOUBLE_CLICK" 00687 * 00688 * Otherwise one of the following single clicks is generated: 00689 * - @ref gui2::event::LEFT_BUTTON_CLICK "LEFT_BUTTON_CLICK" 00690 * - @ref gui2::event::MIDDLE_BUTTON_CLICK "MIDDLE_BUTTON_CLICK" 00691 * - @ref gui2::event::RIGHT_BUTTON_CLICK "RIGHT_BUTTON_CLICK" 00692 * 00693 * @subsubsection double_click To double click or not to double click 00694 * 00695 * Wait a second, a widget has a field whether or not it wants a double click 00696 * for a certain mouse button and now I see that it's bluntly ignored by the 00697 * distributor. Indeed the distributor doesn't care about what the widget wants, 00698 * it does what it wants and leaves the sorting out what's wanted to the 00699 * dispatcher. 00700 * 00701 * The problem is that in the chain events are send to one widget that may not 00702 * be interested in a double click, but another widget in the chain is. There 00703 * are several solutions to this problem: 00704 * -# Sending a click followed by a double click. 00705 * -# Sending a click with a tag field that it actually is a double click. 00706 * -# Send a double click and turn it into a click if the double click is 00707 * unwanted. 00708 * 00709 * The first solution has the disadvantage that a toggle panel likes a click and 00710 * double click, the first click selects the second deselects and now the 00711 * deselected panel gets a double click. When the panel now checks whether it's 00712 * selected it's not and might take the wrong action upon it. 00713 * 00714 * The second option is possible but would be rather intrusive in the code, 00715 * since it would generate another event signature. Adding a signature just for 00716 * this special case seemed a bit too much effort vs. gain. Also the widget 00717 * needs to check whether a click is a click or a double click and choose a 00718 * different code path for it. This in turn would mean a signal handler 00719 * secretly might handle two events and lowers the transparency of the code. 00720 * 00721 * The third option also adds some special case handling but the scope is 00722 * limited and only one part knows about the tricks done. 00723 * 00724 * The last option has been chosen and the dispatcher build the event chain and 00725 * while building the chain it looks whether the widget wants the double click 00726 * or not. It does this test by looking at the wants double click function and 00727 * not test for a handler. The double click test function is made for this 00728 * purpose and depending on the handler might again do the wrong thing. 00729 * (A certain toggle panel might not want to do something on a double click but 00730 * also not being deselected upon a double click. The latter to keep the UI 00731 * consistent, a double click on a toggle panel might execute a special function 00732 * or not, but always keep the panel selected. (That is if the panel can be 00733 * selected.)) 00734 */ 00735
| Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:39 for The Battle for Wesnoth | Gna! | Forum | Wiki | CIA | devdocs |