utils.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
  242. return (
  243. grabOp === Meta.GrabOp.RESIZING_N ||
  244. grabOp === Meta.GrabOp.RESIZING_E ||
  245. grabOp === Meta.GrabOp.RESIZING_W ||
  246. grabOp === Meta.GrabOp.RESIZING_S ||
  247. grabOp === Meta.GrabOp.RESIZING_NE ||
  248. grabOp === Meta.GrabOp.RESIZING_NW ||
  249. grabOp === Meta.GrabOp.RESIZING_SE ||
  250. grabOp === Meta.GrabOp.RESIZING_SW ||
  251. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
  252. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  253. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
  254. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
  255. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
  256. );
  257. }
  258. export function grabMode(grabOp) {
  259. grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
  260. if (
  261. grabOp === Meta.GrabOp.RESIZING_N ||
  262. grabOp === Meta.GrabOp.RESIZING_E ||
  263. grabOp === Meta.GrabOp.RESIZING_W ||
  264. grabOp === Meta.GrabOp.RESIZING_S ||
  265. grabOp === Meta.GrabOp.RESIZING_NE ||
  266. grabOp === Meta.GrabOp.RESIZING_NW ||
  267. grabOp === Meta.GrabOp.RESIZING_SE ||
  268. grabOp === Meta.GrabOp.RESIZING_SW ||
  269. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
  270. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
  271. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
  272. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
  273. grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
  274. ) {
  275. return GRAB_TYPES.RESIZING;
  276. } else if (
  277. grabOp === Meta.GrabOp.KEYBOARD_MOVING ||
  278. grabOp === Meta.GrabOp.MOVING ||
  279. grabOp === Meta.GrabOp.MOVING_UNCONSTRAINED
  280. ) {
  281. return GRAB_TYPES.MOVING;
  282. }
  283. return GRAB_TYPES.UNKNOWN;
  284. }
  285. export function decomposeGrabOp(grabOp) {
  286. grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
  287. switch (grabOp) {
  288. case Meta.GrabOp.RESIZING_NE:
  289. return [Meta.GrabOp.RESIZING_N, Meta.GrabOp.RESIZING_E];
  290. case Meta.GrabOp.RESIZING_NW:
  291. return [Meta.GrabOp.RESIZING_N, Meta.GrabOp.RESIZING_W];
  292. case Meta.GrabOp.RESIZING_SE:
  293. return [Meta.GrabOp.RESIZING_S, Meta.GrabOp.RESIZING_E];
  294. case Meta.GrabOp.RESIZING_SW:
  295. return [Meta.GrabOp.RESIZING_S, Meta.GrabOp.RESIZING_W];
  296. default:
  297. return [grabOp];
  298. }
  299. }
  300. export function directionFromGrab(grabOp) {
  301. if (grabOp === Meta.GrabOp.RESIZING_E || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E) {
  302. return Meta.MotionDirection.RIGHT;
  303. } else if (grabOp === Meta.GrabOp.RESIZING_W || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W) {
  304. return Meta.MotionDirection.LEFT;
  305. } else if (grabOp === Meta.GrabOp.RESIZING_N || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N) {
  306. return Meta.MotionDirection.UP;
  307. } else if (grabOp === Meta.GrabOp.RESIZING_S || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S) {
  308. return Meta.MotionDirection.DOWN;
  309. }
  310. }
  311. export function removeGapOnRect(rectWithGap, gap) {
  312. rectWithGap.x = rectWithGap.x -= gap;
  313. rectWithGap.y = rectWithGap.y -= gap;
  314. rectWithGap.width = rectWithGap.width += gap * 2;
  315. rectWithGap.height = rectWithGap.height += gap * 2;
  316. return rectWithGap;
  317. }
  318. // Credits: PopShell
  319. export function findWindowWith(title) {
  320. let display = global.display;
  321. let type = Meta.TabList.NORMAL_ALL;
  322. let workspaceMgr = display.get_workspace_manager();
  323. let workspaces = workspaceMgr.get_n_workspaces();
  324. for (let wsId = 1; wsId <= workspaces; wsId++) {
  325. let workspace = workspaceMgr.get_workspace_by_index(wsId);
  326. for (const metaWindow of display.get_tab_list(type, workspace)) {
  327. if (
  328. metaWindow.title &&
  329. title &&
  330. (metaWindow.title === title || metaWindow.title.includes(title))
  331. ) {
  332. return metaWindow;
  333. }
  334. }
  335. }
  336. return undefined;
  337. }
  338. export function oppositeDirectionOf(direction) {
  339. if (direction === Meta.MotionDirection.LEFT) {
  340. return Meta.MotionDirection.RIGHT;
  341. } else if (direction === Meta.MotionDirection.RIGHT) {
  342. return Meta.MotionDirection.LEFT;
  343. } else if (direction === Meta.MotionDirection.UP) {
  344. return Meta.MotionDirection.DOWN;
  345. } else if (direction === Meta.MotionDirection.DOWN) {
  346. return Meta.MotionDirection.UP;
  347. }
  348. }
  349. export function monitorIndex(monitorValue) {
  350. if (!monitorValue) return -1;
  351. let wsIndex = monitorValue.indexOf("ws");
  352. let indexVal = monitorValue.slice(0, wsIndex);
  353. indexVal = indexVal.replace("mo", "");
  354. return parseInt(indexVal);
  355. }
  356. export function _disableDecorations() {
  357. let decos = global.window_group.get_children().filter((a) => a.type != null);
  358. decos.forEach((d) => {
  359. global.window_group.remove_child(d);
  360. d.destroy();
  361. });
  362. }
  363. export function dpi() {
  364. return St.ThemeContext.get_for_stage(global.stage).scale_factor;
  365. }
  366. export function isGnome(majorVersion) {
  367. return major == majorVersion;
  368. }
  369. export function isGnomeGTE(majorVersion) {
  370. return major >= majorVersion;
  371. }