[gnome] Move to Grimble tiling manager

This commit is contained in:
2026-01-14 00:55:43 -05:00
parent de41369f5f
commit 941c727d38
90 changed files with 3999 additions and 9602 deletions

View File

@ -1,43 +0,0 @@
import GObject from "gi://GObject";
import St from "gi://St";
import { ThemeManagerBase } from "../shared/theme.js";
import { Logger } from "../shared/logger.js";
import { production } from "../shared/settings.js";
export class ExtensionThemeManager extends ThemeManagerBase {
static {
GObject.registerClass(this);
}
/**
* @param {import("../../extension.js").default} extension
*/
constructor(extension) {
super(extension);
this.metadata = extension.metadata;
}
reloadStylesheet() {
const uuid = this.metadata.uuid;
const stylesheetFile = this.configMgr.stylesheetFile;
const defaultStylesheetFile = this.configMgr.defaultStylesheetFile;
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
try {
theme.unload_stylesheet(defaultStylesheetFile);
theme.unload_stylesheet(stylesheetFile);
if (production) {
theme.load_stylesheet(stylesheetFile);
this.stylesheet = stylesheetFile;
} else {
theme.load_stylesheet(defaultStylesheetFile);
this.stylesheet = defaultStylesheetFile;
}
} catch (e) {
Logger.error(`${uuid} - ${e}`);
return;
}
}
}

View File

@ -1,129 +0,0 @@
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import { gettext as _ } from "resource:///org/gnome/shell/extensions/extension.js";
import { QuickMenuToggle, SystemIndicator } from "resource:///org/gnome/shell/ui/quickSettings.js";
import {
PopupSwitchMenuItem,
PopupSeparatorMenuItem,
} from "resource:///org/gnome/shell/ui/popupMenu.js";
import * as Utils from "./utils.js";
import { Logger } from "../shared/logger.js";
const iconName = "view-grid-symbolic";
/** @typedef {import('../../extension.js').default} ForgeExtension */
class SettingsPopupSwitch extends PopupSwitchMenuItem {
static {
GObject.registerClass(this);
}
/** @type {ForgeExtension} extension */
extension;
/**
* @param {string} title
* @param {ForgeExtension} extension
* @param {string} bind
*/
constructor(title, extension, bind) {
const active = !!extension.settings.get_boolean(bind);
super(title, active);
this.extension = extension;
Logger.info(bind, active);
this.connect("toggled", (item) => this.extension.settings.set_boolean(bind, item.state));
}
}
export class FeatureMenuToggle extends QuickMenuToggle {
static {
GObject.registerClass(this);
}
constructor(extension) {
const title = _("Tiling");
const initSettings = Utils.isGnomeGTE(45)
? { title, iconName, toggleMode: true }
: { label: title, iconName, toggleMode: true };
super(initSettings);
this.extension = extension;
this.extension.settings.bind(
"tiling-mode-enabled",
this,
"checked",
Gio.SettingsBindFlags.DEFAULT
);
this.extension.settings.bind(
"quick-settings-enabled",
this,
"visible",
Gio.SettingsBindFlags.DEFAULT
);
this.menu.setHeader(iconName, _("Forge"), _("Tiling Window Management"));
this.menu.addMenuItem(
(this._singleSwitch = new SettingsPopupSwitch(
_("Gaps Hidden when Single"),
this.extension,
"window-gap-hidden-on-single"
))
);
this.menu.addMenuItem(
(this._focusHintSwitch = new SettingsPopupSwitch(
_("Show Focus Hint Border"),
this.extension,
"focus-border-toggle"
))
);
this.menu.addMenuItem(
(this._focusMovePointer = new SettingsPopupSwitch(
_("Move Pointer with the Focus"),
this.extension,
"move-pointer-focus-enabled"
))
);
// Add an entry-point for more settings
this.menu.addMenuItem(new PopupSeparatorMenuItem());
const settingsItem = this.menu.addAction(_("Settings"), () => this.extension.openPreferences());
// Ensure the settings are unavailable when the screen is locked
settingsItem.visible = Main.sessionMode.allowSettings;
this.menu._settingsActions[this.extension.uuid] = settingsItem;
}
}
export class FeatureIndicator extends SystemIndicator {
static {
GObject.registerClass(this);
}
constructor(extension) {
super();
this.extension = extension;
// Create the icon for the indicator
this._indicator = this._addIndicator();
this._indicator.icon_name = iconName;
const tilingModeEnabled = this.extension.settings.get_boolean("tiling-mode-enabled");
const quickSettingsEnabled = this.extension.settings.get_boolean("quick-settings-enabled");
this._indicator.visible = tilingModeEnabled && quickSettingsEnabled;
this.extension.settings.connect("changed", (_, name) => {
switch (name) {
case "tiling-mode-enabled":
case "quick-settings-enabled":
this._indicator.visible = this.extension.settings.get_boolean(name);
}
});
}
}

