diff options
Diffstat (limited to 'extensions/45/vertical-workspaces/lib/util.js')
-rw-r--r-- | extensions/45/vertical-workspaces/lib/util.js | 375 |
1 files changed, 0 insertions, 375 deletions
diff --git a/extensions/45/vertical-workspaces/lib/util.js b/extensions/45/vertical-workspaces/lib/util.js deleted file mode 100644 index ab79c48..0000000 --- a/extensions/45/vertical-workspaces/lib/util.js +++ /dev/null @@ -1,375 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * util.js - * - * @author GdH <G-dH@github.com> - * @copyright 2022 - 2024 - * @license GPL-3.0 - * - */ - -'use strict'; - -import GLib from 'gi://GLib'; -import Clutter from 'gi://Clutter'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import Gio from 'gi://Gio'; - -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js'; - -let Me; -let _installedExtensions; - -export function init(me) { - Me = me; -} - -export function cleanGlobals() { - Me = null; - _installedExtensions = null; -} - -export class Overrides extends InjectionManager { - constructor() { - super(); - this._overrides = {}; - } - - addOverride(name, prototype, overrideList) { - const backup = this.overrideProto(prototype, overrideList, name); - // don't update originals when override's just refreshing, keep initial content - let originals = this._overrides[name]?.originals; - if (!originals) - originals = backup; - this._overrides[name] = { - originals, - prototype, - }; - } - - removeOverride(name) { - const override = this._overrides[name]; - if (!override) - return false; - - this.overrideProto(override.prototype, override.originals, name); - delete this._overrides[name]; - return true; - } - - removeAll() { - for (let name in this._overrides) { - this.removeOverride(name); - delete this._overrides[name]; - } - } - - overrideProto(proto, overrides, name) { - const backup = {}; - const originals = this._overrides[name]?.originals; - for (let symbol in overrides) { - if (symbol.startsWith('after_')) { - const actualSymbol = symbol.slice('after_'.length); - let fn; - if (originals && originals[actualSymbol]) - fn = originals[actualSymbol]; - else - fn = proto[actualSymbol]; - const afterFn = overrides[symbol]; - proto[actualSymbol] = function (...args) { - args = Array.prototype.slice.call(args); - const res = fn.apply(this, args); - afterFn.apply(this, args); - return res; - }; - backup[actualSymbol] = fn; - } else if (overrides[symbol] !== null) { - backup[symbol] = proto[symbol]; - this._installMethod(proto, symbol, overrides[symbol]); - } - } - return backup; - } -} - -export function openPreferences(metadata) { - if (!metadata) - metadata = Me.metadata; - const windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); - let tracker = Shell.WindowTracker.get_default(); - let metaWin, isMe = null; - - for (let win of windows) { - const app = tracker.get_window_app(win); - if (win.get_title()?.includes(metadata.name) && app.get_name() === 'Extensions') { - // this is our existing window - metaWin = win; - isMe = true; - break; - } else if (win.wm_class?.includes('org.gnome.Shell.Extensions')) { - // this is prefs window of another extension - metaWin = win; - isMe = false; - } - } - - if (metaWin && !isMe) { - // other prefs window blocks opening another prefs window, so close it - metaWin.delete(global.get_current_time()); - } else if (metaWin && isMe) { - // if prefs window already exist, move it to the current WS and activate it - metaWin.change_workspace(global.workspace_manager.get_active_workspace()); - metaWin.activate(global.get_current_time()); - } - - if (!metaWin || (metaWin && !isMe)) { - // delay to avoid errors if previous prefs window has been colsed - GLib.idle_add(GLib.PRIORITY_LOW, () => { - try { - Main.extensionManager.openExtensionPrefs(metadata.uuid, '', {}); - } catch (e) { - console.error(e); - } - }); - } -} - -export function activateSearchProvider(prefix = '') { - const searchEntry = Main.overview.searchEntry; - if (!searchEntry.get_text() || !searchEntry.get_text().startsWith(prefix)) { - prefix = `${prefix} `; - const position = prefix.length; - searchEntry.set_text(prefix); - searchEntry.get_first_child().set_cursor_position(position); - searchEntry.get_first_child().set_selection(position, position); - } else { - searchEntry.set_text(''); - } -} - -export function dashNotDefault() { - return Main.overview.dash !== Main.overview._overview._controls.layoutManager._dash; -} - -export function dashIsDashToDock() { - return Main.overview.dash._isHorizontal !== undefined; -} - -// Reorder Workspaces - callback for Dash and workspacesDisplay -export function reorderWorkspace(direction = 0) { - let activeWs = global.workspace_manager.get_active_workspace(); - let activeWsIdx = activeWs.index(); - let targetIdx = activeWsIdx + direction; - if (targetIdx > -1 && targetIdx < global.workspace_manager.get_n_workspaces()) - global.workspace_manager.reorder_workspace(activeWs, targetIdx); -} - -export function activateKeyboardForWorkspaceView() { - Main.ctrlAltTabManager._items.forEach(i => { - if (i.sortGroup === 1 && i.name === 'Windows') - Main.ctrlAltTabManager.focusGroup(i); - }); -} - -export function exposeWindows(adjustment, activateKeyboard) { - // expose windows for static overview modes - if (!adjustment.value && !Main.overview._animationInProgress) { - if (adjustment.value === 0) { - adjustment.value = 0; - adjustment.ease(1, { - duration: 200, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - if (activateKeyboard) { - Main.ctrlAltTabManager._items.forEach(i => { - if (i.sortGroup === 1 && i.name === 'Windows') - Main.ctrlAltTabManager.focusGroup(i); - }); - } - }, - }); - } - } -} - -export function isShiftPressed(state = null) { - if (state === null) - [,, state] = global.get_pointer(); - return (state & Clutter.ModifierType.SHIFT_MASK) !== 0; -} - -export function isCtrlPressed(state = null) { - if (state === null) - [,, state] = global.get_pointer(); - return (state & Clutter.ModifierType.CONTROL_MASK) !== 0; -} - -export function isAltPressed(state = null) { - if (state === null) - [,, state] = global.get_pointer(); - return (state & Clutter.ModifierType.MOD1_MASK) !== 0; -} - -export function fuzzyMatch(term, text) { - let pos = -1; - const matches = []; - // convert all accented chars to their basic form and to lower case - const _text = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); - const _term = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); - - // if term matches the substring exactly, gains the highest weight - if (_text.includes(_term)) - return 0; - - for (let i = 0; i < _term.length; i++) { - let c = _term[i]; - let p; - if (pos > 0) - p = _term[i - 1]; - while (true) { - pos += 1; - if (pos >= _text.length) - return -1; - - if (_text[pos] === c) { - matches.push(pos); - break; - } else if (_text[pos] === p) { - matches.pop(); - matches.push(pos); - } - } - } - - // add all position to get a weight of the result - // results closer to the beginning of the text and term characters closer to each other will gain more weight. - return matches.reduce((r, p) => r + p) - matches.length * matches[0] + matches[0]; -} - -export function strictMatch(term, text) { - // remove diacritics and accents from letters - let s = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); - let p = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); - let ps = p.split(/ +/); - - // allows to use multiple exact patterns separated by a space in arbitrary order - for (let w of ps) { // escape regex control chars - if (!s.match(w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))) - return -1; - } - return 0; -} - -export function isMoreRelevant(stringA, stringB, pattern) { - let regex = /[^a-zA-Z\d]/; - let strSplitA = stringA.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex); - let strSplitB = stringB.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex); - let aAny = false; - strSplitA.forEach(w => { - aAny = aAny || w.startsWith(pattern); - }); - let bAny = false; - strSplitB.forEach(w => { - bAny = bAny || w.startsWith(pattern); - }); - - // if both strings contain a word that starts with the pattern - // prefer the one whose first word starts with the pattern - if (aAny && bAny) - return !strSplitA[0].startsWith(pattern) && strSplitB[0].startsWith(pattern); - else - return !aAny && bAny; -} - -export function getEnabledExtensions(pattern = '') { - let result = []; - // extensionManager is unreliable at startup because it is uncertain whether all extensions have been loaded - // also gsettings key can contain already removed extensions (user deleted them without disabling them first) - // therefore we have to check what's really installed in the filesystem - if (!_installedExtensions) { - const extensionFiles = [...collectFromDatadirs('extensions', true)]; - _installedExtensions = extensionFiles.map(({ info }) => { - let fileType = info.get_file_type(); - if (fileType !== Gio.FileType.DIRECTORY) - return null; - const uuid = info.get_name(); - return uuid; - }); - } - // _enabledExtensions contains content of the enabled-extensions key from gsettings, not actual state - const enabled = Main.extensionManager._enabledExtensions; - result = _installedExtensions.filter(ext => enabled.includes(ext)); - // _extensions contains already loaded extensions, so we can try to filter out broken or incompatible extensions - const active = Main.extensionManager._extensions; - result = result.filter(ext => { - const extension = active.get(ext); - if (extension) - return ![3, 4].includes(extension.state); // 3 - ERROR, 4 - OUT_OF_TIME (not supported by shell-version in metadata) - // extension can be enabled but not yet loaded, we just cannot see its state at this moment, so let it pass as enabled - return true; - }); - // return only extensions matching the search pattern - return result.filter(uuid => uuid !== null && uuid.includes(pattern)); -} - -function* collectFromDatadirs(subdir, includeUserDir) { - let dataDirs = GLib.get_system_data_dirs(); - if (includeUserDir) - dataDirs.unshift(GLib.get_user_data_dir()); - - for (let i = 0; i < dataDirs.length; i++) { - let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); - let dir = Gio.File.new_for_path(path); - - let fileEnum; - try { - fileEnum = dir.enumerate_children('standard::name,standard::type', - Gio.FileQueryInfoFlags.NONE, null); - } catch (e) { - fileEnum = null; - } - if (fileEnum !== null) { - let info; - while ((info = fileEnum.next_file(null))) - yield { dir: fileEnum.get_child(info), info }; - } - } -} - -export function getScrollDirection(event) { - // scroll wheel provides two types of direction information: - // 1. Clutter.ScrollDirection.DOWN / Clutter.ScrollDirection.UP - // 2. Clutter.ScrollDirection.SMOOTH + event.get_scroll_delta() - // first SMOOTH event returns 0 delta, - // so we need to always read event.direction - // since mouse without smooth scrolling provides exactly one SMOOTH event on one wheel rotation click - // on the other hand, under X11, one wheel rotation click sometimes doesn't send direction event, only several SMOOTH events - // so we also need to convert the delta to direction - let direction = event.get_scroll_direction(); - - if (direction !== Clutter.ScrollDirection.SMOOTH) - return direction; - - let [, delta] = event.get_scroll_delta(); - - if (!delta) - return null; - - direction = delta > 0 ? Clutter.ScrollDirection.DOWN : Clutter.ScrollDirection.UP; - - return direction; -} - -export function getWindows(workspace) { - // We ignore skip-taskbar windows in switchers, but if they are attached - // to their parent, their position in the MRU list may be more appropriate - // than the parent; so start with the complete list ... - let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, workspace); - // ... map windows to their parent where appropriate ... - return windows.map(w => { - return w.is_attached_dialog() ? w.get_transient_for() : w; - // ... and filter out skip-taskbar windows and duplicates - }).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) === i); -} |