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