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