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