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 }