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