utils.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * This file is part of the Forge extension for GNOME
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * Credits:
  18. * This file has some code from Dash-To-Panel extension: convenience.js
  19. * Some code was also adapted from the upstream Gnome Shell source code.
  20. */
  21. // Gnome imports
  22. import Meta from "gi://Meta";
  23. import St from "gi://St";
  24. // Gnome-shell imports
  25. import { PACKAGE_VERSION } from "resource:///org/gnome/shell/misc/config.js";
  26. // App imports
  27. import { ORIENTATION_TYPES, LAYOUT_TYPES, POSITION } from "./tree.js";
  28. import { GRAB_TYPES } from "./window.js";
  29. const [major] = PACKAGE_VERSION.split(".").map((s) => Number(s));
  30. /**
  31. *
  32. * Turns an array into an immutable enum-like object
  33. *
  34. */
  35. export function createEnum(anArray) {
  36. const enumObj = {};
  37. for (const val of anArray) {
  38. enumObj[val] = val;
  39. }
  40. return Object.freeze(enumObj);
  41. }
  42. export function resolveX(rectRequest, metaWindow) {
  43. let metaRect = metaWindow.get_frame_rect();
  44. let monitorRect = metaWindow.get_work_area_current_monitor();
  45. let val = metaRect.x;
  46. let x = rectRequest.x;
  47. switch (typeof x) {
  48. case "string": //center,
  49. switch (x) {
  50. case "center":
  51. val = monitorRect.width * 0.5 - this.resolveWidth(rectRequest, metaWindow) * 0.5;
  52. break;
  53. case "left":
  54. val = 0;
  55. break;
  56. case "right":
  57. val = monitorRect.width - this.resolveWidth(rectRequest, metaWindow);
  58. break;
  59. default:
  60. break;
  61. }
  62. break;
  63. case "number":
  64. val = x;
  65. break;
  66. default:
  67. break;
  68. }
  69. val = monitorRect.x + val;
  70. return val;
  71. }
  72. export function resolveY(rectRequest, metaWindow) {
  73. let metaRect = metaWindow.get_frame_rect();
  74. let monitorRect = metaWindow.get_work_area_current_monitor();
  75. let val = metaRect.y;
  76. let y = rectRequest.y;
  77. switch (typeof y) {
  78. case "string": //center,
  79. switch (y) {
  80. case "center":
  81. val = monitorRect.height * 0.5 - this.resolveHeight(rectRequest, metaWindow) * 0.5;
  82. break;
  83. case "top":
  84. val = 0;
  85. break;
  86. case "bottom": // inverse of y=0
  87. val = monitorRect.height - this.resolveHeight(rectRequest, metaWindow);
  88. break;
  89. default:
  90. break;
  91. }
  92. break;
  93. case "number":
  94. val = y;
  95. break;
  96. default:
  97. break;
  98. }
  99. val = monitorRect.y + val;
  100. return val;
  101. }
  102. export function resolveWidth(rectRequest, metaWindow) {
  103. let metaRect = metaWindow.get_frame_rect();
  104. let monitorRect = metaWindow.get_work_area_current_monitor();
  105. let val = metaRect.width;
  106. let width = rectRequest.width;
  107. switch (typeof width) {
  108. case "number":
  109. if (Number.isInteger(width) && width != 1) {
  110. val = width;
  111. } else {
  112. let monitorWidth = monitorRect.width;
  113. val = monitorWidth * width;
  114. }
  115. break;
  116. default:
  117. break;
  118. }
  119. return val;
  120. }
  121. export function resolveHeight(rectRequest, metaWindow) {
  122. let metaRect = metaWindow.get_frame_rect();
  123. let monitorRect = metaWindow.get_work_area_current_monitor();
  124. let val = metaRect.height;
  125. let height = rectRequest.height;
  126. switch (typeof height) {
  127. case "number":
  128. if (Number.isInteger(height) && height != 1) {
  129. val = height;
  130. } else {
  131. let monitorHeight = monitorRect.height;
  132. val = monitorHeight * height;
  133. }
  134. break;
  135. default:
  136. break;
  137. }
  138. return val;
  139. }
  140. export function orientationFromDirection(direction) {
  141. return direction === Meta.MotionDirection.LEFT || direction === Meta.MotionDirection.RIGHT
  142. ? ORIENTATION_TYPES.HORIZONTAL
  143. : ORIENTATION_TYPES.VERTICAL;
  144. }
  145. export function orientationFromLayout(layout) {
  146. switch (layout) {
  147. case LAYOUT_TYPES.HSPLIT:
  148. case LAYOUT_TYPES.TABBED:
  149. return ORIENTATION_TYPES.HORIZONTAL;
  150. case LAYOUT_TYPES.VSPLIT:
  151. case LAYOUT_TYPES.STACKED:
  152. return ORIENTATION_TYPES.VERTICAL;
  153. default:
  154. break;
  155. }
  156. }
  157. export function positionFromDirection(direction) {
  158. return direction === Meta.MotionDirection.LEFT || direction === Meta.MotionDirection.UP
  159. ? POSITION.BEFORE
  160. : POSITION.AFTER;
  161. }
  162. export function resolveDirection(directionString) {
  163. if (directionString) {
  164. directionString = directionString.toUpperCase();
  165. if (directionString === "LEFT") {
  166. return Meta.MotionDirection.LEFT;
  167. }
  168. if (directionString === "RIGHT") {
  169. return Meta.MotionDirection.RIGHT;
  170. }
  171. if (directionString === "UP") {
  172. return Meta.MotionDirection.UP;
  173. }
  174. if (directionString === "DOWN") {
  175. return Meta.MotionDirection.DOWN;
  176. }
  177. }
  178. return null;
  179. }
  180. export function directionFrom(position, orientation) {
  181. if (position === POSITION.AFTER) {
  182. if (orientation === ORIENTATION_TYPES.HORIZONTAL) {
  183. return Meta.DisplayDirection.RIGHT;
  184. } else {
  185. return Meta.DisplayDirection.DOWN;
  186. }
  187. } else if (position === POSITION.BEFORE) {
  188. if (orientation === ORIENTATION_TYPES.HORIZONTAL) {
  189. return Meta.DisplayDirection.LEFT;
  190. } else {
  191. return Meta.DisplayDirection.UP;
  192. }
  193. }
  194. }
  195. export function rectContainsPoint(rect, pointP) {
  196. if (!(rect && pointP)) return false;
  197. return (
  198. rect.x <= pointP[0] &&
  199. pointP[0] <= rect.x + rect.width &&
  200. rect.y <= pointP[1] &&
  201. pointP[1] <= rect.y + rect.height
  202. );
  203. }
  204. export function orientationFromGrab(grabOp) {
  205. if (
  206. grabOp === Meta.GrabOp.RESIZING_N ||
  207. grabOp === Meta.GrabOp.RESIZING_S ||
  208. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
  209. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S
  210. ) {
  211. return ORIENTATION_TYPES.VERTICAL;
  212. } else if (
  213. grabOp === Meta.GrabOp.RESIZING_E ||
  214. grabOp === Meta.GrabOp.RESIZING_W ||
  215. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  216. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W
  217. ) {
  218. return ORIENTATION_TYPES.HORIZONTAL;
  219. }
  220. return ORIENTATION_TYPES.NONE;
  221. }
  222. export function positionFromGrabOp(grabOp) {
  223. if (
  224. grabOp === Meta.GrabOp.RESIZING_W ||
  225. grabOp === Meta.GrabOp.RESIZING_N ||
  226. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
  227. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N
  228. ) {
  229. return POSITION.BEFORE;
  230. } else if (
  231. grabOp === Meta.GrabOp.RESIZING_E ||
  232. grabOp === Meta.GrabOp.RESIZING_S ||
  233. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  234. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S
  235. ) {
  236. return POSITION.AFTER;
  237. }
  238. return POSITION.UNKNOWN;
  239. }
  240. export function allowResizeGrabOp(grabOp) {
  241. return (
  242. grabOp === Meta.GrabOp.RESIZING_N ||
  243. grabOp === Meta.GrabOp.RESIZING_E ||
  244. grabOp === Meta.GrabOp.RESIZING_W ||
  245. grabOp === Meta.GrabOp.RESIZING_S ||
  246. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
  247. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  248. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
  249. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
  250. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
  251. );
  252. }
  253. export function grabMode(grabOp) {
  254. if (
  255. grabOp === Meta.GrabOp.RESIZING_N ||
  256. grabOp === Meta.GrabOp.RESIZING_E ||
  257. grabOp === Meta.GrabOp.RESIZING_W ||
  258. grabOp === Meta.GrabOp.RESIZING_S ||
  259. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
  260. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  261. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
  262. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
  263. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
  264. ) {
  265. return GRAB_TYPES.RESIZING;
  266. } else if (
  267. grabOp === Meta.GrabOp.KEYBOARD_MOVING ||
  268. grabOp === Meta.GrabOp.MOVING ||
  269. grabOp === Meta.GrabOp.MOVING_UNCONSTRAINED
  270. ) {
  271. return GRAB_TYPES.MOVING;
  272. }
  273. return GRAB_TYPES.UNKNOWN;
  274. }
  275. export function directionFromGrab(grabOp) {
  276. if (grabOp === Meta.GrabOp.RESIZING_E || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E) {
  277. return Meta.MotionDirection.RIGHT;
  278. } else if (grabOp === Meta.GrabOp.RESIZING_W || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W) {
  279. return Meta.MotionDirection.LEFT;
  280. } else if (grabOp === Meta.GrabOp.RESIZING_N || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N) {
  281. return Meta.MotionDirection.UP;
  282. } else if (grabOp === Meta.GrabOp.RESIZING_S || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S) {
  283. return Meta.MotionDirection.DOWN;
  284. }
  285. }
  286. export function removeGapOnRect(rectWithGap, gap) {
  287. rectWithGap.x = rectWithGap.x -= gap;
  288. rectWithGap.y = rectWithGap.y -= gap;
  289. rectWithGap.width = rectWithGap.width += gap * 2;
  290. rectWithGap.height = rectWithGap.height += gap * 2;
  291. return rectWithGap;
  292. }
  293. // Credits: PopShell
  294. export function findWindowWith(title) {
  295. let display = global.display;
  296. let type = Meta.TabList.NORMAL_ALL;
  297. let workspaceMgr = display.get_workspace_manager();
  298. let workspaces = workspaceMgr.get_n_workspaces();
  299. for (let wsId = 1; wsId <= workspaces; wsId++) {
  300. let workspace = workspaceMgr.get_workspace_by_index(wsId);
  301. for (const metaWindow of display.get_tab_list(type, workspace)) {
  302. if (
  303. metaWindow.title &&
  304. title &&
  305. (metaWindow.title === title || metaWindow.title.includes(title))
  306. ) {
  307. return metaWindow;
  308. }
  309. }
  310. }
  311. return undefined;
  312. }
  313. export function oppositeDirectionOf(direction) {
  314. if (direction === Meta.MotionDirection.LEFT) {
  315. return Meta.MotionDirection.RIGHT;
  316. } else if (direction === Meta.MotionDirection.RIGHT) {
  317. return Meta.MotionDirection.LEFT;
  318. } else if (direction === Meta.MotionDirection.UP) {
  319. return Meta.MotionDirection.DOWN;
  320. } else if (direction === Meta.MotionDirection.DOWN) {
  321. return Meta.MotionDirection.UP;
  322. }
  323. }
  324. export function monitorIndex(monitorValue) {
  325. if (!monitorValue) return -1;
  326. let wsIndex = monitorValue.indexOf("ws");
  327. let indexVal = monitorValue.slice(0, wsIndex);
  328. indexVal = indexVal.replace("mo", "");
  329. return parseInt(indexVal);
  330. }
  331. export function _disableDecorations() {
  332. let decos = global.window_group.get_children().filter((a) => a.type != null);
  333. decos.forEach((d) => {
  334. global.window_group.remove_child(d);
  335. d.destroy();
  336. });
  337. }
  338. export function dpi() {
  339. return St.ThemeContext.get_for_stage(global.stage).scale_factor;
  340. }
  341. export function isGnome(majorVersion) {
  342. return major == majorVersion;
  343. }
  344. export function isGnomeGTE(majorVersion) {
  345. return major >= majorVersion;
  346. }