1 module rpui.shortcuts; 2 3 import std.stdio; 4 import std..string; 5 import std.conv; 6 import std.path; 7 8 import rpdl; 9 import rpdl.exception; 10 import rpui.input; 11 import rpui.widget; 12 import rpui.paths; 13 14 struct Shortcut { 15 bool shift; 16 bool ctrl; 17 bool alt; 18 KeyCode key; 19 20 private this(bool shift, bool ctrl, bool alt, KeyCode key) { 21 this.shift = shift; 22 this.ctrl = ctrl; 23 this.alt = alt; 24 this.key = key; 25 } 26 27 /** 28 * Fill `Shortcut` fields from string. 29 * 30 * Example: 31 * --- 32 * readKey("Ctrl"); // will assign `true` to `ctrl` 33 * readKey("A"); // will assign `input.KeyCode.A` to `key` 34 * --- 35 */ 36 void readKey(in string key) { 37 switch (key) { 38 case "Ctrl": 39 this.ctrl = true; 40 break; 41 42 case "Shift": 43 this.shift = true; 44 break; 45 46 case "Alt": 47 this.alt = true; 48 break; 49 50 default: 51 this.key = to!KeyCode(key); 52 } 53 } 54 55 /** 56 * Parsing shortcut from string - all keys will split by '+' symbol. 57 * 58 * Example: 59 * --- 60 * Shortcut("Ctrl+C"); // ctrl = true; key = KeyCode.C; 61 * Shortcut("Ctrl+Shift+S"); // ctrl = true; shift = true; key = KeyCode.C; 62 * --- 63 */ 64 this(in string shortcut) { 65 foreach (string key; shortcut.split("+")) { 66 readKey(key); 67 } 68 } 69 } 70 71 final class Shortcuts { 72 private ShortcutAction[string] shortcuts; /// Available shortcuts. 73 private RpdlTree shortcutsData; /// Shortcuts declared in `rpdl` file. 74 75 /// Action to be invoked when shortcut is pressed. 76 struct ShortcutAction { 77 /// Not yet used. 78 enum Type { 79 simpleWidgetListener, 80 simpleFunction 81 } 82 83 Shortcut[] shortcuts; /// Composition of shortcuts e.g. Ctrl+X Ctrl+S. 84 85 /** 86 * Create action from `shortcutString` - constructor will fill `shortcuts` 87 * by parsing string - all shortcuts will split by space symbol. 88 * 89 * Example: 90 * --- 91 * // Corresponds to: shortcuts ~= Shortcut("Ctrl+X") 92 * ShortcutAction("Ctrl+X") 93 * 94 * // Corresponds to: shortcuts ~= Shortcut("Ctrl+X") ~ Shortcut("Alt+Shift+C") 95 * ShortcutAction("Ctrl+X Alt+C") 96 * --- 97 */ 98 this(in string shortcutString, void delegate() action = null) { 99 foreach (shortcut; shortcutString.split(" ")) { 100 shortcuts ~= Shortcut(shortcut); 101 } 102 103 this.action = action; 104 } 105 106 void delegate() action; 107 } 108 109 /// Load shortcuts from `rpdl` file by `fileName`; `fileName` is absolute. 110 this(in string fileName) { 111 shortcutsData = new RpdlTree(dirName(fileName)); 112 shortcutsData.load(baseName(fileName), RpdlTree.FileType.text); 113 } 114 115 /// Load shortcuts from `rpdl` file relative to $(I resources/ui/shortcuts). 116 static createFromFile(in string fileName) { 117 const paths = createPathes(); 118 const string path = buildPath(paths.resources, "ui", "shortcuts", fileName); 119 return new Shortcuts(path); 120 } 121 122 /// Handle on key released and invoke action if shortcut is pressed. 123 void onKeyReleased(in KeyCode key) { 124 foreach (shortcut; shortcuts) { 125 if (doShortcut(shortcut)) 126 return; 127 } 128 } 129 130 /// Attach `action` to particular shortcut placed by `path` (in `rpdl` file) 131 void attachByPath(in string path, void delegate() action) { 132 try { 133 const shortcut = shortcutsData.data.getString(path ~ ".0"); 134 auto shortcutAction = Shortcuts.ShortcutAction(shortcut, action); 135 136 shortcuts[path] = shortcutAction; 137 } catch (NotFoundException) { 138 debug assert(false, "Not found shortcut with path " ~ path); 139 } 140 } 141 142 /// Attach `action` to particular shortcut 143 void attach(in string shortcut, void delegate() action) { 144 auto shortcutAction = Shortcuts.ShortcutAction(shortcut, action); 145 shortcuts[shortcut] = shortcutAction; 146 } 147 148 string getShourtcutString(in string path) { 149 return shortcutsData.data.getString(path ~ ".0"); 150 } 151 152 /// 153 void merge(Shortcuts shortcuts) { 154 shortcutsData.merge(shortcuts.shortcutsData); 155 } 156 157 /// Invoke shortcut action if all keys from shortcut is pressed. 158 private bool doShortcut(in ShortcutAction shortcutAction) { 159 const Shortcut shortcut = shortcutAction.shortcuts[0]; 160 161 if (isKeyPressed(shortcut.key) && 162 testKeyState(KeyCode.Shift, shortcut.shift) && 163 testKeyState(KeyCode.Ctrl, shortcut.ctrl) && 164 testKeyState(KeyCode.Alt, shortcut.alt)) 165 { 166 shortcutAction.action(); 167 return true; 168 } 169 170 return false; 171 } 172 }