keybindingHandler.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /**
  2. * © gnome-shell-extension-tiling-assistant
  3. * Source: https://git.launchpad.net/ubuntu/+source/gnome-shell-extension-tiling-assistant/tree/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js
  4. *
  5. * This code is partially reused from Ubuntu tiling assistant extension (GPL-2.0).
  6. *
  7. * enableArrowBinding and disableArrowBinding are new methods.
  8. * Other methods are modified for our use case.
  9. */
  10. import * as Main from 'resource:///org/gnome/shell/ui/main.js';
  11. import Meta from 'gi://Meta';
  12. import Shell from 'gi://Shell';
  13. import GLib from 'gi://GLib';
  14. import {Shortcut} from '../common.js';
  15. import {Direction} from '../tileWindowManager.js';
  16. const MAX_RESIZE_GAP = 50;
  17. const MIN_RESIZE_GAP = 10;
  18. /**
  19. * Class to handle the keyboard shortcuts (on the extension side) except the
  20. * ones related to the Layouts. For those, see layoutsManager.js.
  21. */
  22. export default class KeybindingHandler {
  23. _keyBindings;
  24. _windowManager;
  25. _extensionObject = null;
  26. _settings;
  27. _waitingAction;
  28. _enableArrow = false;
  29. _prevArrowTyped;
  30. constructor(windowManager, extension) {
  31. this._extensionObject = extension;
  32. this._settings = extension.getSettings();
  33. this._windowManager = windowManager;
  34. this._prevArrowTyped = [Direction.North, 0];
  35. this._keyBindings = Shortcut.getShortcuts();
  36. this._keyBindings.forEach(key => {
  37. Main.wm.addKeybinding(key, this._settings, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Shell.ActionMode.NORMAL, this._onCustomKeybindingPressed.bind(this, key));
  38. });
  39. }
  40. destroy() {
  41. this._keyBindings.forEach(key => Main.wm.removeKeybinding(key));
  42. this.disableArrowBinding();
  43. }
  44. enableArrowBinding() {
  45. let bindings = ['arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'key-escape'];
  46. bindings.forEach(key => {
  47. Main.wm.addKeybinding(key, this._settings, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Shell.ActionMode.NORMAL, this._onCustomKeybindingPressed.bind(this, key));
  48. });
  49. }
  50. disableArrowBinding() {
  51. let bindings = ['arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'key-escape'];
  52. bindings.forEach(key => Main.wm.removeKeybinding(key));
  53. }
  54. compute_gap(direction) {
  55. const t = GLib.get_real_time();
  56. const dir = this._prevArrowTyped[0];
  57. const time = Math.min(Math.max(t - this._prevArrowTyped[1], 150000), 300000) - 150000;
  58. this._prevArrowTyped = [direction, t];
  59. let gap = dir === direction
  60. ? MAX_RESIZE_GAP - ((time * (MAX_RESIZE_GAP - MIN_RESIZE_GAP) / 150000))
  61. : MIN_RESIZE_GAP;
  62. return gap;
  63. }
  64. /**
  65. * @param {string} shortcutName
  66. */
  67. _onCustomKeybindingPressed(shortcutName) {
  68. switch (shortcutName) {
  69. case 'keybinding-close':
  70. global.display.get_focus_window().delete(global.get_current_time());
  71. break;
  72. case 'keybinding-open-settings':
  73. this._extensionObject?.openPreferences();
  74. break;
  75. case 'keybinding-rotation':
  76. this._windowManager.rotateWindow(global.display.get_focus_window());
  77. break;
  78. case 'keybinding-maximize':
  79. this._windowManager.maximizeTile(global.display.get_focus_window());
  80. break;
  81. case 'keybinding-search':
  82. this._windowManager.createSearchBar();
  83. break;
  84. case 'keybinding-refresh':
  85. // Unused
  86. this._windowManager.refresh();
  87. break;
  88. case 'keybinding-move-left':
  89. this._windowManager.moveTile(Direction.West);
  90. break;
  91. case 'keybinding-move-right':
  92. this._windowManager.moveTile(Direction.East);
  93. break;
  94. case 'keybinding-move-top':
  95. this._windowManager.moveTile(Direction.North);
  96. break;
  97. case 'keybinding-move-bottom':
  98. this._windowManager.moveTile(Direction.South);
  99. break;
  100. case 'keybinding-move':
  101. this._waitingAction = shortcutName;
  102. this.enableArrowBinding();
  103. break;
  104. case 'keybinding-resize':
  105. this._waitingAction = shortcutName;
  106. this.enableArrowBinding();
  107. break;
  108. case 'keybinding-resize-left':
  109. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_W);
  110. break;
  111. case 'keybinding-resize-right':
  112. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_E);
  113. break;
  114. case 'keybinding-resize-top':
  115. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_N);
  116. break;
  117. case 'keybinding-resize-bottom':
  118. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_S);
  119. break;
  120. case 'keybinding-focus':
  121. this._waitingAction = shortcutName;
  122. this.enableArrowBinding();
  123. break;
  124. case 'keybinding-focus-left':
  125. this._windowManager.changeFocus(Direction.West);
  126. break;
  127. case 'keybinding-focus-right':
  128. this._windowManager.changeFocus(Direction.East);
  129. break;
  130. case 'keybinding-focus-top':
  131. this._windowManager.changeFocus(Direction.North);
  132. break;
  133. case 'keybinding-focus-bottom':
  134. this._windowManager.changeFocus(Direction.South);
  135. break;
  136. case 'keybinding-next-workspace':
  137. this._windowManager.moveToWorkspace(true);
  138. break;
  139. case 'keybinding-previous-workspace':
  140. this._windowManager.moveToWorkspace(false);
  141. break;
  142. case 'keybinding-next-monitor':
  143. this._windowManager.moveToNextMonitor();
  144. break;
  145. case 'arrow-up':
  146. if (this._waitingAction === 'keybinding-move') {
  147. this._windowManager.moveTile(Direction.North);
  148. this.disableArrowBinding();
  149. } else if (this._waitingAction === 'keybinding-resize') {
  150. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_N, this.compute_gap(Direction.North));
  151. } else if (this._waitingAction === 'keybinding-focus') {
  152. this._windowManager.changeFocus(Direction.North);
  153. }
  154. break;
  155. case 'arrow-down':
  156. if (this._waitingAction === 'keybinding-move') {
  157. this._windowManager.moveTile(Direction.South);
  158. this.disableArrowBinding();
  159. } else if (this._waitingAction === 'keybinding-resize') {
  160. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_S, this.compute_gap(Direction.South));
  161. } else if (this._waitingAction === 'keybinding-focus') {
  162. this._windowManager.changeFocus(Direction.South);
  163. }
  164. break;
  165. case 'arrow-left':
  166. if (this._waitingAction === 'keybinding-move') {
  167. this._windowManager.moveTile(Direction.West);
  168. this.disableArrowBinding();
  169. } else if (this._waitingAction === 'keybinding-resize') {
  170. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_W, this.compute_gap(Direction.West));
  171. } else if (this._waitingAction === 'keybinding-focus') {
  172. this._windowManager.changeFocus(Direction.West);
  173. }
  174. break;
  175. case 'arrow-right':
  176. if (this._waitingAction === 'keybinding-move') {
  177. this._windowManager.moveTile(Direction.East);
  178. this.disableArrowBinding();
  179. } else if (this._waitingAction === 'keybinding-resize') {
  180. this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_E, this.compute_gap(Direction.East));
  181. } else if (this._waitingAction === 'keybinding-focus') {
  182. this._windowManager.changeFocus(Direction.East);
  183. }
  184. break;
  185. case 'key-escape':
  186. this.disableArrowBinding();
  187. break;
  188. default:
  189. break;
  190. }
  191. }
  192. }