View File

@ -1,493 +0,0 @@
/*
* This file is part of the Forge extension for GNOME
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Gnome imports
import GObject from "gi://GObject";
import Meta from "gi://Meta";
import Shell from "gi://Shell";
// Gnome Shell imports
import * as Main from "resource:///org/gnome/shell/ui/main.js";
// Shared state
import { Logger } from "../shared/logger.js";
export class Keybindings extends GObject.Object {
static {
GObject.registerClass(this);
}
/** @type {import('./extension.js').default} */
ext;
constructor(ext) {
super();
Logger.debug(`created keybindings`);
this._grabbers = new Map();
// this._bindSignals();
this.ext = ext;
this.extWm = ext.extWm;
this.kbdSettings = ext.kbdSettings;
this.settings = ext.settings;
this.buildBindingDefinitions();
}
// @deprecated
_acceleratorActivate(action) {
let grabber = this._grabbers.get(action);
if (grabber) {
Logger.debug(`Firing accelerator ${grabber.accelerator} : ${grabber.name}`);
grabber.callback();
} else {
Logger.error(`No listeners [action={${action}}]`);
}
}
// @deprecated
_bindSignals() {
global.display.connect("accelerator-activated", (_display, action, _deviceId, _timestamp) => {
this._acceleratorActivate(action);
});
}
enable() {
let keybindings = this._bindings;
for (const key in keybindings) {
Main.wm.addKeybinding(
key,
this.kbdSettings,
Meta.KeyBindingFlags.NONE,
Shell.ActionMode.NORMAL,
keybindings[key]
);
}
Logger.debug(`keybindings:enable`);
}
disable() {
let keybindings = this._bindings;
for (const key in keybindings) {
Main.wm.removeKeybinding(key);
}
Logger.debug(`keybindings:disable`);
}
// @deprecated
enableListenForBindings() {
windowConfig.forEach((config) => {
config.shortcut.forEach((shortcut) => {
this.listenFor(shortcut, () => {
config.actions.forEach((action) => {
this.extWm.command(action);
});
});
});
});
}
// @deprecated
disableListenForBindings() {
// The existing grabber items are from the custom config by
// this extension.
this._grabbers.forEach((grabber) => {
global.display.ungrab_accelerator(grabber.action);
Main.wm.allowKeybinding(grabber.name, Shell.ActionMode.NONE);
});
this._grabbers.clear();
}
/**
* API for quick binding of keys to function. This is going to be useful with SpaceMode
*
* @param {String} accelerator - keybinding combinations
* @param {Function} callback - function to call when the accelerator is invoked
*
* Credits:
* - https://superuser.com/a/1182899
* - Adapted based on current Gnome-shell API or syntax
*/
listenFor(accelerator, callback) {
let grabFlags = Meta.KeyBindingFlags.NONE;
let action = global.display.grab_accelerator(accelerator, grabFlags);
if (action == Meta.KeyBindingAction.NONE) {
Logger.error(`Unable to grab accelerator [binding={${accelerator}}]`);
// TODO - check the gnome keybindings for conflicts and notify the user
} else {
let name = Meta.external_binding_name_for_action(action);
Logger.debug(`Requesting WM to allow binding [name={${name}}]`);
Main.wm.allowKeybinding(name, Shell.ActionMode.ALL);
this._grabbers.set(action, {
name: name,
accelerator: accelerator,
callback: callback,
action: action,
});
}
}
get modifierState() {
const [_x, _y, state] = this.extWm.getPointer();
return state;
}
allowDragDropTile() {
const tileModifier = this.kbdSettings.get_string("mod-mask-mouse-tile");
const modState = this.modifierState;
// Using Clutter.ModifierType values and also testing for pointer
// being grabbed (256). E.g. grabbed + pressing Super = 256 + 64 = 320
// See window.js#_handleMoving() - an overlay preview is shown.
// See window.js#_handleGrabOpEnd() - when the drag has been dropped
switch (tileModifier) {
case "Super":
return modState === 64 || modState === 320;
case "Alt":
return modState === 8 || modState === 264;
case "Ctrl":
return modState === 4 || modState === 260;
case "None":
return true;
}
return false;
}
buildBindingDefinitions() {
this._bindings = {
"window-toggle-float": () => {
let actions = [
{
name: "FloatToggle",
mode: "float",
x: "center",
y: "center",
width: 0.65,
height: 0.75,
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-toggle-always-float": () => {
let action = {
name: "FloatClassToggle",
mode: "float",
x: "center",
y: "center",
width: 0.65,
height: 0.75,
};
this.extWm.command(action);
},
"window-focus-left": () => {
let actions = [
{
name: "Focus",
direction: "Left",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-focus-down": () => {
let actions = [
{
name: "Focus",
direction: "Down",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-focus-up": () => {
let actions = [
{
name: "Focus",
direction: "Up",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-focus-right": () => {
let actions = [
{
name: "Focus",
direction: "Right",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-swap-left": () => {
let actions = [
{
name: "Swap",
direction: "Left",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-swap-down": () => {
let actions = [
{
name: "Swap",
direction: "Down",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-swap-up": () => {
let actions = [
{
name: "Swap",
direction: "Up",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-swap-right": () => {
let actions = [
{
name: "Swap",
direction: "Right",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-move-left": () => {
let actions = [
{
name: "Move",
direction: "Left",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-move-down": () => {
let actions = [
{
name: "Move",
direction: "Down",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-move-up": () => {
let actions = [
{
name: "Move",
direction: "Up",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"window-move-right": () => {
let actions = [
{
name: "Move",
direction: "Right",
},
];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"con-split-layout-toggle": () => {
let actions = [{ name: "LayoutToggle" }];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"con-split-vertical": () => {
let actions = [{ name: "Split", orientation: "vertical" }];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"con-split-horizontal": () => {
let actions = [{ name: "Split", orientation: "horizontal" }];
actions.forEach((action) => {
this.extWm.command(action);
});
},
"con-stacked-layout-toggle": () => {
let action = { name: "LayoutStackedToggle" };
this.extWm.command(action);
},
"con-tabbed-layout-toggle": () => {
let action = { name: "LayoutTabbedToggle" };
this.extWm.command(action);
},
"con-tabbed-showtab-decoration-toggle": () => {
let action = { name: "ShowTabDecorationToggle" };
this.extWm.command(action);
},
"focus-border-toggle": () => {
let action = { name: "FocusBorderToggle" };
this.extWm.command(action);
},
"prefs-tiling-toggle": () => {
let action = { name: "TilingModeToggle" };
this.extWm.command(action);
},
"window-gap-size-increase": () => {
let action = { name: "GapSize", amount: 1 };
this.extWm.command(action);
},
"window-gap-size-decrease": () => {
let action = { name: "GapSize", amount: -1 };
this.extWm.command(action);
},
"workspace-active-tile-toggle": () => {
let action = { name: "WorkspaceActiveTileToggle" };
this.extWm.command(action);
},
"prefs-open": () => {
let action = { name: "PrefsOpen" };
this.extWm.command(action);
},
"window-swap-last-active": () => {
let action = {
name: "WindowSwapLastActive",
};
this.extWm.command(action);
},
"window-snap-one-third-right": () => {
let action = {
name: "SnapLayoutMove",
direction: "Right",
amount: 1 / 3,
};
this.extWm.command(action);
},
"window-snap-two-third-right": () => {
let action = {
name: "SnapLayoutMove",
direction: "Right",
amount: 2 / 3,
};
this.extWm.command(action);
},
"window-snap-one-third-left": () => {
let action = {
name: "SnapLayoutMove",
direction: "Left",
amount: 1 / 3,
};
this.extWm.command(action);
},
"window-snap-two-third-left": () => {
let action = {
name: "SnapLayoutMove",
direction: "Left",
amount: 2 / 3,
};
this.extWm.command(action);
},
"window-snap-center": () => {
let action = {
name: "SnapLayoutMove",
direction: "Center",
};
this.extWm.command(action);
},
"window-resize-top-increase": () => {
let action = {
name: "WindowResizeTop",
amount: this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-top-decrease": () => {
let action = {
name: "WindowResizeTop",
amount: -1 * this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-bottom-increase": () => {
let action = {
name: "WindowResizeBottom",
amount: this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-bottom-decrease": () => {
let action = {
name: "WindowResizeBottom",
amount: -1 * this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-left-increase": () => {
let action = {
name: "WindowResizeLeft",
amount: this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-left-decrease": () => {
let action = {
name: "WindowResizeLeft",
amount: -1 * this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-right-increase": () => {
let action = {
name: "WindowResizeRight",
amount: this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
"window-resize-right-decrease": () => {
let action = {
name: "WindowResizeRight",
amount: -1 * this.settings.get_uint("resize-amount"),
};
this.extWm.command(action);
},
};
}
}

View File

@ -1,407 +0,0 @@
/*
* This file is part of the Forge extension for GNOME
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Credits:
* This file has some code from Dash-To-Panel extension: convenience.js
* Some code was also adapted from the upstream Gnome Shell source code.
*/
// Gnome imports
import Meta from "gi://Meta";
import St from "gi://St";
// Gnome-shell imports
import { PACKAGE_VERSION } from "resource:///org/gnome/shell/misc/config.js";
// App imports
import { ORIENTATION_TYPES, LAYOUT_TYPES, POSITION } from "./tree.js";
import { GRAB_TYPES } from "./window.js";
const [major] = PACKAGE_VERSION.split(".").map((s) => Number(s));
/**
*
* Turns an array into an immutable enum-like object
*
*/
export function createEnum(anArray) {
const enumObj = {};
for (const val of anArray) {
enumObj[val] = val;
}
return Object.freeze(enumObj);
}
export function resolveX(rectRequest, metaWindow) {
let metaRect = metaWindow.get_frame_rect();
let monitorRect = metaWindow.get_work_area_current_monitor();
let val = metaRect.x;
let x = rectRequest.x;
switch (typeof x) {
case "string": //center,
switch (x) {
case "center":
val = monitorRect.width * 0.5 - this.resolveWidth(rectRequest, metaWindow) * 0.5;
break;
case "left":
val = 0;
break;
case "right":
val = monitorRect.width - this.resolveWidth(rectRequest, metaWindow);
break;
default:
break;
}
break;
case "number":
val = x;
break;
default:
break;
}
val = monitorRect.x + val;
return val;
}
export function resolveY(rectRequest, metaWindow) {
let metaRect = metaWindow.get_frame_rect();
let monitorRect = metaWindow.get_work_area_current_monitor();
let val = metaRect.y;
let y = rectRequest.y;
switch (typeof y) {
case "string": //center,
switch (y) {
case "center":
val = monitorRect.height * 0.5 - this.resolveHeight(rectRequest, metaWindow) * 0.5;
break;
case "top":
val = 0;
break;
case "bottom": // inverse of y=0
val = monitorRect.height - this.resolveHeight(rectRequest, metaWindow);
break;
default:
break;
}
break;
case "number":
val = y;
break;
default:
break;
}
val = monitorRect.y + val;
return val;
}
export function resolveWidth(rectRequest, metaWindow) {
let metaRect = metaWindow.get_frame_rect();
let monitorRect = metaWindow.get_work_area_current_monitor();
let val = metaRect.width;
let width = rectRequest.width;
switch (typeof width) {
case "number":
if (Number.isInteger(width) && width != 1) {
val = width;
} else {
let monitorWidth = monitorRect.width;
val = monitorWidth * width;
}
break;
default:
break;
}
return val;
}
export function resolveHeight(rectRequest, metaWindow) {
let metaRect = metaWindow.get_frame_rect();
let monitorRect = metaWindow.get_work_area_current_monitor();
let val = metaRect.height;
let height = rectRequest.height;
switch (typeof height) {
case "number":
if (Number.isInteger(height) && height != 1) {
val = height;
} else {
let monitorHeight = monitorRect.height;
val = monitorHeight * height;
}
break;
default:
break;
}
return val;
}
export function orientationFromDirection(direction) {
return direction === Meta.MotionDirection.LEFT || direction === Meta.MotionDirection.RIGHT
? ORIENTATION_TYPES.HORIZONTAL
: ORIENTATION_TYPES.VERTICAL;
}
export function orientationFromLayout(layout) {
switch (layout) {
case LAYOUT_TYPES.HSPLIT:
case LAYOUT_TYPES.TABBED:
return ORIENTATION_TYPES.HORIZONTAL;
case LAYOUT_TYPES.VSPLIT:
case LAYOUT_TYPES.STACKED:
return ORIENTATION_TYPES.VERTICAL;
default:
break;
}
}
export function positionFromDirection(direction) {
return direction === Meta.MotionDirection.LEFT || direction === Meta.MotionDirection.UP
? POSITION.BEFORE
: POSITION.AFTER;
}
export function resolveDirection(directionString) {
if (directionString) {
directionString = directionString.toUpperCase();
if (directionString === "LEFT") {
return Meta.MotionDirection.LEFT;
}
if (directionString === "RIGHT") {
return Meta.MotionDirection.RIGHT;
}
if (directionString === "UP") {
return Meta.MotionDirection.UP;
}
if (directionString === "DOWN") {
return Meta.MotionDirection.DOWN;
}
}
return null;
}
export function directionFrom(position, orientation) {
if (position === POSITION.AFTER) {
if (orientation === ORIENTATION_TYPES.HORIZONTAL) {
return Meta.DisplayDirection.RIGHT;
} else {
return Meta.DisplayDirection.DOWN;
}
} else if (position === POSITION.BEFORE) {
if (orientation === ORIENTATION_TYPES.HORIZONTAL) {
return Meta.DisplayDirection.LEFT;
} else {
return Meta.DisplayDirection.UP;
}
}
}
export function rectContainsPoint(rect, pointP) {
if (!(rect && pointP)) return false;
return (
rect.x <= pointP[0] &&
pointP[0] <= rect.x + rect.width &&
rect.y <= pointP[1] &&
pointP[1] <= rect.y + rect.height
);
}
export function orientationFromGrab(grabOp) {
if (
grabOp === Meta.GrabOp.RESIZING_N ||
grabOp === Meta.GrabOp.RESIZING_S ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S
) {
return ORIENTATION_TYPES.VERTICAL;
} else if (
grabOp === Meta.GrabOp.RESIZING_E ||
grabOp === Meta.GrabOp.RESIZING_W ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W
) {
return ORIENTATION_TYPES.HORIZONTAL;
}
return ORIENTATION_TYPES.NONE;
}
export function positionFromGrabOp(grabOp) {
if (
grabOp === Meta.GrabOp.RESIZING_W ||
grabOp === Meta.GrabOp.RESIZING_N ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N
) {
return POSITION.BEFORE;
} else if (
grabOp === Meta.GrabOp.RESIZING_E ||
grabOp === Meta.GrabOp.RESIZING_S ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S
) {
return POSITION.AFTER;
}
return POSITION.UNKNOWN;
}
export function allowResizeGrabOp(grabOp) {
grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
return (
grabOp === Meta.GrabOp.RESIZING_N ||
grabOp === Meta.GrabOp.RESIZING_E ||
grabOp === Meta.GrabOp.RESIZING_W ||
grabOp === Meta.GrabOp.RESIZING_S ||
grabOp === Meta.GrabOp.RESIZING_NE ||
grabOp === Meta.GrabOp.RESIZING_NW ||
grabOp === Meta.GrabOp.RESIZING_SE ||
grabOp === Meta.GrabOp.RESIZING_SW ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
);
}
export function grabMode(grabOp) {
grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
if (
grabOp === Meta.GrabOp.RESIZING_N ||
grabOp === Meta.GrabOp.RESIZING_E ||
grabOp === Meta.GrabOp.RESIZING_W ||
grabOp === Meta.GrabOp.RESIZING_S ||
grabOp === Meta.GrabOp.RESIZING_NE ||
grabOp === Meta.GrabOp.RESIZING_NW ||
grabOp === Meta.GrabOp.RESIZING_SE ||
grabOp === Meta.GrabOp.RESIZING_SW ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S ||
grabOp === Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN
) {
return GRAB_TYPES.RESIZING;
} else if (
grabOp === Meta.GrabOp.KEYBOARD_MOVING ||
grabOp === Meta.GrabOp.MOVING ||
grabOp === Meta.GrabOp.MOVING_UNCONSTRAINED
) {
return GRAB_TYPES.MOVING;
}
return GRAB_TYPES.UNKNOWN;
}
export function decomposeGrabOp(grabOp) {
grabOp &= ~1024; // ignore META_GRAB_OP_WINDOW_FLAG_UNCONSTRAINED
switch (grabOp) {
case Meta.GrabOp.RESIZING_NE:
return [Meta.GrabOp.RESIZING_N, Meta.GrabOp.RESIZING_E];
case Meta.GrabOp.RESIZING_NW:
return [Meta.GrabOp.RESIZING_N, Meta.GrabOp.RESIZING_W];
case Meta.GrabOp.RESIZING_SE:
return [Meta.GrabOp.RESIZING_S, Meta.GrabOp.RESIZING_E];
case Meta.GrabOp.RESIZING_SW:
return [Meta.GrabOp.RESIZING_S, Meta.GrabOp.RESIZING_W];
default:
return [grabOp];
}
}
export function directionFromGrab(grabOp) {
if (grabOp === Meta.GrabOp.RESIZING_E || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_E) {
return Meta.MotionDirection.RIGHT;
} else if (grabOp === Meta.GrabOp.RESIZING_W || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_W) {
return Meta.MotionDirection.LEFT;
} else if (grabOp === Meta.GrabOp.RESIZING_N || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_N) {
return Meta.MotionDirection.UP;
} else if (grabOp === Meta.GrabOp.RESIZING_S || grabOp === Meta.GrabOp.KEYBOARD_RESIZING_S) {
return Meta.MotionDirection.DOWN;
}
}
export function removeGapOnRect(rectWithGap, gap) {
rectWithGap.x = rectWithGap.x -= gap;
rectWithGap.y = rectWithGap.y -= gap;
rectWithGap.width = rectWithGap.width += gap * 2;
rectWithGap.height = rectWithGap.height += gap * 2;
return rectWithGap;
}
// Credits: PopShell
export function findWindowWith(title) {
let display = global.display;
let type = Meta.TabList.NORMAL_ALL;
let workspaceMgr = display.get_workspace_manager();
let workspaces = workspaceMgr.get_n_workspaces();
for (let wsId = 1; wsId <= workspaces; wsId++) {
let workspace = workspaceMgr.get_workspace_by_index(wsId);
for (const metaWindow of display.get_tab_list(type, workspace)) {
if (
metaWindow.title &&
title &&
(metaWindow.title === title || metaWindow.title.includes(title))
) {
return metaWindow;
}
}
}
return undefined;
}
export function oppositeDirectionOf(direction) {
if (direction === Meta.MotionDirection.LEFT) {
return Meta.MotionDirection.RIGHT;
} else if (direction === Meta.MotionDirection.RIGHT) {
return Meta.MotionDirection.LEFT;
} else if (direction === Meta.MotionDirection.UP) {
return Meta.MotionDirection.DOWN;
} else if (direction === Meta.MotionDirection.DOWN) {
return Meta.MotionDirection.UP;
}
}
export function monitorIndex(monitorValue) {
if (!monitorValue) return -1;
let wsIndex = monitorValue.indexOf("ws");
let indexVal = monitorValue.slice(0, wsIndex);
indexVal = indexVal.replace("mo", "");
return parseInt(indexVal);
}
export function _disableDecorations() {
let decos = global.window_group.get_children().filter((a) => a.type != null);
decos.forEach((d) => {
global.window_group.remove_child(d);
d.destroy();
});
}
export function dpi() {
return St.ThemeContext.get_for_stage(global.stage).scale_factor;
}
export function isGnome(majorVersion) {
return major == majorVersion;
}
export function isGnomeGTE(majorVersion) {
return major >= majorVersion;
}