1 // Written in the D programming language.
2 
3 /**
4 
5 This module contains dlangui event types declarations.
6 
7 Event types: MouseEvent, KeyEvent, ScrollEvent.
8 
9 Action and Accelerator.
10 
11 
12 Copyright: Vadim Lopatin, 2014
13 License:   Boost License 1.0
14 Authors:   Vadim Lopatin, coolreader.org@gmail.com
15 */
16 module dlangui.core.events;
17 
18 import dlangui.core.i18n;
19 import dlangui.core.collections;
20 
21 //static if (DLANGUI_GUI) {
22 //}
23 private import dlangui.widgets.widget;
24 
25 private import std.string;
26 private import std.conv;
27 private import std.utf : toUTF32;
28 
29 /// Keyboard accelerator (key + modifiers)
30 struct Accelerator {
31     /// Key code, usually one of KeyCode enum items
32     uint keyCode;
33     /// Key flags bit set, usually one of KeyFlag enum items
34     uint keyFlags;
35     /// Returns accelerator text description
36     @property dstring label() {
37         dstring buf;
38         version (OSX) {
39             static if (true) {
40                 if (keyFlags & KeyFlag.Control)
41                     buf ~= "Ctrl+";
42                 if (keyFlags & KeyFlag.Shift)
43                     buf ~= "Shift+";
44                 if (keyFlags & KeyFlag.Option)
45                     buf ~= "Opt+";
46                 if (keyFlags & KeyFlag.Command)
47                     buf ~= "Cmd+";
48             } else {
49                 if (keyFlags & KeyFlag.Control)
50                     buf ~= "⌃";
51                 if (keyFlags & KeyFlag.Shift)
52                     buf ~= "⇧";
53                 if (keyFlags & KeyFlag.Option)
54                     buf ~= "⌥";
55                 if (keyFlags & KeyFlag.Command)
56                     buf ~= "⌘";
57             }
58             buf ~= toUTF32(keyName(keyCode));
59         } else {
60             if ((keyFlags & KeyFlag.LControl) == KeyFlag.LControl && (keyFlags & KeyFlag.RControl) == KeyFlag.RControl)
61                 buf ~= "LCtrl+RCtrl+";
62             else if ((keyFlags & KeyFlag.LControl) == KeyFlag.LControl)
63                 buf ~= "LCtrl+";
64             else if ((keyFlags & KeyFlag.RControl) == KeyFlag.RControl)
65                 buf ~= "RCtrl+";
66             else if (keyFlags & KeyFlag.Control)
67                 buf ~= "Ctrl+";
68             if ((keyFlags & KeyFlag.LAlt) == KeyFlag.LAlt && (keyFlags & KeyFlag.RAlt) == KeyFlag.RAlt)
69                 buf ~= "LAlt+RAlt+";
70             else if ((keyFlags & KeyFlag.LAlt) == KeyFlag.LAlt)
71                 buf ~= "LAlt+";
72             else if ((keyFlags & KeyFlag.RAlt) == KeyFlag.RAlt)
73                 buf ~= "RAlt+";
74             else if (keyFlags & KeyFlag.Alt)
75                 buf ~= "Alt+";
76             if ((keyFlags & KeyFlag.LShift) == KeyFlag.LShift && (keyFlags & KeyFlag.RShift) == KeyFlag.RShift)
77                 buf ~= "LShift+RShift+";
78             else if ((keyFlags & KeyFlag.LShift) == KeyFlag.LShift)
79                 buf ~= "LShift+";
80             else if ((keyFlags & KeyFlag.RShift) == KeyFlag.RShift)
81                 buf ~= "RShift+";
82             else if (keyFlags & KeyFlag.Shift)
83                 buf ~= "Shift+";
84             if ((keyFlags & KeyFlag.LMenu) == KeyFlag.LMenu && (keyFlags & KeyFlag.RMenu) == KeyFlag.RMenu)
85                 buf ~= "LMenu+RMenu+";
86             else if ((keyFlags & KeyFlag.LMenu) == KeyFlag.LMenu)
87                 buf ~= "LMenu+";
88             else if ((keyFlags & KeyFlag.RMenu) == KeyFlag.RMenu)
89                 buf ~= "RMenu+";
90             else if (keyFlags & KeyFlag.Menu)
91                 buf ~= "Menu+";
92             buf ~= toUTF32(keyName(keyCode));
93         }
94         return cast(dstring)buf;
95     }
96 
97     /// Serializes accelerator text description
98     @property string toString() const {
99         char[] buf;
100         // ctrl
101         if ((keyFlags & KeyFlag.LControl) == KeyFlag.LControl && (keyFlags & KeyFlag.RControl) == KeyFlag.RControl)
102             buf ~= "LCtrl+RCtrl+";
103         else if ((keyFlags & KeyFlag.LControl) == KeyFlag.LControl)
104             buf ~= "LCtrl+";
105         else if ((keyFlags & KeyFlag.RControl) == KeyFlag.RControl)
106             buf ~= "RCtrl+";
107         else if (keyFlags & KeyFlag.Control)
108             buf ~= "Ctrl+";
109         // alt
110         if ((keyFlags & KeyFlag.LAlt) == KeyFlag.LAlt && (keyFlags & KeyFlag.RAlt) == KeyFlag.RAlt)
111             buf ~= "LAlt+RAlt+";
112         else if ((keyFlags & KeyFlag.LAlt) == KeyFlag.LAlt)
113             buf ~= "LAlt+";
114         else if ((keyFlags & KeyFlag.RAlt) == KeyFlag.RAlt)
115             buf ~= "RAlt+";
116         else if (keyFlags & KeyFlag.Alt)
117             buf ~= "Alt+";
118         // shift
119         if ((keyFlags & KeyFlag.LShift) == KeyFlag.LShift && (keyFlags & KeyFlag.RShift) == KeyFlag.RShift)
120             buf ~= "LShift+RShift+";
121         else if ((keyFlags & KeyFlag.LShift) == KeyFlag.LShift)
122             buf ~= "LShift+";
123         else if ((keyFlags & KeyFlag.RShift) == KeyFlag.RShift)
124             buf ~= "RShift+";
125         else if (keyFlags & KeyFlag.Shift)
126             buf ~= "Shift+";
127         // menu
128         if ((keyFlags & KeyFlag.LMenu) == KeyFlag.LMenu && (keyFlags & KeyFlag.RMenu) == KeyFlag.RMenu)
129             buf ~= "LMenu+RMenu+";
130         else if ((keyFlags & KeyFlag.LMenu) == KeyFlag.LMenu)
131             buf ~= "LMenu+";
132         else if ((keyFlags & KeyFlag.RMenu) == KeyFlag.RMenu)
133             buf ~= "RMenu+";
134         else if (keyFlags & KeyFlag.Menu)
135             buf ~= "Menu+";
136         buf ~= keyName(keyCode);
137         return cast(string)buf;
138     }
139     /// parse accelerator from string
140     bool parse(string s) {
141         keyCode = 0;
142         keyFlags = 0;
143         s = s.strip;
144         for(;;) {
145             bool flagFound = false;
146             if (s.startsWith("Ctrl+")) {
147                 keyFlags |= KeyFlag.Control;
148                 s = s[5 .. $];
149                 flagFound = true;
150             }
151             if (s.startsWith("LCtrl+")) {
152                 keyFlags |= KeyFlag.LControl;
153                 s = s[5 .. $];
154                 flagFound = true;
155             }
156             if (s.startsWith("RCtrl+")) {
157                 keyFlags |= KeyFlag.RControl;
158                 s = s[5 .. $];
159                 flagFound = true;
160             }
161             if (s.startsWith("Alt+")) {
162                 keyFlags |= KeyFlag.Alt;
163                 s = s[4 .. $];
164                 flagFound = true;
165             }
166             if (s.startsWith("LAlt+")) {
167                 keyFlags |= KeyFlag.LAlt;
168                 s = s[4 .. $];
169                 flagFound = true;
170             }
171             if (s.startsWith("RAlt+")) {
172                 keyFlags |= KeyFlag.RAlt;
173                 s = s[4 .. $];
174                 flagFound = true;
175             }
176             if (s.startsWith("Shift+")) {
177                 keyFlags |= KeyFlag.Shift;
178                 s = s[6 .. $];
179                 flagFound = true;
180             }
181             if (s.startsWith("LShift+")) {
182                 keyFlags |= KeyFlag.LShift;
183                 s = s[6 .. $];
184                 flagFound = true;
185             }
186             if (s.startsWith("RShift+")) {
187                 keyFlags |= KeyFlag.RShift;
188                 s = s[6 .. $];
189                 flagFound = true;
190             }
191             if (s.startsWith("Menu+")) {
192                 keyFlags |= KeyFlag.Menu;
193                 s = s[5 .. $];
194                 flagFound = true;
195             }
196             if (s.startsWith("LMenu+")) {
197                 keyFlags |= KeyFlag.LMenu;
198                 s = s[5 .. $];
199                 flagFound = true;
200             }
201             if (s.startsWith("RMenu+")) {
202                 keyFlags |= KeyFlag.RMenu;
203                 s = s[5 .. $];
204                 flagFound = true;
205             }
206             if (!flagFound)
207                 break;
208             s = s.strip;
209         }
210         keyCode = parseKeyName(s);
211         return keyCode != 0;
212     }
213 }
214 
215 /// use to for requesting of action state (to enable/disable, hide, get check status, etc)
216 struct ActionState {
217     enum StateFlag {
218         enabled = 1,
219         visible = 2,
220         checked = 4
221     }
222     protected ubyte _flags;
223     /// when false, control showing this action should be disabled
224     @property bool enabled() const { return (_flags & StateFlag.enabled) != 0; }
225     @property void enabled(bool f) { _flags = f ? (_flags | StateFlag.enabled) : (_flags & ~StateFlag.enabled); }
226     /// when false, control showing this action should be hidden
227     @property bool visible() const { return (_flags & StateFlag.visible) != 0; }
228     @property void visible(bool f) { _flags = f ? (_flags | StateFlag.visible) : (_flags & ~StateFlag.visible); }
229     /// when true, for checkbox/radiobutton-like controls state should shown as checked
230     @property bool checked() const { return (_flags & StateFlag.checked) != 0; }
231     @property void checked(bool f) { _flags = f ? (_flags | StateFlag.checked) : (_flags & ~StateFlag.checked); }
232     this(bool en, bool vis, bool check) {
233         _flags = (en ? StateFlag.enabled : 0) 
234             | (vis ? StateFlag.visible : 0) 
235             | (check ? StateFlag.checked : 0);
236     }
237     string toString() const { 
238         return (enabled ? "enabled" : "disabled") ~ (visible ? "_visible" : "_invisible") ~ (checked ? "_checked" : "");
239     }
240 }
241 
242 const ACTION_STATE_ENABLED = ActionState(true, true, false);
243 const ACTION_STATE_DISABLE = ActionState(false, true, false);
244 const ACTION_STATE_INVISIBLE = ActionState(false, false, false);
245 
246 /// Match key flags
247 static bool matchKeyFlags(uint eventFlags, uint requestedFlags) {
248     if (eventFlags == requestedFlags)
249         return true;
250     if ((requestedFlags & KeyFlag.RControl) == KeyFlag.RControl && (eventFlags & KeyFlag.RControl) != KeyFlag.RControl)
251         return false;
252     if ((requestedFlags & KeyFlag.LControl) == KeyFlag.LControl && (eventFlags & KeyFlag.LControl) != KeyFlag.LControl)
253         return false;
254     if ((requestedFlags & KeyFlag.RShift) == KeyFlag.RShift && (eventFlags & KeyFlag.RShift) != KeyFlag.RShift)
255         return false;
256     if ((requestedFlags & KeyFlag.LShift) == KeyFlag.LShift && (eventFlags & KeyFlag.LShift) != KeyFlag.LShift)
257         return false;
258     if ((requestedFlags & KeyFlag.RAlt) == KeyFlag.RAlt && (eventFlags & KeyFlag.RAlt) != KeyFlag.RAlt)
259         return false;
260     if ((requestedFlags & KeyFlag.LAlt) == KeyFlag.LAlt && (eventFlags & KeyFlag.LAlt) != KeyFlag.LAlt)
261         return false;
262     if ((requestedFlags & KeyFlag.RMenu) == KeyFlag.RMenu && (eventFlags & KeyFlag.RMenu) != KeyFlag.RMenu)
263         return false;
264     if ((requestedFlags & KeyFlag.LMenu) == KeyFlag.LMenu && (eventFlags & KeyFlag.LMenu) != KeyFlag.LMenu)
265         return false;
266     if ((requestedFlags & KeyFlag.Control) == KeyFlag.Control && (eventFlags & KeyFlag.Control) != KeyFlag.Control)
267         return false;
268     if ((requestedFlags & KeyFlag.Shift) == KeyFlag.Shift && (eventFlags & KeyFlag.Shift) != KeyFlag.Shift)
269         return false;
270     if ((requestedFlags & KeyFlag.Alt) == KeyFlag.Alt && (eventFlags & KeyFlag.Alt) != KeyFlag.Alt)
271         return false;
272     if ((requestedFlags & KeyFlag.Menu) == KeyFlag.Menu && (eventFlags & KeyFlag.Menu) != KeyFlag.Menu)
273         return false;
274     return true;
275 }
276 
277 /** 
278     UI action
279 
280     For using in menus, toolbars, etc.
281 
282  */
283 class Action {
284     /// numerical id
285     protected int _id;
286     /// label to show in UI
287     protected UIString _label;
288     /// icon resource id
289     protected string _iconId;
290     /// accelerator list
291     protected Accelerator[] _accelerators;
292     /// optional string parameter
293     protected string _stringParam;
294     /// optional long parameter
295     protected long _longParam = long.min;
296     /// optional object parameter
297     protected Object _objectParam;
298 
299     protected ActionState _state = ACTION_STATE_ENABLED;
300 
301     protected ActionState _defaultState = ACTION_STATE_ENABLED;
302 
303     /// set default state to disabled, visible, not-checked
304     Action disableByDefault() { _defaultState = ACTION_STATE_DISABLE; return this; }
305     /// set default state to disabled, invisible, not-checked
306     Action hideByDefault() { _defaultState = ACTION_STATE_INVISIBLE; return this; }
307     /// default state for action if action state lookup failed
308     @property const(ActionState) defaultState() const { return _defaultState; }
309     /// default state for action if action state lookup failed
310     @property Action defaultState(ActionState s) { _defaultState = s; return this; }
311     /// action state (can be used to enable/disable, show/hide, check/uncheck control)
312     @property const(ActionState) state() const { return _state; }
313     /// update action state (for non-const action)
314     @property Action state(const ActionState s) { _state = s; return this; }
315     /// update action state (can be changed even for const objects)
316     @property const(Action) state(const ActionState s) const {
317         if (_state != s) {
318             // hack
319             Action nonConstThis = cast(Action) this;
320             nonConstThis._state = s;
321         }
322         return this; 
323     }
324     @property const(Action) checked(bool newValue) const {
325         state = ActionState(_state.enabled, _state.visible, newValue);
326         return this;
327     }
328 
329     /// returns optional string parameter
330     @property string stringParam() const {
331         return _stringParam;
332     }
333     /// sets optional string parameter
334     @property Action stringParam(string v) {
335         _stringParam = v;
336         return this;
337     }
338     /// sets optional long parameter
339     @property long longParam() const {
340         return _longParam;
341     }
342     /// returns optional long parameter
343     @property Action longParam(long v) {
344         _longParam = v;
345         return this;
346     }
347     /// returns additional custom (Object) parameter
348     @property Object objectParam() {
349         return _objectParam;
350     }
351     /// returns additional custom (Object) parameter
352     @property const(Object) objectParam() const {
353         return _objectParam;
354     }
355     /// sets additional custom (Object) parameter
356     @property Action objectParam(Object v) {
357         _objectParam = v;
358         return this;
359     }
360     /// deep copy constructor
361     this(immutable Action a) {
362         _id = a._id;
363         _label = a._label;
364         _iconId = a._iconId;
365         _state = a._state;
366         _defaultState = a._defaultState;
367         _accelerators = a._accelerators.dup;
368         _stringParam = a._stringParam;
369         _longParam = a._longParam;
370         if (a._objectParam)
371             _objectParam = cast(Object)a._objectParam;
372     }
373     /// deep copy
374     @property Action clone() immutable { return new Action(this); }
375     /// deep copy
376     @property Action clone() const { return new Action(cast(immutable)this); }
377     /// deep copy
378     @property Action clone() { return new Action(cast(immutable)this); }
379     /// create action only with ID
380     this(int id) {
381         _id = id;
382     }
383     /// create action with id, labelResourceId, and optional icon and key accelerator.
384     this(int id, string labelResourceId, string iconResourceId = null, uint keyCode = 0, uint keyFlags = 0) {
385         _id = id;
386         _label.id = labelResourceId;
387         _iconId = iconResourceId;
388         if (keyCode) {
389             version (OSX) {
390                 if (keyFlags & KeyFlag.Control) {
391                     _accelerators ~= Accelerator(keyCode, (keyFlags & ~KeyFlag.Control) | KeyFlag.Command);
392                 }
393             }
394             _accelerators ~= Accelerator(keyCode, keyFlags);
395         }
396     }
397     /// action with accelerator, w/o label
398     this(int id, uint keyCode, uint keyFlags = 0) {
399         _id = id;
400         version (OSX) {
401             if (keyFlags & KeyFlag.Control) {
402                 _accelerators ~= Accelerator(keyCode, (keyFlags & ~KeyFlag.Control) | KeyFlag.Command);
403             }
404         }
405         _accelerators ~= Accelerator(keyCode, keyFlags);
406     }
407     /// action with label, icon, and accelerator
408     this(int id, dstring label, string iconResourceId = null, uint keyCode = 0, uint keyFlags = 0) {
409         _id = id;
410         _label.value = label;
411         _iconId = iconResourceId;
412         if (keyCode) {
413             version (OSX) {
414                 if (keyFlags & KeyFlag.Control) {
415                     _accelerators ~= Accelerator(keyCode, (keyFlags & ~KeyFlag.Control) | KeyFlag.Command);
416                 }
417             }
418             _accelerators ~= Accelerator(keyCode, keyFlags);
419         }
420     }
421     /// returs array of accelerators
422     @property Accelerator[] accelerators() {
423         // check for accelerators override in settings
424         Accelerator[] res = findActionAccelerators(_id);
425         if (res) {
426             //Log.d("Found accelerators ", res);
427             return res;
428         }
429         // return this action accelerators
430         return _accelerators;
431     }
432     /// returs const array of accelerators
433     @property const(Accelerator)[] accelerators() const {
434         // check for accelerators override in settings
435         Accelerator[] res = findActionAccelerators(_id);
436         if (res) {
437             //Log.d("Found accelerators ", res);
438             return res;
439         }
440         return _accelerators;
441     }
442     /// returns text description for first accelerator of action; null if no accelerators
443     @property dstring acceleratorText() {
444         if (_accelerators.length < 1)
445             return null;
446         return _accelerators[0].label;
447     }
448     /// returns tooltip text for action
449     @property dstring tooltipText() {
450         dchar[] buf;
451         // strip out & characters
452         foreach(ch; label) {
453             if (ch != '&')
454                 buf ~= ch;
455         }
456         dstring accel = acceleratorText;
457         if (accel.length > 0) {
458             buf ~= " ("d;
459             buf ~= accel;
460             buf ~= ")"d;
461         }
462         return cast(dstring)buf;
463     }
464     /// adds one more accelerator
465     Action addAccelerator(uint keyCode, uint keyFlags = 0) {
466         _accelerators ~= Accelerator(keyCode, keyFlags);
467         return this;
468     }
469     /// adds one more accelerator only if platform is OSX
470     Action addMacAccelerator(uint keyCode, uint keyFlags = 0) {
471         version (OSX) {
472             _accelerators ~= Accelerator(keyCode, keyFlags);
473         }
474         return this;
475     }
476     /// adds one more accelerator only if platform is not OSX
477     Action addNonMacAccelerator(uint keyCode, uint keyFlags = 0) {
478         version (OSX) {
479         } else {
480             _accelerators ~= Accelerator(keyCode, keyFlags);
481         }
482         return this;
483     }
484     /// returns true if accelerator matches provided key code and flags
485     bool checkAccelerator(uint keyCode, uint keyFlags) const {
486         foreach(a; _accelerators) {
487             if (a.keyCode == keyCode && matchKeyFlags(keyFlags, a.keyFlags))
488                 return true;
489         }
490         return false;
491     }
492     /// returns action id
493     @property int id() const {
494         return _id;
495     }
496     /// sets action id
497     @property Action id(int newId) {
498         _id = newId;
499         return this;
500     }
501     /// compares id of this action with another action id
502     bool opEquals(int anotherActionId) const {
503         return _id == anotherActionId;
504     }
505     /// compares id of this action with another action id
506     bool opEquals(const Action action) const {
507         return _id == action._id;
508     }
509     /// sets label string resource id
510     @property Action label(string resourceId) {
511         _label = resourceId;
512         return this;
513     }
514     /// sets label unicode string
515     @property Action label(dstring text) {
516         _label = text;
517         return this;
518     }
519     /// returns label unicode string (translates if resource id is set)
520     @property dstring label() const {
521         return _label.value;
522     }
523     /// access to label UIString
524     @property ref const (UIString) labelValue() const {
525         return _label;
526     }
527     /// returns icon resource id
528     @property string iconId() const {
529         return _iconId;
530     }
531     /// sets icon resource id
532     @property Action iconId(string id) {
533         _iconId = id;
534         return this;
535     }
536     /// returns true if it's dummy action to specify separator
537     @property bool isSeparator() const {
538         return _id == SEPARATOR_ACTION_ID;
539     }
540 
541     override string toString() const {
542         return "Action(" ~ to!string(_id) ~ ")";
543     }
544 }
545 
546 /// use this ID for menu and toolbar separators
547 const int SEPARATOR_ACTION_ID = -1;
548 __gshared Action ACTION_SEPARATOR = new Action(SEPARATOR_ACTION_ID);
549 
550 
551 /// Map of Accelerator to Action
552 struct ActionMap {
553     protected Action[Accelerator] _map;
554     /// Add all actions from list
555     void add(ActionList items) {
556         foreach(a; items) {
557             foreach(acc; a.accelerators)
558                 _map[acc] = a;
559         }
560     }
561     /// Add array of actions
562     void add(Action[] items) {
563         foreach(a; items) {
564             foreach(acc; a.accelerators)
565                 _map[acc] = a;
566         }
567     }
568     /// Add array of actions
569     void add(const Action[] items) {
570         foreach(a; items) {
571             foreach(acc; a.accelerators)
572                 _map[acc] = a.clone;
573         }
574     }
575     /// Add action
576     void add(Action a) {
577         foreach(acc; a.accelerators)
578             _map[acc] = a;
579     }
580     private static __gshared immutable uint[] flagMasks = [
581         KeyFlag.LRControl | KeyFlag.LRAlt | KeyFlag.LRShift | KeyFlag.LRMenu,
582 
583         KeyFlag.LRControl | KeyFlag.LRAlt | KeyFlag.LRShift | KeyFlag.LRMenu,
584         KeyFlag.LRControl | KeyFlag.Alt | KeyFlag.LRShift | KeyFlag.LRMenu,
585         KeyFlag.LRControl | KeyFlag.LRAlt | KeyFlag.Shift | KeyFlag.LRMenu,
586         KeyFlag.LRControl | KeyFlag.LRAlt | KeyFlag.LRShift | KeyFlag.Menu,
587 
588         KeyFlag.Control | KeyFlag.Alt | KeyFlag.LRShift | KeyFlag.LRMenu,
589         KeyFlag.Control | KeyFlag.LRAlt | KeyFlag.Shift | KeyFlag.LRMenu,
590         KeyFlag.Control | KeyFlag.LRAlt | KeyFlag.LRShift | KeyFlag.Menu,
591         KeyFlag.LRControl | KeyFlag.Alt | KeyFlag.Shift | KeyFlag.LRMenu,
592         KeyFlag.LRControl | KeyFlag.Alt | KeyFlag.LRShift | KeyFlag.Menu,
593         KeyFlag.LRControl | KeyFlag.LRAlt | KeyFlag.Shift | KeyFlag.Menu,
594 
595         KeyFlag.Control | KeyFlag.Alt | KeyFlag.Shift | KeyFlag.LRMenu,
596         KeyFlag.Control | KeyFlag.Alt | KeyFlag.LRShift | KeyFlag.Menu,
597         KeyFlag.Control | KeyFlag.LRAlt | KeyFlag.Shift | KeyFlag.Menu,
598         KeyFlag.LRControl | KeyFlag.Alt | KeyFlag.Shift | KeyFlag.Menu,
599 
600         KeyFlag.Control | KeyFlag.Alt | KeyFlag.Shift | KeyFlag.Menu
601     ];
602     /// Aind action by key, return null if not found
603     Action findByKey(uint keyCode, uint flags) {
604         Accelerator acc;
605         acc.keyCode = keyCode;
606         foreach(mask; flagMasks) {
607             acc.keyFlags = flags & mask;
608             if (auto p = acc in _map) {
609                 if (p.checkAccelerator(keyCode, flags))
610                     return *p;
611             }
612         }
613         return null;
614     }
615 }
616 
617 /// List of Actions, for looking up Action by key
618 struct ActionList {
619     private Collection!Action _actions;
620     alias _actions this;
621 
622     /// Add several actions from array
623     void add(Action[] items) {
624         foreach(a; items)
625             _actions ~= a;
626     }
627 
628     /// Add all items from another list
629     void add(ref ActionList items) {
630         foreach(a; items)
631             _actions ~= a;
632     }
633 
634     /// Find action by key, return null if not found
635     Action findByKey(uint keyCode, uint flags) {
636         foreach(a; _actions)
637             if (a.checkAccelerator(keyCode, flags))
638                 return a;
639         return null;
640     }
641 }
642 
643 /// Mouse action codes for MouseEvent
644 enum MouseAction : ubyte {
645     /// button down handling is cancelled
646     Cancel,   
647     /// button is down
648     ButtonDown, 
649     /// button is up
650     ButtonUp, 
651     /// mouse pointer is moving
652     Move,     
653     /// pointer is back inside widget while button is down after FocusOut
654     FocusIn,  
655     /// pointer moved outside of widget while button was down (if handler returns true, Move events will be sent even while pointer is outside widget)
656     FocusOut, 
657     /// scroll wheel movement
658     Wheel,    
659     //Hover,    // pointer entered widget which while button was not down (return true to track Hover state)
660     /// pointer left widget which has before processed Move message, while button was not down
661     Leave     
662 }
663 
664 /// Mouse flag bits (mouse buttons and keyboard modifiers) for MouseEvent
665 enum MouseFlag : ushort {
666     // mouse buttons
667     /// Left mouse button is down
668     LButton = 0x0001,
669     /// Middle mouse button is down
670     MButton = 0x0010,
671     /// Right mouse button is down
672     RButton = 0x0002,
673     /// X1 mouse button is down
674     XButton1= 0x0020,
675     /// X2 mouse button is down
676     XButton2= 0x0040,
677 
678     // keyboard modifiers
679     /// Ctrl key is down
680     Control = 0x0008,
681     /// Shift key is down
682     Shift   = 0x0004,
683     /// Alt key is down
684     Alt     = 0x0080,
685 
686     /// Mask for mouse button flags
687     ButtonsMask = LButton | MButton | RButton | XButton1 | XButton2,
688     /// Mask for keyboard flags
689     KeyMask = Control|Shift|Alt,
690 }
691 
692 /// Mouse button codes for MouseEvent
693 enum MouseButton : ubyte {
694     /// no button
695     None,
696     /// left mouse button
697     Left = MouseFlag.LButton,
698     /// right mouse button
699     Right = MouseFlag.RButton,
700     /// right mouse button
701     Middle = MouseFlag.MButton,
702     /// additional mouse button 1
703     XButton1 = MouseFlag.XButton1, // additional button 1
704     /// additional mouse button 2
705     XButton2 = MouseFlag.XButton2, // additional button 2
706 }
707 
708 /// converts MouseButton to MouseFlag
709 ushort mouseButtonToFlag(MouseButton btn) {
710     switch(btn) with (MouseButton) {
711         case Left: return MouseFlag.LButton;
712         case Right: return MouseFlag.RButton;
713         case Middle: return MouseFlag.MButton;
714         case XButton1: return MouseFlag.XButton1;
715         case XButton2: return MouseFlag.XButton2;
716         default: return 0;
717     }
718 }
719 
720 /// double click max interval, milliseconds; may be changed by platform
721 __gshared long DOUBLE_CLICK_THRESHOLD_MS = 400;
722 
723 /// Mouse button state details for MouseEvent
724 struct ButtonDetails {
725     /// Clock.currStdTime() for down event of this button (0 if button is up) set after double click to time when first click occured.
726     protected long  _prevDownTs;
727     /// Clock.currStdTime() for down event of this button (0 if button is up).
728     protected long  _downTs;
729     /// Clock.currStdTime() for up event of this button (0 if button is still down).
730     protected long  _upTs;
731     /// x coordinates of down event
732     protected short _downX;
733     /// y coordinates of down event
734     protected short _downY;
735     /// mouse button flags when down event occured
736     protected ushort _downFlags;
737     /// true if button is made down shortly after up - valid if button is down
738     protected bool _doubleClick;
739     /// true if button is made down twice shortly after up - valid if button is down
740     protected bool _tripleClick;
741 
742     /// Returns true if button is made down shortly after up
743     @property bool doubleClick() {
744         return _doubleClick;
745     }
746 
747     /// Returns true if button is made down twice shortly after up
748     @property bool tripleClick() {
749         return _tripleClick;
750     }
751 
752 
753 
754     void reset() {
755         _downTs = _upTs = 0;
756         _downFlags = 0;
757         _downX = _downY = 0;
758     }
759 
760     /// update for button down
761     void down(short x, short y, ushort flags) {
762         static import std.datetime;
763         //Log.d("Button down ", x, ",", y, " _downTs=", _downTs, " _upTs=", _upTs);
764         long oldDownTs = _downTs;
765         _downX = x;
766         _downY = y;
767         _downFlags = flags;
768         _upTs = 0;
769         _downTs = std.datetime.Clock.currStdTime;
770         long downIntervalMs = (_downTs - oldDownTs) / 10000;
771         long prevDownIntervalMs = (_downTs - _prevDownTs) / 10000;
772         //Log.d("Button down ", x, ",", y, " _downTs=", _downTs, " _upTs=", _upTs, " downInterval=", downIntervalMs);
773         _tripleClick = (prevDownIntervalMs && prevDownIntervalMs < DOUBLE_CLICK_THRESHOLD_MS * 2);
774         _doubleClick = !_tripleClick && (oldDownTs && downIntervalMs < DOUBLE_CLICK_THRESHOLD_MS);
775         _prevDownTs = _doubleClick ? oldDownTs : 0;
776     }
777     /// update for button up
778     void up(short x, short y, ushort flags) {
779         static import std.datetime;
780         //Log.d("Button up ", x, ",", y, " _downTs=", _downTs, " _upTs=", _upTs);
781         _doubleClick = false;
782         _tripleClick = false;
783         _upTs = std.datetime.Clock.currStdTime;
784     }
785     /// returns true if button is currently pressed
786     @property bool isDown() { return _downTs != 0 && _upTs == 0; }
787     /// returns button down state duration in hnsecs (1/10000 of second).
788     @property int downDuration() {
789         static import std.datetime;
790         if (_downTs == 0)
791             return 0;
792         if (_downTs != 0 && _upTs != 0)
793             return cast(int)(_upTs - _downTs);
794         long ts = std.datetime.Clock.currStdTime;
795         return cast(int)(ts - _downTs);
796     }
797     /// X coordinate of point where button was pressed Down last time
798     @property short downX() { return _downX; }
799     /// Y coordinate of point where button was pressed Down last time
800     @property short downY() { return _downY; }
801     /// bit set of mouse flags saved on button down
802     @property ushort downFlags() { return _downFlags; }
803 }
804 
805 /** 
806     Mouse event
807  */
808 class MouseEvent {
809     /// timestamp of event
810     protected long _eventTimestamp;
811     /// mouse action code
812     protected MouseAction _action;
813     /// mouse button code for ButtonUp/ButtonDown
814     protected MouseButton _button;
815     /// x coordinate of pointer
816     protected short _x;
817     /// y coordinate of pointer
818     protected short _y;
819     /// flags bit set - usually from MouseFlag enum
820     protected ushort _flags;
821     /// wheel delta
822     protected short _wheelDelta;
823     /// widget which currently tracks mouse events
824     protected Widget _trackingWidget;
825     /// left button state details
826     protected ButtonDetails _lbutton;
827     /// middle button state details
828     protected ButtonDetails _mbutton;
829     /// right button state details
830     protected ButtonDetails _rbutton;
831     /// when true, no tracking of mouse on ButtonDown is necessary
832     protected bool _doNotTrackButtonDown;
833     /// left button state details
834     @property ref ButtonDetails lbutton() { return _lbutton; }
835     /// right button state details
836     @property ref ButtonDetails rbutton() { return _rbutton; }
837     /// middle button state details
838     @property ref ButtonDetails mbutton() { return _mbutton; }
839     /// button state details for event's button
840     @property ref ButtonDetails buttonDetails() { 
841         if (_button == MouseButton.Right)
842             return _rbutton; 
843         if (_button == MouseButton.Middle)
844             return _mbutton; 
845         return _lbutton;
846     }
847     /// button which caused ButtonUp or ButtonDown action
848     @property MouseButton button() { return _button; }
849     /// action
850     @property MouseAction action() { return _action; }
851     /// override action code (for usage from platform code)
852     void changeAction(MouseAction a) { _action = a; }
853     /// returns flags (buttons and keys state)
854     @property ushort flags() { return _flags; }
855     /// returns mouse button flags only
856     @property ushort buttonFlags() { return _flags & MouseFlag.ButtonsMask; }
857     /// returns keyboard modifier flags only
858     @property ushort keyFlags() { return _flags & MouseFlag.KeyMask; }
859     /// returns delta for Wheel event
860     @property short wheelDelta() { return _wheelDelta; }
861     /// x coordinate of mouse pointer (relative to window client area)
862     @property short x() { return _x; }
863     /// y coordinate of mouse pointer (relative to window client area)
864     @property short y() { return _y; }
865 
866     /// returns point for mouse cursor position
867     @property Point pos() { return Point(_x, _y); }
868 
869     /// returns true if no modifier flags are set
870     @property bool noModifiers() { return (_flags & (MouseFlag.Control | MouseFlag.Alt | MouseFlag.Shift)) == 0; }
871     /// returns true if any modifier flag is set
872     @property bool hasModifiers() { return !noModifiers; }
873 
874 
875     /// Returns true for ButtonDown event when button is pressed second time in short interval after pressing first time
876     @property bool doubleClick() {
877         if (_action != MouseAction.ButtonDown)
878             return false;
879         return buttonDetails.doubleClick;
880     }
881 
882     /// Returns true for ButtonDown event when button is pressed third time in short interval after pressing first time
883     @property bool tripleClick() {
884         if (_action != MouseAction.ButtonDown)
885             return false;
886         return buttonDetails.tripleClick;
887     }
888 
889     /// get event tracking widget to override
890     @property Widget trackingWidget() { return _trackingWidget; }
891     /// returns mouse button tracking flag
892     @property bool doNotTrackButtonDown() { return _doNotTrackButtonDown; }
893     /// sets mouse button tracking flag
894     @property void doNotTrackButtonDown(bool flg) { _doNotTrackButtonDown = flg; }
895     /// override mouse tracking widget
896     void track(Widget w) {
897         _trackingWidget = w;
898     }
899     /// copy constructor
900     this (MouseEvent e) {
901         _eventTimestamp = e._eventTimestamp;
902         _action = e._action;
903         _button = e._button;
904         _flags = e._flags;
905         _x = e._x;
906         _y = e._y;
907         _lbutton = e._lbutton;
908         _rbutton = e._rbutton;
909         _mbutton = e._mbutton;
910         _wheelDelta = e._wheelDelta;
911     }
912     /// construct mouse event from data
913     this (MouseAction a, MouseButton b, ushort f, short x, short y, short wheelDelta = 0) {
914         static import std.datetime;
915         _eventTimestamp = std.datetime.Clock.currStdTime;
916         _action = a;
917         _button = b;
918         _flags = f;
919         _x = x;
920         _y = y;
921         _wheelDelta = wheelDelta;
922     }
923 
924 	override @property string toString() {
925 		import std.conv;
926 		return "MouseEvent(" ~ to!string(_action) ~ ", " ~ to!string(cast(MouseButton)_button) ~ ", " ~ "%04x".format(_flags) ~ ", (" ~ to!string(_x) ~ "," ~ to!string(y) ~ "))";
927 	}
928 
929 }
930 
931 /// Keyboard actions for KeyEvent
932 enum KeyAction : uint {
933     /// key is pressed
934     KeyDown,
935     /// key is released
936     KeyUp,
937     /// text is entered
938     Text,
939     /// repeated key down
940     Repeat,
941 }
942 
943 /// Keyboard flags for KeyEvent
944 enum KeyFlag : uint {
945     /// Ctrl key is down
946     Control = 0x0008,
947     /// Shift key is down
948     Shift   = 0x0004,
949     /// Alt key is down
950     Alt     = 0x0080,
951     Option  = Alt,
952     /// Menu key
953     Menu    = 0x0040,
954     Command = Menu,
955     // Flags not counting left or right difference
956     MainFlags = 0xFF,
957     /// Right Ctrl key is down
958     RControl = 0x0108,
959     /// Right Shift key is down
960     RShift   = 0x0104,
961     /// Right Alt key is down
962     RAlt     = 0x0180,
963     /// Left Ctrl key is down
964     LControl = 0x0208,
965     /// Left Shift key is down
966     LShift   = 0x0204,
967     /// Left Alt key is down
968     LAlt     = 0x0280,
969     /// Left Menu/Win key is down
970     LMenu    = 0x0240,
971     /// Right Menu/Win key is down
972     RMenu    = 0x0140,
973 
974     LRControl = LControl | RControl, // both left and right
975     LRAlt = LAlt | RAlt, // both left and right
976     LRShift = LShift | RShift, // both left and right
977     LRMenu = LMenu | RMenu, // both left and right
978 }
979 
980 /// Key code constants for KeyEvent
981 enum KeyCode : uint {
982     NONE = 0,
983     /// backspace
984     BACK = 8,
985     /// tab
986     TAB = 9,
987     /// return / enter key
988     RETURN = 0x0D,
989     /// shift
990     SHIFT = 0x10,
991     /// ctrl
992     CONTROL = 0x11,
993     /// alt
994     ALT = 0x12, // VK_MENU
995     /// pause
996     PAUSE = 0x13,
997     /// caps lock
998     CAPS = 0x14, // VK_CAPITAL, caps lock
999     /// esc
1000     ESCAPE = 0x1B, // esc
1001     /// space
1002     SPACE = 0x20,
1003     /// page up
1004     PAGEUP = 0x21, // VK_PRIOR
1005     /// page down
1006     PAGEDOWN = 0x22, // VK_NEXT
1007     /// end
1008     END = 0x23, // VK_END
1009     /// home
1010     HOME = 0x24, // VK_HOME
1011     /// left arrow
1012     LEFT = 0x25,
1013     /// up arrow
1014     UP = 0x26,
1015     /// right arrow
1016     RIGHT = 0x27,
1017     /// down arrow
1018     DOWN = 0x28,
1019     /// ins
1020     INS = 0x2D,
1021     /// delete
1022     DEL = 0x2E,
1023     /// 0
1024     KEY_0 = 0x30,
1025     /// 1
1026     KEY_1 = 0x31,
1027     /// 2
1028     KEY_2 = 0x32,
1029     /// 3
1030     KEY_3 = 0x33,
1031     /// 4
1032     KEY_4 = 0x34,
1033     /// 5
1034     KEY_5 = 0x35,
1035     /// 6
1036     KEY_6 = 0x36,
1037     /// 7
1038     KEY_7 = 0x37,
1039     /// 8
1040     KEY_8 = 0x38,
1041     /// 9
1042     KEY_9 = 0x39,
1043     /// A
1044     KEY_A = 0x41,
1045     /// B
1046     KEY_B = 0x42,
1047     /// C
1048     KEY_C = 0x43,
1049     /// D
1050     KEY_D = 0x44,
1051     /// E
1052     KEY_E = 0x45,
1053     /// F
1054     KEY_F = 0x46,
1055     /// G
1056     KEY_G = 0x47,
1057     /// H
1058     KEY_H = 0x48,
1059     /// I
1060     KEY_I = 0x49,
1061     /// J
1062     KEY_J = 0x4a,
1063     /// K
1064     KEY_K = 0x4b,
1065     /// L
1066     KEY_L = 0x4c,
1067     /// M
1068     KEY_M = 0x4d,
1069     /// N
1070     KEY_N = 0x4e,
1071     /// O
1072     KEY_O = 0x4f,
1073     /// P
1074     KEY_P = 0x50,
1075     /// Q
1076     KEY_Q = 0x51,
1077     /// R
1078     KEY_R = 0x52,
1079     /// S
1080     KEY_S = 0x53,
1081     /// T
1082     KEY_T = 0x54,
1083     /// U
1084     KEY_U = 0x55,
1085     /// V
1086     KEY_V = 0x56,
1087     /// W
1088     KEY_W = 0x57,
1089     /// X
1090     KEY_X = 0x58,
1091     /// Y
1092     KEY_Y = 0x59,
1093     /// Z
1094     KEY_Z = 0x5a,
1095     /// [
1096     KEY_BRACKETOPEN = 0xDB,
1097     /// ]
1098     KEY_BRACKETCLOSE = 0xDD,
1099     /// key /
1100     KEY_DIVIDE = 0x6F,
1101     /// key +
1102     KEY_ADD = 0x6B,
1103     /// key *
1104     KEY_MULTIPLY = 0x6A,
1105     /// key ,
1106     KEY_COMMA = 0xBC,
1107     /// key .
1108     KEY_PERIOD = 0xBE,
1109     /// key -
1110     KEY_SUBTRACT = 0x6D,
1111     /// left win key
1112     LWIN = 0x5b,
1113     /// right win key
1114     RWIN = 0x5c,
1115     /// numpad 0
1116     NUM_0 = 0x60,
1117     /// numpad 1
1118     NUM_1 = 0x61,
1119     /// numpad 2
1120     NUM_2 = 0x62,
1121     /// numpad 3
1122     NUM_3 = 0x63,
1123     /// numpad 4
1124     NUM_4 = 0x64,
1125     /// numpad 5
1126     NUM_5 = 0x65,
1127     /// numpad 6
1128     NUM_6 = 0x66,
1129     /// numpad 7
1130     NUM_7 = 0x67,
1131     /// numpad 8
1132     NUM_8 = 0x68,
1133     /// numpad 9
1134     NUM_9 = 0x69,
1135     /// numpad *
1136     MUL = 0x6A,
1137     /// numpad +
1138     ADD = 0x6B,
1139     /// numpad /
1140     DIV = 0x6F,
1141     /// numpad -
1142     SUB = 0x6D,
1143     /// numpad .
1144     DECIMAL = 0x6E,
1145     /// F1
1146     F1 = 0x70,
1147     /// F2
1148     F2 = 0x71,
1149     /// F3
1150     F3 = 0x72,
1151     /// F4
1152     F4 = 0x73,
1153     /// F5
1154     F5 = 0x74,
1155     /// F6
1156     F6 = 0x75,
1157     /// F7
1158     F7 = 0x76,
1159     /// F8
1160     F8 = 0x77,
1161     /// F9
1162     F9 = 0x78,
1163     /// F10
1164     F10 = 0x79,
1165     /// F11
1166     F11 = 0x7a,
1167     /// F12
1168     F12 = 0x7b,
1169     /// F13
1170     F13 = 0x7c,
1171     /// F14
1172     F14 = 0x7d,
1173     /// F15
1174     F15 = 0x7e,
1175     /// F16
1176     F16 = 0x7f,
1177     /// F17
1178     F17 = 0x80,
1179     /// F18
1180     F18 = 0x81,
1181     /// F19
1182     F19 = 0x82,
1183     /// F20
1184     F20 = 0x83,
1185     /// F21
1186     F21 = 0x84,
1187     /// F22
1188     F22 = 0x85,
1189     /// F23
1190     F23 = 0x86,
1191     /// F24
1192     F24 = 0x87,
1193     /// num lock
1194     NUMLOCK = 0x90,
1195     /// scroll lock
1196     SCROLL = 0x91, // scroll lock
1197     /// left shift
1198     LSHIFT = 0xA0,
1199     /// right shift
1200     RSHIFT = 0xA1,
1201     /// left ctrl
1202     LCONTROL = 0xA2,
1203     /// right ctrl
1204     RCONTROL = 0xA3,
1205     /// left alt
1206     LALT = 0xA4,
1207     /// right alt
1208     RALT = 0xA5,
1209     //LMENU = 0xA4, //VK_LMENU
1210     //RMENU = 0xA5,
1211     /// ;
1212     SEMICOLON = 0x201, 
1213     /// ~
1214     TILDE = 0x202,
1215     /// '
1216     QUOTE = 0x203,
1217     /// /
1218     SLASH = 0x204,
1219     /// \
1220     BACKSLASH = 0x205,
1221     /// =
1222     EQUAL = 0x206,
1223 }
1224 
1225 /// Keyboard event
1226 class KeyEvent {
1227     /// action
1228     protected KeyAction _action;
1229     /// key code, usually from KeyCode enum
1230     protected uint _keyCode;
1231     /// key flags bit set, usually combined from KeyFlag enum
1232     protected uint _flags;
1233     /// entered text
1234     protected dstring _text;
1235     /// key action (KeyDown, KeyUp, Text, Repeat)
1236     @property KeyAction action() { return _action; }
1237     /// key code (usually from KeyCode enum)
1238     @property uint keyCode() { return _keyCode; }
1239     /// flags (shift, ctrl, alt...) - KeyFlag enum
1240     @property uint flags() { return _flags; }
1241     /// entered text, for Text action
1242     @property dstring text() { return _text; }
1243 
1244     /// returns true if no modifier flags are set
1245     @property bool noModifiers() { return (_flags & (KeyFlag.Control | KeyFlag.Alt | KeyFlag.Menu | KeyFlag.Shift)) == 0; }
1246     /// returns true if any modifier flag is set
1247     @property bool hasModifiers() { return !noModifiers; }
1248 
1249     /// create key event
1250     this(KeyAction action, uint keyCode, uint flags, dstring text = null) {
1251         _action = action;
1252         _keyCode = keyCode;
1253         _flags = flags;
1254         _text = text;
1255     }
1256 	override @property string toString() {
1257 		import std.conv;
1258 		return "KeyEvent(" ~ to!string(_action) ~ ", " ~ to!string(cast(KeyCode)_keyCode) ~ ", " ~ "%04x".format(_flags) ~ ", \"" ~ toUTF8(_text) ~ "\")";
1259 	}
1260 }
1261 
1262 /// Scroll bar / slider action codes for ScrollEvent.
1263 enum ScrollAction : ubyte {
1264     /// space above indicator pressed
1265     PageUp,
1266     /// space below indicator pressed
1267     PageDown, 
1268     /// up/left button pressed
1269     LineUp,
1270     /// down/right button pressed
1271     LineDown,
1272     /// slider pressed
1273     SliderPressed,
1274     /// dragging in progress
1275     SliderMoved,
1276     /// dragging finished
1277     SliderReleased
1278 }
1279 
1280 /// Slider/scrollbar event
1281 class ScrollEvent {
1282     private ScrollAction _action;
1283     private int _minValue;
1284     private int _maxValue;
1285     private int _pageSize;
1286     private int _position;
1287     private bool _positionChanged;
1288     /// action
1289     @property ScrollAction action() { return _action; }
1290     /// min value
1291     @property int minValue() { return _minValue; }
1292     /// max value
1293     @property int maxValue() { return _maxValue; }
1294     /// visible part size
1295     @property int pageSize() { return _pageSize; }
1296     /// current position
1297     @property int position() { return _position; }
1298     /// returns true if position has been changed using position property setter
1299     @property bool positionChanged() { return _positionChanged; }
1300     /// change position in event handler to update slider position
1301     @property void position(int newPosition) { _position = newPosition; _positionChanged = true; }
1302     /// create scroll event
1303     this(ScrollAction action, int minValue, int maxValue, int pageSize, int position) {
1304         _action = action;
1305         _minValue = minValue;
1306         _maxValue = maxValue;
1307         _pageSize = pageSize;
1308         _position = position;
1309     }
1310     /// default update position for actions like PageUp/PageDown, LineUp/LineDown
1311     int defaultUpdatePosition() {
1312         int delta = 0;
1313         switch (_action) with(ScrollAction)
1314         {
1315             case LineUp:
1316                 delta = _pageSize / 20;
1317                 if (delta < 1)
1318                     delta = 1;
1319                 delta = -delta;
1320                 break;
1321             case LineDown:
1322                 delta = _pageSize / 20;
1323                 if (delta < 1)
1324                     delta = 1;
1325                 break;
1326             case PageUp:
1327                 delta = _pageSize * 3 / 4;
1328                 if (delta < 1)
1329                     delta = 1;
1330                 delta = -delta;
1331                 break;
1332             case PageDown:
1333                 delta = _pageSize * 3 / 4;
1334                 if (delta < 1)
1335                     delta = 1;
1336                 break;
1337             default:
1338                 return position;
1339         }
1340         int newPosition = _position + delta;
1341         if (newPosition > _maxValue - _pageSize)
1342             newPosition = _maxValue - _pageSize;
1343         if (newPosition < _minValue)
1344             newPosition = _minValue;
1345         if (_position != newPosition)
1346             position = newPosition;
1347         return position;
1348     }
1349 }
1350 
1351 /** 
1352 Converts key name to KeyCode enum value
1353 For unknown key code, returns 0
1354 */
1355 uint parseKeyName(string name) {
1356     switch (name) {
1357         case "A": case "a": return KeyCode.KEY_A;
1358         case "B": case "b": return KeyCode.KEY_B;
1359         case "C": case "c": return KeyCode.KEY_C;
1360         case "D": case "d": return KeyCode.KEY_D;
1361         case "E": case "e": return KeyCode.KEY_E;
1362         case "F": case "f": return KeyCode.KEY_F;
1363         case "G": case "g": return KeyCode.KEY_G;
1364         case "H": case "h": return KeyCode.KEY_H;
1365         case "I": case "i": return KeyCode.KEY_I;
1366         case "J": case "j": return KeyCode.KEY_J;
1367         case "K": case "k": return KeyCode.KEY_K;
1368         case "L": case "l": return KeyCode.KEY_L;
1369         case "M": case "m": return KeyCode.KEY_M;
1370         case "N": case "n": return KeyCode.KEY_N;
1371         case "O": case "o": return KeyCode.KEY_O;
1372         case "P": case "p": return KeyCode.KEY_P;
1373         case "Q": case "q": return KeyCode.KEY_Q;
1374         case "R": case "r": return KeyCode.KEY_R;
1375         case "S": case "s": return KeyCode.KEY_S;
1376         case "T": case "t": return KeyCode.KEY_T;
1377         case "U": case "u": return KeyCode.KEY_U;
1378         case "V": case "v": return KeyCode.KEY_V;
1379         case "W": case "w": return KeyCode.KEY_W;
1380         case "X": case "x": return KeyCode.KEY_X;
1381         case "Y": case "y": return KeyCode.KEY_Y;
1382         case "Z": case "z": return KeyCode.KEY_Z;
1383         case "F1": return KeyCode.F1;
1384         case "F2": return KeyCode.F2;
1385         case "F3": return KeyCode.F3;
1386         case "F4": return KeyCode.F4;
1387         case "F5": return KeyCode.F5;
1388         case "F6": return KeyCode.F6;
1389         case "F7": return KeyCode.F7;
1390         case "F8": return KeyCode.F8;
1391         case "F9": return KeyCode.F9;
1392         case "F10": return KeyCode.F10;
1393         case "F11": return KeyCode.F11;
1394         case "F12": return KeyCode.F12;
1395         case "F13": return KeyCode.F13;
1396         case "F14": return KeyCode.F14;
1397         case "F15": return KeyCode.F15;
1398         case "F16": return KeyCode.F16;
1399         case "F17": return KeyCode.F17;
1400         case "F18": return KeyCode.F18;
1401         case "F19": return KeyCode.F19;
1402         case "F20": return KeyCode.F20;
1403         case "F21": return KeyCode.F21;
1404         case "F22": return KeyCode.F22;
1405         case "F23": return KeyCode.F23;
1406         case "F24": return KeyCode.F24;
1407         case "/": return KeyCode.KEY_DIVIDE;
1408         case "*": return KeyCode.KEY_MULTIPLY;
1409         case "Tab": return KeyCode.TAB;
1410         case "PageUp": return KeyCode.PAGEUP;
1411         case "PageDown": return KeyCode.PAGEDOWN;
1412         case "Home": return KeyCode.HOME;
1413         case "End": return KeyCode.END;
1414         case "Left": return KeyCode.LEFT;
1415         case "Right": return KeyCode.RIGHT;
1416         case "Up": return KeyCode.UP;
1417         case "Down": return KeyCode.DOWN;
1418         case "Ins": return KeyCode.INS;
1419         case "Del": return KeyCode.DEL;
1420         case "[": return KeyCode.KEY_BRACKETOPEN;
1421         case "]": return KeyCode.KEY_BRACKETCLOSE;
1422         case ",": return KeyCode.KEY_COMMA;
1423         case ".": return KeyCode.KEY_PERIOD;
1424         case "Backspace": return KeyCode.BACK;
1425         case "Enter": return KeyCode.RETURN;
1426         case "Space": return KeyCode.SPACE;
1427         default:
1428             return 0;
1429     }
1430 }
1431 
1432 /** 
1433     Converts KeyCode enum value to human readable key name 
1434 
1435     For unknown key code, prints its hex value.
1436 */
1437 string keyName(uint keyCode) {
1438     switch (keyCode) {
1439         case KeyCode.KEY_A:
1440             return "A";
1441         case KeyCode.KEY_B:
1442             return "B";
1443         case KeyCode.KEY_C:
1444             return "C";
1445         case KeyCode.KEY_D:
1446             return "D";
1447         case KeyCode.KEY_E:
1448             return "E";
1449         case KeyCode.KEY_F:
1450             return "F";
1451         case KeyCode.KEY_G:
1452             return "G";
1453         case KeyCode.KEY_H:
1454             return "H";
1455         case KeyCode.KEY_I:
1456             return "I";
1457         case KeyCode.KEY_J:
1458             return "J";
1459         case KeyCode.KEY_K:
1460             return "K";
1461         case KeyCode.KEY_L:
1462             return "L";
1463         case KeyCode.KEY_M:
1464             return "M";
1465         case KeyCode.KEY_N:
1466             return "N";
1467         case KeyCode.KEY_O:
1468             return "O";
1469         case KeyCode.KEY_P:
1470             return "P";
1471         case KeyCode.KEY_Q:
1472             return "Q";
1473         case KeyCode.KEY_R:
1474             return "R";
1475         case KeyCode.KEY_S:
1476             return "S";
1477         case KeyCode.KEY_T:
1478             return "T";
1479         case KeyCode.KEY_U:
1480             return "U";
1481         case KeyCode.KEY_V:
1482             return "V";
1483         case KeyCode.KEY_W:
1484             return "W";
1485         case KeyCode.KEY_X:
1486             return "X";
1487         case KeyCode.KEY_Y:
1488             return "Y";
1489         case KeyCode.KEY_Z:
1490             return "Z";
1491         case KeyCode.KEY_0:
1492             return "0";
1493         case KeyCode.KEY_1:
1494             return "1";
1495         case KeyCode.KEY_2:
1496             return "2";
1497         case KeyCode.KEY_3:
1498             return "3";
1499         case KeyCode.KEY_4:
1500             return "4";
1501         case KeyCode.KEY_5:
1502             return "5";
1503         case KeyCode.KEY_6:
1504             return "6";
1505         case KeyCode.KEY_7:
1506             return "7";
1507         case KeyCode.KEY_8:
1508             return "8";
1509         case KeyCode.KEY_9:
1510             return "9";
1511         case KeyCode.KEY_DIVIDE:
1512             return "/";
1513         case KeyCode.KEY_MULTIPLY:
1514             return "*";
1515         case KeyCode.TAB:
1516             return "Tab";
1517         case KeyCode.F1:
1518             return "F1";
1519         case KeyCode.F2:
1520             return "F2";
1521         case KeyCode.F3:
1522             return "F3";
1523         case KeyCode.F4:
1524             return "F4";
1525         case KeyCode.F5:
1526             return "F5";
1527         case KeyCode.F6:
1528             return "F6";
1529         case KeyCode.F7:
1530             return "F7";
1531         case KeyCode.F8:
1532             return "F8";
1533         case KeyCode.F9:
1534             return "F9";
1535         case KeyCode.F10:
1536             return "F10";
1537         case KeyCode.F11:
1538             return "F11";
1539         case KeyCode.F12:
1540             return "F12";
1541         case KeyCode.F13:
1542             return "F13";
1543         case KeyCode.F14:
1544             return "F14";
1545         case KeyCode.F15:
1546             return "F15";
1547         case KeyCode.F16:
1548             return "F16";
1549         case KeyCode.F17:
1550             return "F17";
1551         case KeyCode.F18:
1552             return "F18";
1553         case KeyCode.F19:
1554             return "F19";
1555         case KeyCode.F20:
1556             return "F20";
1557         case KeyCode.F21:
1558             return "F21";
1559         case KeyCode.F22:
1560             return "F22";
1561         case KeyCode.F23:
1562             return "F23";
1563         case KeyCode.F24:
1564             return "F24";
1565         case KeyCode.PAGEUP:
1566             return "PageUp";
1567         case KeyCode.PAGEDOWN:
1568             return "PageDown";
1569         case KeyCode.HOME:
1570             return "Home";
1571         case KeyCode.END:
1572             return "End";
1573         case KeyCode.LEFT:
1574             return "Left";
1575         case KeyCode.RIGHT:
1576             return "Right";
1577         case KeyCode.UP:
1578             return "Up";
1579         case KeyCode.DOWN:
1580             return "Down";
1581         case KeyCode.INS:
1582             return "Ins";
1583         case KeyCode.DEL:
1584             return "Del";
1585         case KeyCode.KEY_BRACKETOPEN:
1586             return "[";
1587         case KeyCode.KEY_BRACKETCLOSE:
1588             return "]";
1589         case KeyCode.BACK:
1590             return "Backspace";
1591         case KeyCode.SPACE:
1592             return "Space";
1593         case KeyCode.RETURN:
1594             return "Enter";
1595         default:
1596             return format("0x%08x", keyCode);
1597     }
1598 }
1599 
1600 /// base class for custom events
1601 class CustomEvent {
1602     protected int _id;
1603     protected uint _uniqueId;
1604 
1605     protected static __gshared uint _uniqueIdGenerator;
1606 
1607     protected Widget _destinationWidget;
1608     // event id
1609     @property int id() { return _id; }
1610     @property uint uniqueId() { return _uniqueId; }
1611     @property Widget destinationWidget() { return _destinationWidget; }
1612 
1613     protected Object _objectParam;
1614     @property Object objectParam() {
1615         return _objectParam;
1616     }
1617     @property CustomEvent objectParam(Object value) {
1618         _objectParam = value;
1619         return this;
1620     }
1621 
1622     protected int _intParam;
1623     @property int intParam() {
1624         return _intParam;
1625     }
1626     @property CustomEvent intParam(int value) {
1627         _intParam = value;
1628         return this;
1629     }
1630 
1631     this(int ID) {
1632         _id = ID;
1633         _uniqueId = ++_uniqueIdGenerator;
1634     }
1635 }
1636 
1637 immutable int CUSTOM_RUNNABLE = 1;
1638 
1639 /// operation to execute (usually sent from background threads to run some code in UI thread)
1640 class RunnableEvent : CustomEvent {
1641     protected void delegate() _action;
1642     this(int ID, Widget destinationWidget, void delegate() action) {
1643         super(ID);
1644         _destinationWidget = destinationWidget;
1645         _action = action;
1646     }
1647     void run() {
1648         _action();
1649     }
1650 }
1651 
1652 /**
1653 Queue destroy event.
1654 
1655 This event allows delayed widget destruction and is used internally by 
1656 $(LINK2 $(DDOX_ROOT_DIR)dlangui/platforms/common/platform/Window.queueWidgetDestroy.html, Window.queueWidgetDestroy()).
1657 */
1658 class QueueDestroyEvent : RunnableEvent {
1659     private Widget _widgetToDestroy;
1660     this (Widget widgetToDestroy)
1661     {
1662         _widgetToDestroy = widgetToDestroy;
1663         super(1,null, delegate void () {
1664             if (_widgetToDestroy.parent) 
1665                 _widgetToDestroy.parent.removeChild(_widgetToDestroy);
1666             destroy(_widgetToDestroy);
1667         });
1668     }
1669 }
1670 
1671 interface CustomEventTarget {
1672     /// post event to handle in UI thread (this method can be used from background thread)
1673     void postEvent(CustomEvent event);
1674 
1675     /// post task to execute in UI thread (this method can be used from background thread)
1676     void executeInUiThread(void delegate() runnable);
1677 }
1678 
1679 private static __gshared string[int] actionIdToNameMap;
1680 private static __gshared int[string] actionNameToIdMap;
1681 private static __gshared Accelerator[][int] actionAcceleratorsMap;
1682 
1683 /// clear global action accelerators map
1684 void clearActionAcceleratorsMap() {
1685     destroy(actionAcceleratorsMap);
1686 }
1687 /// overrides accelerators for action by id
1688 void setActionAccelerators(int actionId, Accelerator[] accelerators) {
1689     actionAcceleratorsMap[actionId] = accelerators;
1690 }
1691 /// lookup accelerators override for action by id
1692 Accelerator[] findActionAccelerators(int actionId) {
1693     if (auto found = actionId in actionAcceleratorsMap)
1694         return *found;
1695     return null;
1696 }
1697 /// lookup accelerators override for action by name (e.g. "EditorActions.ToggleLineComment")
1698 Accelerator[] findActionAccelerators(string actionName) {
1699     int actionId = actionNameToId(actionName);
1700     if (actionId == 0)
1701         return null;
1702     if (auto found = actionAcceleratorsMap[actionId])
1703         return found;
1704     return null;
1705 }
1706 
1707 /// converts id to name for actions registered by registerActionEnum, returns null if not found
1708 string actionIdToName(int id) {
1709     if (auto name = id in actionIdToNameMap) {
1710         return *name;
1711     }
1712     return null;
1713 }
1714 
1715 /// converts action name id for for actions registered by registerActionEnum, returns 0 if not found
1716 int actionNameToId(string name) {
1717     if (auto id = name in actionNameToIdMap) {
1718         return *id;
1719     }
1720     return 0;
1721 }
1722 
1723 private string[] enumMemberNames(T)() if (is(T == enum)) {
1724     import std.traits;
1725     string[] res;
1726     foreach(item; __traits(allMembers, T)) {
1727         res ~= T.stringof ~ "." ~ item;
1728     }
1729     return res;
1730 }
1731 
1732 private int[] enumMemberValues(T)() if (is(T == enum)) {
1733     import std.traits;
1734     int[] res;
1735     foreach(item; EnumMembers!T) {
1736         res ~= cast(int)item;
1737     }
1738     return res;
1739 }
1740 
1741 /// register enum items as action names and ids for lookup by actionIdToName and actionNameToId functions (names will be generated as "EnumName.EnumItemName")
1742 void registerActionEnum(T)() if (is(T == enum)) {
1743     immutable int[] memberValues = enumMemberValues!T;
1744     immutable string[] memberNames = enumMemberNames!T;
1745     //pragma(msg, enumMemberValues!T);
1746     //pragma(msg, enumMemberNames!T);
1747     foreach(i; 0 .. memberValues.length) {
1748         actionIdToNameMap[memberValues[i]] = memberNames[i];
1749         actionNameToIdMap[memberNames[i]] = memberValues[i];
1750     }
1751 }