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