From d6c39ec97a6f776b4d5249e25bf4d13d57eb2c66 Mon Sep 17 00:00:00 2001 From: Leleat Date: Sat, 19 Aug 2023 11:42:07 +0200 Subject: [PATCH 1/5] Port 45: Update metadata.json Since there are big changes upstream drop support for older releases. --- tiling-assistant@leleat-on-github/metadata.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tiling-assistant@leleat-on-github/metadata.json b/tiling-assistant@leleat-on-github/metadata.json index d1a0d21..06c7cfe 100644 --- a/tiling-assistant@leleat-on-github/metadata.json +++ b/tiling-assistant@leleat-on-github/metadata.json @@ -2,8 +2,7 @@ "description": "Expand GNOME's 2 column tiling and add a Windows-snap-assist-inspired popup...", "name": "Tiling Assistant", "shell-version": [ - "43", - "44" + "45" ], "url": "https://github.com/Leleat/Tiling-Assistant", "uuid": "tiling-assistant@leleat-on-github", From c3f61f9136654f380e034ac3c380df47c208e437 Mon Sep 17 00:00:00 2001 From: Leleat Date: Sat, 12 Aug 2023 18:05:05 +0200 Subject: [PATCH 2/5] Port 45: Adapt to ESM --- .eslintrc.json | 3 +- .../extension.js | 432 +++++++------ .../metadata.json | 1 + tiling-assistant@leleat-on-github/prefs.js | 598 +++++++++--------- .../src/common.js | 42 +- .../src/dependencies/gi.js | 8 + .../src/dependencies/prefs.js | 4 + .../src/dependencies/prefs/gi.js | 6 + .../src/dependencies/shell.js | 11 + .../src/dependencies/unexported/altTab.js | 16 + .../dependencies/unexported/windowManager.js | 1 + .../src/extension/activeWindowHint.js | 19 +- .../src/extension/altTab.js | 261 +++++++- .../src/extension/keybindingHandler.js | 29 +- .../src/extension/layoutsManager.js | 55 +- .../src/extension/moveHandler.js | 28 +- .../src/extension/resizeHandler.js | 18 +- .../src/extension/tileEditingMode.js | 56 +- .../src/extension/tilingPopup.js | 21 +- .../src/extension/tilingWindowManager.js | 29 +- .../src/extension/utility.js | 36 +- .../src/prefs/layoutRow.js | 20 +- .../src/prefs/layoutRowEntry.js | 16 +- .../src/prefs/layoutsPrefs.js | 26 +- .../src/prefs/shortcutListener.js | 11 +- 25 files changed, 953 insertions(+), 794 deletions(-) create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/gi.js create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/prefs.js create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/prefs/gi.js create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/shell.js create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/unexported/altTab.js create mode 100644 tiling-assistant@leleat-on-github/src/dependencies/unexported/windowManager.js diff --git a/.eslintrc.json b/.eslintrc.json index 816c42d..0a4d706 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,8 @@ }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": "latest" + "ecmaVersion": "latest", + "sourceType": "module" }, "rules": { "array-bracket-newline": [ diff --git a/tiling-assistant@leleat-on-github/extension.js b/tiling-assistant@leleat-on-github/extension.js index 43e26ec..353848c 100644 --- a/tiling-assistant@leleat-on-github/extension.js +++ b/tiling-assistant@leleat-on-github/extension.js @@ -16,16 +16,16 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -'use strict'; +import { Gio, GLib, Meta } from './src/dependencies/gi.js'; +import { Extension, Main } from './src/dependencies/shell.js'; -const { Gio, GLib, Meta } = imports.gi; -const ByteArray = imports.byteArray; -const Main = imports.ui.main; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Rect, Util } = Me.imports.src.extension.utility; +import MoveHandler from './src/extension/moveHandler.js'; +import ResizeHandler from './src/extension/resizeHandler.js'; +import KeybindingHandler from './src/extension/keybindingHandler.js'; +import LayoutsManager from './src/extension/layoutsManager.js'; +import ActiveWindowHint from './src/extension/activeWindowHint.js'; +import AltTabOverride from './src/extension/altTab.js'; +import { Rect } from './src/extension/utility.js'; /** * 2 entry points: @@ -37,8 +37,8 @@ const { Rect, Util } = Me.imports.src.extension.utility; */ class SettingsOverrider { - constructor(settings) { - this._settings = settings; + constructor(settingsSingleton) { + this._settings = settingsSingleton; this._overrides = new Map(); this._originalSettings = new Map(); this._maybeNullValue = GLib.Variant.new_maybe( @@ -114,7 +114,7 @@ class SettingsOverrider { const schemaId = splits.slice(0, -1).join('.'); const settings = this._originalSettings.get(schemaId) ?? - ExtensionUtils.getSettings(schemaId); + new Gio.Settings({ schema_id: schemaId }); value = value.get_variant(); if (value.equal(this._maybeNullValue)) @@ -145,222 +145,218 @@ class SettingsOverrider { } } -function init() { - ExtensionUtils.initTranslations(Me.metadata.uuid); -} +export default class TilingAssistantExtension extends Extension { + async enable() { + this.settings = (await import('./src/common.js')).Settings; + this.settings.initialize(this.getSettings()); + this._settingsOverrider = new SettingsOverrider(this.settings); -function enable() { - this._settings = Me.imports.src.common.Settings; - this._settings.initialize(); - this._settingsOverrider = new SettingsOverrider(this._settings); - - this._twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; - this._twm.initialize(); - - const MoveHandler = Me.imports.src.extension.moveHandler; - this._moveHandler = new MoveHandler.Handler(); - const ResizeHandler = Me.imports.src.extension.resizeHandler; - this._resizeHandler = new ResizeHandler.Handler(); - const KeybindingHandler = Me.imports.src.extension.keybindingHandler; - this._keybindingHandler = new KeybindingHandler.Handler(); - const LayoutsManager = Me.imports.src.extension.layoutsManager; - this._layoutsManager = new LayoutsManager.LayoutManager(); - const activeWindowHint = Me.imports.src.extension.activeWindowHint; - this._activeWindowHintHandler = new activeWindowHint.Handler(); - - const AltTabOverride = Me.imports.src.extension.altTab.Override; - this._altTabOverride = new AltTabOverride(); - - // Disable native tiling. - this._settingsOverrider.add(ExtensionUtils.getSettings('org.gnome.mutter'), - 'edge-tiling', new GLib.Variant('b', false)); - - // Disable native keybindings for Super+Up/Down/Left/Right - const gnomeMutterKeybindings = ExtensionUtils.getSettings( - 'org.gnome.mutter.keybindings'); - const gnomeDesktopKeybindings = ExtensionUtils.getSettings( - 'org.gnome.desktop.wm.keybindings'); - const sc = Me.imports.src.common.Shortcuts; - const emptyStrvVariant = new GLib.Variant('as', []); - - if (gnomeDesktopKeybindings.get_strv('maximize').includes('Up') && - this._settings.getStrv(sc.MAXIMIZE).includes('Up')) { - this._settingsOverrider.add(gnomeDesktopKeybindings, - 'maximize', emptyStrvVariant); - } - if (gnomeDesktopKeybindings.get_strv('unmaximize').includes('Down') && - this._settings.getStrv(sc.RESTORE_WINDOW).includes('Down')) { - this._settingsOverrider.add(gnomeDesktopKeybindings, - 'unmaximize', emptyStrvVariant); - } - if (gnomeMutterKeybindings.get_strv('toggle-tiled-left').includes('Left') && - this._settings.getStrv(sc.LEFT).includes('Left')) { - this._settingsOverrider.add(gnomeMutterKeybindings, - 'toggle-tiled-left', emptyStrvVariant); - } - if (gnomeMutterKeybindings.get_strv('toggle-tiled-right').includes('Right') && - this._settings.getStrv(sc.RIGHT).includes('Right')) { - this._settingsOverrider.add(gnomeMutterKeybindings, - 'toggle-tiled-right', emptyStrvVariant); + const twmModule = await import('./src/extension/tilingWindowManager.js'); + + this._twm = twmModule.TilingWindowManager; + this._twm.initialize(); + + this._moveHandler = new MoveHandler(); + this._resizeHandler = new ResizeHandler(); + this._keybindingHandler = new KeybindingHandler(); + this._layoutsManager = new LayoutsManager(); + this._activeWindowHintHandler = new ActiveWindowHint(); + this._altTabOverride = new AltTabOverride(); + + // Disable native tiling. + this._settingsOverrider.add(new Gio.Settings({ + schema_id: 'org.gnome.mutter' + }), 'edge-tiling', new GLib.Variant('b', false)); + + // Disable native keybindings for Super+Up/Down/Left/Right + const gnomeMutterKeybindings = new Gio.Settings({ + schema_id: 'org.gnome.mutter.keybindings' + }); + const gnomeDesktopKeybindings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.wm.keybindings' + }); + const sc = (await import('./src/common.js')).Shortcuts; + const emptyStrvVariant = new GLib.Variant('as', []); + + if (gnomeDesktopKeybindings.get_strv('maximize').includes('Up') && + this.settings.getStrv(sc.MAXIMIZE).includes('Up')) { + this._settingsOverrider.add(gnomeDesktopKeybindings, + 'maximize', emptyStrvVariant); + } + if (gnomeDesktopKeybindings.get_strv('unmaximize').includes('Down') && + this.settings.getStrv(sc.RESTORE_WINDOW).includes('Down')) { + this._settingsOverrider.add(gnomeDesktopKeybindings, + 'unmaximize', emptyStrvVariant); + } + if (gnomeMutterKeybindings.get_strv('toggle-tiled-left').includes('Left') && + this.settings.getStrv(sc.LEFT).includes('Left')) { + this._settingsOverrider.add(gnomeMutterKeybindings, + 'toggle-tiled-left', emptyStrvVariant); + } + if (gnomeMutterKeybindings.get_strv('toggle-tiled-right').includes('Right') && + this.settings.getStrv(sc.RIGHT).includes('Right')) { + this._settingsOverrider.add(gnomeMutterKeybindings, + 'toggle-tiled-right', emptyStrvVariant); + } + + // Include tiled windows when dragging from the top panel. + this._getDraggableWindowForPosition = Main.panel._getDraggableWindowForPosition; + Main.panel._getDraggableWindowForPosition = function (stageX) { + const workspaceManager = global.workspace_manager; + const windows = workspaceManager.get_active_workspace().list_windows(); + const allWindowsByStacking = global.display.sort_windows_by_stacking(windows).reverse(); + + return allWindowsByStacking.find(w => { + const rect = w.get_frame_rect(); + const workArea = w.get_work_area_current_monitor(); + return w.is_on_primary_monitor() && + w.showing_on_its_workspace() && + w.get_window_type() !== Meta.WindowType.DESKTOP && + (w.maximized_vertically || w.tiledRect?.y === workArea.y) && + stageX > rect.x && stageX < rect.x + rect.width; + }); + }; + + // Restore tiled window properties after session was unlocked. + this._loadAfterSessionLock(); + + // Setting used for detection of a fresh install and do compatibility + // changes if necessary... + this.settings.setInt('last-version-installed', this.metadata.version); } - // Include tiled windows when dragging from the top panel. - this._getDraggableWindowForPosition = Main.panel._getDraggableWindowForPosition; - Main.panel._getDraggableWindowForPosition = function (stageX) { - const workspaceManager = global.workspace_manager; - const windows = workspaceManager.get_active_workspace().list_windows(); - const allWindowsByStacking = global.display.sort_windows_by_stacking(windows).reverse(); - - return allWindowsByStacking.find(w => { - const rect = w.get_frame_rect(); - const workArea = w.get_work_area_current_monitor(); - return w.is_on_primary_monitor() && - w.showing_on_its_workspace() && - w.get_window_type() !== Meta.WindowType.DESKTOP && - (w.maximized_vertically || w.tiledRect?.y === workArea.y) && - stageX > rect.x && stageX < rect.x + rect.width; + disable() { + // Save tiled window properties, if the session was locked to restore + // them after the session is unlocked again. + this._saveBeforeSessionLock(); + + this._settingsOverrider.destroy(); + this._settingsOverrider = null; + this._moveHandler.destroy(); + this._moveHandler = null; + this._resizeHandler.destroy(); + this._resizeHandler = null; + this._keybindingHandler.destroy(); + this._keybindingHandler = null; + this._layoutsManager.destroy(); + this._layoutsManager = null; + this._activeWindowHintHandler.destroy(); + this._activeWindowHintHandler = null; + + this._altTabOverride.destroy(); + this._altTabOverride = null; + + this._twm.destroy(); + this._twm = null; + + this.settings.destroy(); + this.settings = null; + + // Restore old functions. + Main.panel._getDraggableWindowForPosition = this._getDraggableWindowForPosition; + this._getDraggableWindowForPosition = null; + + // Delete custom tiling properties. + const openWindows = global.display.get_tab_list(Meta.TabList.NORMAL, null); + openWindows.forEach(w => { + delete w.isTiled; + delete w.tiledRect; + delete w.untiledRect; }); - }; + } - // Restore tiled window properties after session was unlocked. - _loadAfterSessionLock(); + /** + * Extensions are disabled when the screen is locked. So save the custom tiling + * properties of windows before locking the screen. + */ + _saveBeforeSessionLock() { + if (!Main.sessionMode.isLocked) + return; - // Setting used for detection of a fresh install and do compatibility - // changes if necessary... - this._settings.setInt('last-version-installed', Me.metadata.version); -} + this._wasLocked = true; -function disable() { - // Save tiled window properties, if the session was locked to restore - // them after the session is unlocked again. - _saveBeforeSessionLock(); - - this._settingsOverrider.destroy(); - this._settingsOverrider = null; - this._moveHandler.destroy(); - this._moveHandler = null; - this._resizeHandler.destroy(); - this._resizeHandler = null; - this._keybindingHandler.destroy(); - this._keybindingHandler = null; - this._layoutsManager.destroy(); - this._layoutsManager = null; - this._activeWindowHintHandler.destroy(); - this._activeWindowHintHandler = null; - - this._altTabOverride.destroy(); - this._altTabOverride = null; - - this._twm.destroy(); - this._twm = null; - - this._settings.destroy(); - this._settings = null; - - // Restore old functions. - Main.panel._getDraggableWindowForPosition = this._getDraggableWindowForPosition; - this._getDraggableWindowForPosition = null; - - // Delete custom tiling properties. - const openWindows = global.display.get_tab_list(Meta.TabList.NORMAL, null); - openWindows.forEach(w => { - delete w.isTiled; - delete w.tiledRect; - delete w.untiledRect; - }); -} + const rectToJsObj = rect => rect && { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }; -/** - * Extensions are disabled when the screen is locked. So save the custom tiling - * properties of windows before locking the screen. - */ -function _saveBeforeSessionLock() { - if (!Main.sessionMode.isLocked) - return; - - this._wasLocked = true; - - const rectToJsObj = rect => rect && { - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height - }; - - // can't just check for isTiled because maximized windows may - // have an untiledRect as well in case window gaps are used - const openWindows = this._twm.getWindows(true); - const savedWindows = openWindows.filter(w => w.untiledRect).map(w => { - return { - windowId: w.get_stable_sequence(), - isTiled: w.isTiled, - tiledRect: rectToJsObj(w.tiledRect), - untiledRect: rectToJsObj(w.untiledRect) + // can't just check for isTiled because maximized windows may + // have an untiledRect as well in case window gaps are used + const openWindows = this._twm.getWindows(true); + const savedWindows = openWindows.filter(w => w.untiledRect).map(w => { + return { + windowId: w.get_stable_sequence(), + isTiled: w.isTiled, + tiledRect: rectToJsObj(w.tiledRect), + untiledRect: rectToJsObj(w.untiledRect) + }; + }); + + const saveObj = { + 'windows': savedWindows, + 'tileGroups': Array.from(this._twm.getTileGroups()) }; - }); - - const saveObj = { - 'windows': savedWindows, - 'tileGroups': Array.from(this._twm.getTileGroups()) - }; - - const userPath = GLib.get_user_config_dir(); - const parentPath = GLib.build_filenamev([userPath, '/tiling-assistant']); - const parent = Gio.File.new_for_path(parentPath); - try { parent.make_directory_with_parents(null); } catch (e) {} - const path = GLib.build_filenamev([parentPath, '/tiledSessionRestore.json']); - const file = Gio.File.new_for_path(path); - try { file.create(Gio.FileCreateFlags.NONE, null); } catch (e) {} - file.replace_contents(JSON.stringify(saveObj), null, false, - Gio.FileCreateFlags.REPLACE_DESTINATION, null); -} -/** - * Extensions are disabled when the screen is locked. After having saved them, - * reload them here. - */ -function _loadAfterSessionLock() { - if (!this._wasLocked) - return; - - this._wasLocked = false; - - const userPath = GLib.get_user_config_dir(); - const path = GLib.build_filenamev([userPath, '/tiling-assistant/tiledSessionRestore.json']); - const file = Gio.File.new_for_path(path); - if (!file.query_exists(null)) - return; - - try { file.create(Gio.FileCreateFlags.NONE, null); } catch (e) {} - const [success, contents] = file.load_contents(null); - if (!success || !contents.length) - return; - - const openWindows = this._twm.getWindows(true); - const saveObj = JSON.parse(ByteArray.toString(contents)); - - const windowObjects = saveObj['windows']; - windowObjects.forEach(wObj => { - const { windowId, isTiled, tiledRect, untiledRect } = wObj; - const window = openWindows.find(w => w.get_stable_sequence() === windowId); - if (!window) + const userPath = GLib.get_user_config_dir(); + const parentPath = GLib.build_filenamev([userPath, '/tiling-assistant']); + const parent = Gio.File.new_for_path(parentPath); + try { parent.make_directory_with_parents(null); } catch (e) {} + const path = GLib.build_filenamev([parentPath, '/tiledSessionRestore.json']); + const file = Gio.File.new_for_path(path); + try { file.create(Gio.FileCreateFlags.NONE, null); } catch (e) {} + file.replace_contents(JSON.stringify(saveObj), null, false, + Gio.FileCreateFlags.REPLACE_DESTINATION, null); + } + + /** + * Extensions are disabled when the screen is locked. After having saved them, + * reload them here. + */ + _loadAfterSessionLock() { + if (!this._wasLocked) return; - const jsToRect = jsRect => jsRect && new Rect( - jsRect.x, jsRect.y, jsRect.width, jsRect.height - ); - - window.isTiled = isTiled; - window.tiledRect = jsToRect(tiledRect); - window.untiledRect = jsToRect(untiledRect); - }); - - const tileGroups = new Map(saveObj['tileGroups']); - this._twm.setTileGroups(tileGroups); - openWindows.forEach(w => { - if (tileGroups.has(w.get_id())) { - const group = this._twm.getTileGroupFor(w); - this._twm.updateTileGroup(group); - } - }); + this._wasLocked = false; + + const userPath = GLib.get_user_config_dir(); + const path = GLib.build_filenamev([userPath, '/tiling-assistant/tiledSessionRestore.json']); + const file = Gio.File.new_for_path(path); + if (!file.query_exists(null)) + return; + + try { file.create(Gio.FileCreateFlags.NONE, null); } catch (e) {} + const [success, contents] = file.load_contents(null); + if (!success || !contents.length) + return; + + const openWindows = this._twm.getWindows(true); + const saveObj = JSON.parse(new TextDecoder().decode(contents)); + + const windowObjects = saveObj['windows']; + windowObjects.forEach(wObj => { + const { windowId, isTiled, tiledRect, untiledRect } = wObj; + const window = openWindows.find(w => w.get_stable_sequence() === windowId); + if (!window) + return; + + const jsToRect = jsRect => jsRect && new Rect( + jsRect.x, jsRect.y, jsRect.width, jsRect.height + ); + + window.isTiled = isTiled; + window.tiledRect = jsToRect(tiledRect); + window.untiledRect = jsToRect(untiledRect); + }); + + const tileGroups = new Map(saveObj['tileGroups']); + this._twm.setTileGroups(tileGroups); + openWindows.forEach(w => { + if (tileGroups.has(w.get_id())) { + const group = this._twm.getTileGroupFor(w); + this._twm.updateTileGroup(group); + } + }); + } } diff --git a/tiling-assistant@leleat-on-github/metadata.json b/tiling-assistant@leleat-on-github/metadata.json index 06c7cfe..42163d1 100644 --- a/tiling-assistant@leleat-on-github/metadata.json +++ b/tiling-assistant@leleat-on-github/metadata.json @@ -6,6 +6,7 @@ ], "url": "https://github.com/Leleat/Tiling-Assistant", "uuid": "tiling-assistant@leleat-on-github", + "gettext-domain": "tiling-assistant@leleat-on-github", "settings-schema": "org.gnome.shell.extensions.tiling-assistant", "version": 43 } diff --git a/tiling-assistant@leleat-on-github/prefs.js b/tiling-assistant@leleat-on-github/prefs.js index 156925c..8f92c61 100644 --- a/tiling-assistant@leleat-on-github/prefs.js +++ b/tiling-assistant@leleat-on-github/prefs.js @@ -1,304 +1,304 @@ -'use strict'; - -const { Gdk, Gio, GLib, Gtk } = imports.gi; -const ByteArray = imports.byteArray; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const LayoutPrefs = Me.imports.src.prefs.layoutsPrefs.Prefs; -const { ShortcutListener } = Me.imports.src.prefs.shortcutListener; -const { Settings, Shortcuts } = Me.imports.src.common; - -function init() { - ExtensionUtils.initTranslations(Me.metadata.uuid); -} - -function fillPreferencesWindow(window) { - // Load css file - const provider = new Gtk.CssProvider(); - const path = GLib.build_filenamev([Me.path, 'stylesheet.css']); - provider.load_from_path(path); - Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), - provider, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ); - - window.set_can_navigate_back(true); - - const settings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); - const builder = new Gtk.Builder(); - builder.set_translation_domain(Me.metadata.uuid); - builder.add_from_file(`${Me.path}/src/ui/prefs.ui`); - - // Add general preference page - window.add(builder.get_object('general')); - - // Add keybindings preference page - window.add(builder.get_object('keybindings')); - - // Add layouts preference page on condition of advanced setting - const layoutsPage = builder.get_object('layouts'); - settings.connect(`changed::${Settings.ENABLE_ADV_EXP_SETTINGS}`, () => { - settings.get_boolean(Settings.ENABLE_ADV_EXP_SETTINGS) - ? window.add(layoutsPage) - : window.remove(layoutsPage); - }); - - if (settings.get_boolean(Settings.ENABLE_ADV_EXP_SETTINGS)) - window.add(layoutsPage); - - // Bind settings to GUI - _bindSwitches(settings, builder); - _bindSpinbuttons(settings, builder); - _bindComboRows(settings, builder); - _bindRadioButtons(settings, builder); - _bindKeybindings(settings, builder); - _bindColorButtons(settings, builder); - - // LayoutPrefs manages everything related to layouts on the - // prefs side (including the keyboard shortcuts) - new LayoutPrefs(settings, builder); - - // Set visibility for deprecated settings - _setDeprecatedSettings(settings, builder); - - // Add a button into the headerbar with info - _addHeaderBarInfoButton(window, settings, builder); -} - -/* - * Bind GUI switches to settings. - */ -function _bindSwitches(settings, builder) { - const switches = [ - Settings.ENABLE_TILING_POPUP, - Settings.POPUP_ALL_WORKSPACES, - Settings.RAISE_TILE_GROUPS, - Settings.TILEGROUPS_IN_APP_SWITCHER, - Settings.MAXIMIZE_WITH_GAPS, - Settings.SHOW_LAYOUT_INDICATOR, - Settings.ENABLE_ADV_EXP_SETTINGS, - Settings.DISABLE_TILE_GROUPS, - Settings.LOW_PERFORMANCE_MOVE_MODE, - Settings.MONITOR_SWITCH_GRACE_PERIOD, - Settings.ADAPT_EDGE_TILING_TO_FAVORITE_LAYOUT, - Settings.ENABLE_TILE_ANIMATIONS, - Settings.ENABLE_UNTILE_ANIMATIONS, - Settings.ENABLE_HOLD_INVERSE_LANDSCAPE, - Settings.ENABLE_HOLD_INVERSE_PORTRAIT - ]; - - switches.forEach(key => { - const widget = builder.get_object(key.replaceAll('-', '_')); - settings.bind(key, widget, 'active', Gio.SettingsBindFlags.DEFAULT); - }); -} - -/* - * Bind GUI spinbuttons to settings. - */ -function _bindSpinbuttons(settings, builder) { - const spinButtons = [ - Settings.WINDOW_GAP, - Settings.SINGLE_SCREEN_GAP, - Settings.SCREEN_TOP_GAP, - Settings.SCREEN_LEFT_GAP, - Settings.SCREEN_RIGHT_GAP, - Settings.SCREEN_BOTTOM_GAP, - Settings.ACTIVE_WINDOW_HINT_BORDER_SIZE, - Settings.ACTIVE_WINDOW_HINT_INNER_BORDER_SIZE, - Settings.INVERSE_TOP_MAXIMIZE_TIMER, - Settings.VERTICAL_PREVIEW_AREA, - Settings.HORIZONTAL_PREVIEW_AREA - ]; - - spinButtons.forEach(key => { - const widget = builder.get_object(key.replaceAll('-', '_')); - settings.bind(key, widget, 'value', Gio.SettingsBindFlags.DEFAULT); - }); -} - -/* - * Bind GUI AdwComboRows to settings. - */ -function _bindComboRows(settings, builder) { - const comboRows = [ - Settings.ADAPTIVE_TILING_MOD, - Settings.FAVORITE_LAYOUT_MOD, - Settings.IGNORE_TA_MOD, - Settings.RESTORE_SIZE_ON - ]; - - comboRows.forEach(key => { - const widget = builder.get_object(key.replaceAll('-', '_')); - settings.bind(key, widget, 'selected', Gio.SettingsBindFlags.DEFAULT); - widget.set_selected(settings.get_int(key)); - }); -} - -/* - * Bind GUI color buttons to settings. - */ -function _bindColorButtons(settings, builder) { - const switches = [ - Settings.ACTIVE_WINDOW_HINT_COLOR - ]; - - switches.forEach(key => { - const widget = builder.get_object(`${key.replaceAll('-', '_')}_button`); - widget.connect('color-set', () => { - settings.set_string(key, widget.get_rgba().to_string()); +import { Gdk, Gio, GLib, Gtk } from './src/dependencies/prefs/gi.js'; +import { ExtensionPreferences } from './src/dependencies/prefs.js'; + +import LayoutPrefs from './src/prefs/layoutsPrefs.js'; +import { Settings, Shortcuts } from './src/common.js'; +// eslint-disable-next-line no-unused-vars +import { ShortcutListener } from './src/prefs/shortcutListener.js'; + +export default class Prefs extends ExtensionPreferences { + fillPreferencesWindow(window) { + // Load css file + const provider = new Gtk.CssProvider(); + const path = GLib.build_filenamev([this.path, 'stylesheet.css']); + provider.load_from_path(path); + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + + window.set_can_navigate_back(true); + + const settings = this.getSettings(); + const builder = new Gtk.Builder(); + builder.set_translation_domain(this.uuid); + builder.add_from_file(`${this.path}/src/ui/prefs.ui`); + + // Add general preference page + window.add(builder.get_object('general')); + + // Add keybindings preference page + window.add(builder.get_object('keybindings')); + + // Add layouts preference page on condition of advanced setting + const layoutsPage = builder.get_object('layouts'); + settings.connect(`changed::${Settings.ENABLE_ADV_EXP_SETTINGS}`, () => { + settings.get_boolean(Settings.ENABLE_ADV_EXP_SETTINGS) + ? window.add(layoutsPage) + : window.remove(layoutsPage); }); - // initilaize color - const rgba = new Gdk.RGBA(); - rgba.parse(settings.get_string(key)); - widget.set_rgba(rgba); - }); -} - -/* - * Bind radioButtons to settings. - */ -function _bindRadioButtons(settings, builder) { - // These 'radioButtons' are basically just used as a 'fake ComboBox' with - // explanations for the different options. So there is just *one* gsetting - // (an int) which saves the current 'selection'. - const radioButtons = [ - { - key: Settings.DYNAMIC_KEYBINDINGS, - rowNames: [ - 'dynamic_keybinding_disabled_row', - 'dynamic_keybinding_window_focus_row', - 'dynamic_keybinding_tiling_state_row', - 'dynamic_keybinding_tiling_state_windows_row', - 'dynamic_keybinding_favorite_layout_row' - ] - }, - { - key: Settings.ACTIVE_WINDOW_HINT, - rowNames: [ - 'active_window_hint_disabled_row', - 'active_window_hint_minimal_row', - 'active_window_hint_always_row' - ] - }, - { - key: Settings.DEFAULT_MOVE_MODE, - rowNames: [ - 'edge_tiling_row', - 'adaptive_tiling_row', - 'favorite_layout_row', - 'ignore_ta_row' - ] - } - ]; - - radioButtons.forEach(({ key, rowNames }) => { - const currActive = settings.get_int(key); - - rowNames.forEach((name, idx) => { - const row = builder.get_object(name.replaceAll('-', '_')); - const checkButton = row.activatable_widget; - checkButton.connect('toggled', () => settings.set_int(key, idx)); - - // Set initial state - if (idx === currActive) - checkButton.activate(); + if (settings.get_boolean(Settings.ENABLE_ADV_EXP_SETTINGS)) + window.add(layoutsPage); + + // Bind settings to GUI + this._bindSwitches(settings, builder); + this._bindSpinbuttons(settings, builder); + this._bindComboRows(settings, builder); + this._bindRadioButtons(settings, builder); + this._bindKeybindings(settings, builder); + this._bindColorButtons(settings, builder); + + // LayoutPrefs manages everything related to layouts on the + // prefs side (including the keyboard shortcuts) + new LayoutPrefs(settings, builder, this.path); + + // Set visibility for deprecated settings + this._setDeprecatedSettings(settings, builder); + + // Add a button into the headerbar with info + this._addHeaderBarInfoButton(window, settings, builder); + } + + /* + * Bind GUI switches to settings. + */ + _bindSwitches(settings, builder) { + const switches = [ + Settings.ENABLE_TILING_POPUP, + Settings.POPUP_ALL_WORKSPACES, + Settings.RAISE_TILE_GROUPS, + Settings.TILEGROUPS_IN_APP_SWITCHER, + Settings.MAXIMIZE_WITH_GAPS, + Settings.SHOW_LAYOUT_INDICATOR, + Settings.ENABLE_ADV_EXP_SETTINGS, + Settings.DISABLE_TILE_GROUPS, + Settings.LOW_PERFORMANCE_MOVE_MODE, + Settings.MONITOR_SWITCH_GRACE_PERIOD, + Settings.ADAPT_EDGE_TILING_TO_FAVORITE_LAYOUT, + Settings.ENABLE_TILE_ANIMATIONS, + Settings.ENABLE_UNTILE_ANIMATIONS, + Settings.ENABLE_HOLD_INVERSE_LANDSCAPE, + Settings.ENABLE_HOLD_INVERSE_PORTRAIT + ]; + + switches.forEach(key => { + const widget = builder.get_object(key.replaceAll('-', '_')); + settings.bind(key, widget, 'active', Gio.SettingsBindFlags.DEFAULT); + }); + } + + /* + * Bind GUI spinbuttons to settings. + */ + _bindSpinbuttons(settings, builder) { + const spinButtons = [ + Settings.WINDOW_GAP, + Settings.SINGLE_SCREEN_GAP, + Settings.SCREEN_TOP_GAP, + Settings.SCREEN_LEFT_GAP, + Settings.SCREEN_RIGHT_GAP, + Settings.SCREEN_BOTTOM_GAP, + Settings.ACTIVE_WINDOW_HINT_BORDER_SIZE, + Settings.ACTIVE_WINDOW_HINT_INNER_BORDER_SIZE, + Settings.INVERSE_TOP_MAXIMIZE_TIMER, + Settings.VERTICAL_PREVIEW_AREA, + Settings.HORIZONTAL_PREVIEW_AREA + ]; + + spinButtons.forEach(key => { + const widget = builder.get_object(key.replaceAll('-', '_')); + settings.bind(key, widget, 'value', Gio.SettingsBindFlags.DEFAULT); + }); + } + + /* + * Bind GUI AdwComboRows to settings. + */ + _bindComboRows(settings, builder) { + const comboRows = [ + Settings.ADAPTIVE_TILING_MOD, + Settings.FAVORITE_LAYOUT_MOD, + Settings.IGNORE_TA_MOD, + Settings.RESTORE_SIZE_ON + ]; + + comboRows.forEach(key => { + const widget = builder.get_object(key.replaceAll('-', '_')); + settings.bind(key, widget, 'selected', Gio.SettingsBindFlags.DEFAULT); + widget.set_selected(settings.get_int(key)); + }); + } + + /* + * Bind GUI color buttons to settings. + */ + _bindColorButtons(settings, builder) { + const switches = [ + Settings.ACTIVE_WINDOW_HINT_COLOR + ]; + + switches.forEach(key => { + const widget = builder.get_object(`${key.replaceAll('-', '_')}_button`); + widget.connect('color-set', () => { + settings.set_string(key, widget.get_rgba().to_string()); + }); + + // initilaize color + const rgba = new Gdk.RGBA(); + rgba.parse(settings.get_string(key)); + widget.set_rgba(rgba); + }); + } + + /* + * Bind radioButtons to settings. + */ + _bindRadioButtons(settings, builder) { + // These 'radioButtons' are basically just used as a 'fake ComboBox' with + // explanations for the different options. So there is just *one* gsetting + // (an int) which saves the current 'selection'. + const radioButtons = [ + { + key: Settings.DYNAMIC_KEYBINDINGS, + rowNames: [ + 'dynamic_keybinding_disabled_row', + 'dynamic_keybinding_window_focus_row', + 'dynamic_keybinding_tiling_state_row', + 'dynamic_keybinding_tiling_state_windows_row', + 'dynamic_keybinding_favorite_layout_row' + ] + }, + { + key: Settings.ACTIVE_WINDOW_HINT, + rowNames: [ + 'active_window_hint_disabled_row', + 'active_window_hint_minimal_row', + 'active_window_hint_always_row' + ] + }, + { + key: Settings.DEFAULT_MOVE_MODE, + rowNames: [ + 'edge_tiling_row', + 'adaptive_tiling_row', + 'favorite_layout_row', + 'ignore_ta_row' + ] + } + ]; + + radioButtons.forEach(({ key, rowNames }) => { + const currActive = settings.get_int(key); + + rowNames.forEach((name, idx) => { + const row = builder.get_object(name.replaceAll('-', '_')); + const checkButton = row.activatable_widget; + checkButton.connect('toggled', () => settings.set_int(key, idx)); + + // Set initial state + if (idx === currActive) + checkButton.activate(); + }); + }); + } + + /* + * Bind keybinding widgets to settings. + */ + _bindKeybindings(settings, builder) { + const shortcuts = Shortcuts.getAllKeys(); + shortcuts.forEach(key => { + const shortcut = builder.get_object(key.replaceAll('-', '_')); + shortcut.initialize(key, settings); + }); + } + + /** + * Sets the visibility of deprecated settings. Those setting aren't visible + * in the GUI unless they have a user set value. That means they aren't + * discoverable through the GUI and need to first be set with the gsetting. + * The normal rows should have the id of: GSETTING_WITH_UNDERSCORES_row. + * ShortcutListeners have the format of GSETTING_WITH_UNDERSCORES. + */ + _setDeprecatedSettings(settings, builder) { + // Keybindings + ['toggle-tiling-popup', 'auto-tile'].forEach(s => { + const isNonDefault = settings.get_strv(s)[0] !== settings.get_default_value(s).get_strv()[0]; + builder.get_object(s.replaceAll('-', '_')).set_visible(isNonDefault); }); - }); -} - -/* - * Bind keybinding widgets to settings. - */ -function _bindKeybindings(settings, builder) { - const shortcuts = Shortcuts.getAllKeys(); - shortcuts.forEach(key => { - const shortcut = builder.get_object(key.replaceAll('-', '_')); - shortcut.initialize(key, settings); - }); -} - -/** - * Sets the visibility of deprecated settings. Those setting aren't visible - * in the GUI unless they have a user set value. That means they aren't - * discoverable through the GUI and need to first be set with the gsetting. - * The normal rows should have the id of: GSETTING_WITH_UNDERSCORES_row. - * ShortcutListeners have the format of GSETTING_WITH_UNDERSCORES. - */ -function _setDeprecatedSettings(settings, builder) { - // Keybindings - ['toggle-tiling-popup', 'auto-tile'].forEach(s => { - const isNonDefault = settings.get_strv(s)[0] !== settings.get_default_value(s).get_strv()[0]; - builder.get_object(s.replaceAll('-', '_')).set_visible(isNonDefault); - }); - - // Switches - ['tilegroups-in-app-switcher'].forEach(s => { - const isNonDefault = settings.get_boolean(s) !== settings.get_default_value(s).get_boolean(); - builder.get_object(`${s.replaceAll('-', '_')}_row`).set_visible(isNonDefault); - }); -} - -function _addHeaderBarInfoButton(window, settings, builder) { - // Add headerBar button for menu - // TODO: is this a 'reliable' method to access the headerbar? - const page = builder.get_object('general'); - const pages_stack = page.get_parent(); // AdwViewStack - const content_stack = pages_stack.get_parent().get_parent(); // GtkStack - const preferences = content_stack.get_parent(); // GtkBox - const headerbar = preferences.get_first_child(); // AdwHeaderBar - headerbar.pack_start(builder.get_object('info_menu')); - - // Setup menu actions - const actionGroup = new Gio.SimpleActionGroup(); - window.insert_action_group('prefs', actionGroup); - - const bugReportAction = new Gio.SimpleAction({ name: 'open-bug-report' }); - bugReportAction.connect('activate', this._openBugReport.bind(this, window)); - actionGroup.add_action(bugReportAction); - - const userGuideAction = new Gio.SimpleAction({ name: 'open-user-guide' }); - userGuideAction.connect('activate', this._openUserGuide.bind(this, window)); - actionGroup.add_action(userGuideAction); - - const changelogAction = new Gio.SimpleAction({ name: 'open-changelog' }); - changelogAction.connect('activate', this._openChangelog.bind(this, window)); - actionGroup.add_action(changelogAction); - - const licenseAction = new Gio.SimpleAction({ name: 'open-license' }); - licenseAction.connect('activate', this._openLicense.bind(this, window)); - actionGroup.add_action(licenseAction); - - const hiddenSettingsAction = new Gio.SimpleAction({ name: 'open-hidden-settings' }); - hiddenSettingsAction.connect('activate', this._openHiddenSettings.bind(this, window, builder)); - actionGroup.add_action(hiddenSettingsAction); - - // Button to return to main settings page - const returnButton = builder.get_object('hidden_settings_return_button'); - returnButton.connect('clicked', () => window.close_subpage()); -} - -function _openBugReport(window) { - Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/issues', Gdk.CURRENT_TIME); -} - -function _openUserGuide(window) { - Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/wiki', Gdk.CURRENT_TIME); -} - -function _openChangelog(window) { - Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/CHANGELOG.md', Gdk.CURRENT_TIME); -} - -function _openLicense(window) { - Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/LICENSE', Gdk.CURRENT_TIME); -} -function _openHiddenSettings(window, builder) { - const hiddenSettingsPage = builder.get_object('hidden_settings'); - window.present_subpage(hiddenSettingsPage); + // Switches + ['tilegroups-in-app-switcher'].forEach(s => { + const isNonDefault = settings.get_boolean(s) !== settings.get_default_value(s).get_boolean(); + builder.get_object(`${s.replaceAll('-', '_')}_row`).set_visible(isNonDefault); + }); + } + + _addHeaderBarInfoButton(window, settings, builder) { + // Add headerBar button for menu + // TODO: is this a 'reliable' method to access the headerbar? + const page = builder.get_object('general'); + const gtkStack = page + .get_parent() + .get_parent() + .get_parent(); + const adwHeaderBar = gtkStack + .get_next_sibling() + .get_first_child() + .get_first_child() + .get_first_child(); + + adwHeaderBar.pack_start(builder.get_object('info_menu')); + + // Setup menu actions + const actionGroup = new Gio.SimpleActionGroup(); + window.insert_action_group('prefs', actionGroup); + + const bugReportAction = new Gio.SimpleAction({ name: 'open-bug-report' }); + bugReportAction.connect('activate', this._openBugReport.bind(this, window)); + actionGroup.add_action(bugReportAction); + + const userGuideAction = new Gio.SimpleAction({ name: 'open-user-guide' }); + userGuideAction.connect('activate', this._openUserGuide.bind(this, window)); + actionGroup.add_action(userGuideAction); + + const changelogAction = new Gio.SimpleAction({ name: 'open-changelog' }); + changelogAction.connect('activate', this._openChangelog.bind(this, window)); + actionGroup.add_action(changelogAction); + + const licenseAction = new Gio.SimpleAction({ name: 'open-license' }); + licenseAction.connect('activate', this._openLicense.bind(this, window)); + actionGroup.add_action(licenseAction); + + const hiddenSettingsAction = new Gio.SimpleAction({ name: 'open-hidden-settings' }); + hiddenSettingsAction.connect('activate', this._openHiddenSettings.bind(this, window, builder)); + actionGroup.add_action(hiddenSettingsAction); + + // Button to return to main settings page + const returnButton = builder.get_object('hidden_settings_return_button'); + returnButton.connect('clicked', () => window.close_subpage()); + } + + _openBugReport(window) { + Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/issues', Gdk.CURRENT_TIME); + } + + _openUserGuide(window) { + Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/wiki', Gdk.CURRENT_TIME); + } + + _openChangelog(window) { + Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/CHANGELOG.md', Gdk.CURRENT_TIME); + } + + _openLicense(window) { + Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/LICENSE', Gdk.CURRENT_TIME); + } + + _openHiddenSettings(window, builder) { + const hiddenSettingsPage = builder.get_object('hidden_settings'); + window.present_subpage(hiddenSettingsPage); + } } diff --git a/tiling-assistant@leleat-on-github/src/common.js b/tiling-assistant@leleat-on-github/src/common.js index 67d1ca2..72bef63 100644 --- a/tiling-assistant@leleat-on-github/src/common.js +++ b/tiling-assistant@leleat-on-github/src/common.js @@ -1,5 +1,3 @@ -'use strict'; - /** * Helper classes / enums for the settings.xml used in the extension files * *and* prefs files @@ -8,7 +6,7 @@ /** * A Singleton providing access to the settings. */ -var Settings = class Settings { +export class Settings { static _settings; static ENABLE_TILING_POPUP = 'enable-tiling-popup'; static POPUP_ALL_WORKSPACES = 'tiling-popup-all-workspace'; @@ -46,14 +44,12 @@ var Settings = class Settings { static ENABLE_HOLD_INVERSE_PORTRAIT = 'enable-hold-maximize-inverse-portrait'; static RESTORE_SIZE_ON = 'restore-window-size-on'; - static initialize() { - const ExtensionUtils = imports.misc.extensionUtils; - const Me = ExtensionUtils.getCurrentExtension(); - this._settings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); + static initialize(gioSettings) { + this._settings = gioSettings; } static destroy() { - this._settings.run_dispose(); + this._settings = null; } /** @@ -183,13 +179,13 @@ var Settings = class Settings { static reset(key) { this._settings.reset(key); } -}; +} /** * A Singleton providing access to the shortcut keys except the * ones related to the Layouts. */ -var Shortcuts = class Shortcuts { +export class Shortcuts { static TOGGLE_POPUP = 'toggle-tiling-popup'; static EDIT_MODE = 'tile-edit-mode'; static AUTO_FILL = 'auto-tile'; @@ -253,37 +249,37 @@ var Shortcuts = class Shortcuts { this.DEBUGGING_FREE_RECTS ]; } -}; +} // Enums: -var RestoreOn = class RestoreWindowSizeBehavior { +export class RestoreOn { static ON_GRAB_START = 0; // Grab Start static ON_GRAB_END = 1; // 'Grab End' -}; +} -var DynamicKeybindings = class DynamicKeybindingBehavior { +export class DynamicKeybindings { // Order comes from prefs static DISABLED = 0; static FOCUS = 1; static TILING_STATE = 2; static TILING_STATE_WINDOWS = 3; static FAVORITE_LAYOUT = 4; -}; +} -var MoveModes = class MoveModes { +export class MoveModes { // Order comes from prefs static EDGE_TILING = 0; static ADAPTIVE_TILING = 1; static FAVORITE_LAYOUT = 2; static IGNORE_TA = 3; -}; +} -var Orientation = class Orientation { +export class Orientation { static H = 1; static V = 2; -}; +} -var Direction = class Direction { +export class Direction { static N = 1; static E = 2; static S = 4; @@ -302,11 +298,11 @@ var Direction = class Direction { return opposite; } -}; +} // Classes for the layouts: // See src/prefs/layoutsPrefs.js for details on layouts. -var Layout = class Layout { +export class Layout { /** * @param {object} layout is the parsed object from the layouts file. */ @@ -416,7 +412,7 @@ var Layout = class Layout { return [true, '', -1]; } -}; +} var LayoutItem = class LayoutItem { constructor() { diff --git a/tiling-assistant@leleat-on-github/src/dependencies/gi.js b/tiling-assistant@leleat-on-github/src/dependencies/gi.js new file mode 100644 index 0000000..f9f0600 --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/gi.js @@ -0,0 +1,8 @@ +export { default as Atk } from 'gi://Atk'; +export { default as Clutter } from 'gi://Clutter'; +export { default as Gio } from 'gi://Gio'; +export { default as GLib } from 'gi://GLib'; +export { default as GObject } from 'gi://GObject'; +export { default as Meta } from 'gi://Meta'; +export { default as Shell } from 'gi://Shell'; +export { default as St } from 'gi://St'; diff --git a/tiling-assistant@leleat-on-github/src/dependencies/prefs.js b/tiling-assistant@leleat-on-github/src/dependencies/prefs.js new file mode 100644 index 0000000..30f203e --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/prefs.js @@ -0,0 +1,4 @@ +export { + ExtensionPreferences, + gettext as _ +} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; diff --git a/tiling-assistant@leleat-on-github/src/dependencies/prefs/gi.js b/tiling-assistant@leleat-on-github/src/dependencies/prefs/gi.js new file mode 100644 index 0000000..974c9c2 --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/prefs/gi.js @@ -0,0 +1,6 @@ +export { default as Adw } from 'gi://Adw'; +export { default as Gdk } from 'gi://Gdk'; +export { default as Gio } from 'gi://Gio'; +export { default as GLib } from 'gi://GLib'; +export { default as GObject } from 'gi://GObject'; +export { default as Gtk } from 'gi://Gtk'; diff --git a/tiling-assistant@leleat-on-github/src/dependencies/shell.js b/tiling-assistant@leleat-on-github/src/dependencies/shell.js new file mode 100644 index 0000000..cd0b07f --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/shell.js @@ -0,0 +1,11 @@ +export { + Extension, + gettext as _ +} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export * as AltTab from 'resource:///org/gnome/shell/ui/altTab.js'; +export * as Main from 'resource:///org/gnome/shell/ui/main.js'; +export * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +export * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +export * as SwitcherPopup from 'resource:///org/gnome/shell/ui/switcherPopup.js'; +export * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js'; diff --git a/tiling-assistant@leleat-on-github/src/dependencies/unexported/altTab.js b/tiling-assistant@leleat-on-github/src/dependencies/unexported/altTab.js new file mode 100644 index 0000000..ba8e793 --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/unexported/altTab.js @@ -0,0 +1,16 @@ +import { Meta } from '../gi.js'; + +export const baseIconSizes = [96, 64, 48, 32, 22]; +export const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds + +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); +} diff --git a/tiling-assistant@leleat-on-github/src/dependencies/unexported/windowManager.js b/tiling-assistant@leleat-on-github/src/dependencies/unexported/windowManager.js new file mode 100644 index 0000000..06b5db8 --- /dev/null +++ b/tiling-assistant@leleat-on-github/src/dependencies/unexported/windowManager.js @@ -0,0 +1 @@ +export const WINDOW_ANIMATION_TIME = 250; diff --git a/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js b/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js index 6180095..5c04f80 100644 --- a/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js +++ b/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js @@ -1,16 +1,11 @@ -'use strict'; +import { Clutter, GObject, Meta, St } from '../dependencies/gi.js'; +import { Main } from '../dependencies/shell.js'; -const { main: Main } = imports.ui; -const { Clutter, GObject, Meta, St } = imports.gi; +import { Settings } from '../common.js'; +import { Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Settings } = Me.imports.src.common; -const { Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; - -var Handler = class ActiveWindowHintHandler { +export default class ActiveWindowHintHandler { constructor() { // On a fresh install no color is set for the hint yet. Use the bg color // from the tile preview style by using a temporary widget. @@ -56,7 +51,7 @@ var Handler = class ActiveWindowHintHandler { this._hint = new AlwaysHint(); } } -}; +} const Hint = GObject.registerClass( class ActiveWindowHint extends St.Widget { diff --git a/tiling-assistant@leleat-on-github/src/extension/altTab.js b/tiling-assistant@leleat-on-github/src/extension/altTab.js index d7004e3..68d4d79 100644 --- a/tiling-assistant@leleat-on-github/src/extension/altTab.js +++ b/tiling-assistant@leleat-on-github/src/extension/altTab.js @@ -1,48 +1,87 @@ -'use strict'; - -const { altTab: AltTab, main: Main, switcherPopup: SwitcherPopup } = imports.ui; -const { Gio, GLib, GObject, Meta, Shell, St } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Settings } = Me.imports.src.common; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; +import { + Atk, + Clutter, + Gio, + GLib, + GObject, + Meta, + Shell, + St +} from '../dependencies/gi.js'; +import { + AltTab, + Extension, + Main, + SwitcherPopup +} from '../dependencies/shell.js'; +import { + baseIconSizes, + APP_ICON_HOVER_TIMEOUT +} from '../dependencies/unexported/altTab.js'; + +import { Settings } from '../common.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; /** * Optionally, override GNOME's altTab / appSwitcher to group tileGroups */ -var Override = class AltTabOverride { +export default class AltTabOverride { constructor() { - this._originalAltTab = AltTab.AppSwitcherPopup; - if (Settings.getBoolean(Settings.TILEGROUPS_IN_APP_SWITCHER)) - AltTab.AppSwitcherPopup = TilingAppSwitcherPopup; + this._overrideNativeAppSwitcher(); this._settingsId = Settings.changed(Settings.TILEGROUPS_IN_APP_SWITCHER, () => { - AltTab.AppSwitcherPopup = Settings.getBoolean(Settings.TILEGROUPS_IN_APP_SWITCHER) - ? TilingAppSwitcherPopup - : this._originalAltTab; + if (Settings.getBoolean(Settings.TILEGROUPS_IN_APP_SWITCHER)) + this._overrideNativeAppSwitcher(); + else + this._restoreNativeAppSwitcher(); }); } destroy() { Settings.disconnect(this._settingsId); - AltTab.AppSwitcherPopup = this._originalAltTab; + this._restoreNativeAppSwitcher(); } -}; -var TilingAppSwitcherPopup = GObject.registerClass( + _overrideNativeAppSwitcher() { + Main.wm.setCustomKeybindingHandler( + 'switch-applications', + Shell.ActionMode.NORMAL, + this._startSwitcher.bind(this) + ); + } + + _restoreNativeAppSwitcher() { + Main.wm.setCustomKeybindingHandler( + 'switch-applications', + Shell.ActionMode.NORMAL, + Main.wm._startSwitcher.bind(Main.wm) + ); + } + + /** + * Copy-pasta from windowManager.js. Removed unused stuff... + * + * @param {*} display - + * @param {*} window - + * @param {*} binding - + */ + _startSwitcher(display, window, binding) { + if (Main.wm._workspaceSwitcherPopup !== null) + Main.wm._workspaceSwitcherPopup.destroy(); + + const tabPopup = new TilingAppSwitcherPopup(); + + if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask())) + tabPopup.destroy(); + } +} + +export const TilingAppSwitcherPopup = GObject.registerClass( class TilingAppSwitcherPopup extends AltTab.AppSwitcherPopup { _init() { SwitcherPopup.SwitcherPopup.prototype._init.call(this); - this._thumbnails = null; - this._thumbnailTimeoutId = 0; - this._currentWindow = -1; - - this.thumbnailsVisible = false; - const settings = new Gio.Settings({ schema_id: 'org.gnome.shell.app-switcher' }); const workspace = settings.get_boolean('current-workspace-only') ? global.workspace_manager.get_active_workspace() @@ -80,13 +119,12 @@ class TilingAppSwitcherPopup extends AltTab.AppSwitcherPopup { } }); -var TilingAppSwitcher = GObject.registerClass( -class TilingAppSwitcher extends AltTab.AppSwitcher { +export const TilingAppSwitcher = GObject.registerClass( +class TilingAppSwitcher extends SwitcherPopup.SwitcherList { _init(altTabPopup, windows) { // Don't make the SwitcherButtons squares since 1 SwitcherButton // may contain multiple AppIcons for a tileGroup. - const squareItems = false; - SwitcherPopup.SwitcherList.prototype._init.call(this, squareItems); + super._init(false); this.icons = []; this._arrows = []; @@ -163,6 +201,163 @@ class TilingAppSwitcher extends AltTab.AppSwitcher { this._apps = []; } + _setIconSize() { + let j = 0; + while (this._items.length > 1 && this._items[j].style_class !== 'item-box') + j++; + + let themeNode = this._items[j].get_theme_node(); + this._list.ensure_style(); + + let iconPadding = themeNode.get_horizontal_padding(); + let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT); + let [, labelNaturalHeight] = this.icons[j].label.get_preferred_height(-1); + let iconSpacing = labelNaturalHeight + iconPadding + iconBorder; + let totalSpacing = this._list.spacing * (this._items.length - 1); + + // We just assume the whole screen here due to weirdness happening with the passed width + let primary = Main.layoutManager.primaryMonitor; + let parentPadding = this.get_parent().get_theme_node().get_horizontal_padding(); + let availWidth = primary.width - parentPadding - this.get_theme_node().get_horizontal_padding(); + + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + let iconSizes = baseIconSizes.map(s => s * scaleFactor); + let iconSize = baseIconSizes[0]; + + if (this._items.length > 1) { + for (let i = 0; i < baseIconSizes.length; i++) { + iconSize = baseIconSizes[i]; + let height = iconSizes[i] + iconSpacing; + let w = height * this._items.length + totalSpacing; + if (w <= availWidth) + break; + } + } + + this._iconSize = iconSize; + + for (let i = 0; i < this.icons.length; i++) { + // eslint-disable-next-line eqeqeq + if (this.icons[i].icon != null) + break; + this.icons[i].set_size(iconSize); + } + } + + vfunc_get_preferred_height(forWidth) { + if (!this._iconSize) + this._setIconSize(); + + return super.vfunc_get_preferred_height(forWidth); + } + + vfunc_allocate(box) { + // Allocate the main list items + super.vfunc_allocate(box); + + let contentBox = this.get_theme_node().get_content_box(box); + + let arrowHeight = Math.floor(this.get_theme_node().get_padding(St.Side.BOTTOM) / 3); + let arrowWidth = arrowHeight * 2; + + // Now allocate each arrow underneath its item + let childBox = new Clutter.ActorBox(); + for (let i = 0; i < this._items.length; i++) { + let itemBox = this._items[i].allocation; + childBox.x1 = contentBox.x1 + Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2); + childBox.x2 = childBox.x1 + arrowWidth; + childBox.y1 = contentBox.y1 + itemBox.y2 + arrowHeight; + childBox.y2 = childBox.y1 + arrowHeight; + this._arrows[i].allocate(childBox); + } + } + + // We override SwitcherList's _onItemMotion method to delay + // activation when the thumbnail list is open + _onItemMotion(item) { + if (item === this._items[this._highlighted] || + item === this._items[this._delayedHighlighted]) + return Clutter.EVENT_PROPAGATE; + + const index = this._items.indexOf(item); + + if (this._mouseTimeOutId !== 0) { + GLib.source_remove(this._mouseTimeOutId); + this._delayedHighlighted = -1; + this._mouseTimeOutId = 0; + } + + if (this._altTabPopup.thumbnailsVisible) { + this._delayedHighlighted = index; + this._mouseTimeOutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + APP_ICON_HOVER_TIMEOUT, + () => { + this._enterItem(index); + this._delayedHighlighted = -1; + this._mouseTimeOutId = 0; + return GLib.SOURCE_REMOVE; + }); + GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem'); + } else { + this._itemEntered(index); + } + + return Clutter.EVENT_PROPAGATE; + } + + _enterItem(index) { + let [x, y] = global.get_pointer(); + let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); + if (this._items[index].contains(pickedActor)) + this._itemEntered(index); + } + + // We override SwitcherList's highlight() method to also deal with + // the AppSwitcher->ThumbnailSwitcher arrows. Apps with only 1 window + // will hide their arrows by default, but show them when their + // thumbnails are visible (ie, when the app icon is supposed to be + // in justOutline mode). Apps with multiple windows will normally + // show a dim arrow, but show a bright arrow when they are + // highlighted. + highlight(n, justOutline) { + if (this.icons[this._highlighted]) { + if (this.icons[this._highlighted].cachedWindows.length === 1) + this._arrows[this._highlighted].hide(); + else + this._arrows[this._highlighted].remove_style_pseudo_class('highlighted'); + } + + super.highlight(n, justOutline); + + if (this._highlighted !== -1) { + if (justOutline && this.icons[this._highlighted].cachedWindows.length === 1) + this._arrows[this._highlighted].show(); + else + this._arrows[this._highlighted].add_style_pseudo_class('highlighted'); + } + } + + _addIcon(appIcon) { + this.icons.push(appIcon); + let item = this.addItem(appIcon, appIcon.label); + + appIcon.app.connectObject('notify::state', app => { + if (app.state !== Shell.AppState.RUNNING) + this._removeIcon(app); + }, this); + + let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' }); + arrow.connect('repaint', () => SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM)); + this.add_actor(arrow); + this._arrows.push(arrow); + + if (appIcon.cachedWindows.length === 1) + arrow.hide(); + else + item.add_accessible_state(Atk.StateType.EXPANDABLE); + } + _removeIcon(item) { const index = this.icons.findIndex(i => i === item); if (index === -1) @@ -181,7 +376,7 @@ class TilingAppSwitcher extends AltTab.AppSwitcher { * This may contain multiple AppIcons to represent a tileGroup with chain icons * between the AppIcons. */ -var AppSwitcherItem = GObject.registerClass({ +const AppSwitcherItem = GObject.registerClass({ Signals: { 'all-icons-removed': {} } }, class AppSwitcherItem extends St.BoxLayout { _init(windows) { @@ -220,7 +415,9 @@ var AppSwitcherItem = GObject.registerClass({ this.chainIcons = []; const winTracker = Shell.WindowTracker.get_default(); - const path = Me.dir.get_child('media/insert-link-symbolic.svg').get_path(); + const path = Extension.lookupByURL(import.meta.url) + .dir.get_child('media/insert-link-symbolic.svg') + .get_path(); const icon = new Gio.FileIcon({ file: Gio.File.new_for_path(path) }); const apps = this.isTileGroup diff --git a/tiling-assistant@leleat-on-github/src/extension/keybindingHandler.js b/tiling-assistant@leleat-on-github/src/extension/keybindingHandler.js index d6d74ba..f2c2fe1 100644 --- a/tiling-assistant@leleat-on-github/src/extension/keybindingHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/keybindingHandler.js @@ -1,25 +1,16 @@ -'use strict'; +import { Clutter, Meta, Shell, St } from '../dependencies/gi.js'; +import { _, Main } from '../dependencies/shell.js'; -const { Clutter, Meta, Shell, St } = imports.gi; -const Main = imports.ui.main; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Direction, DynamicKeybindings, Settings, Shortcuts } = Me.imports.src.common; -const { Rect, Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; - -const Gettext = imports.gettext; -const Domain = Gettext.domain(Me.metadata.uuid); -const _ = Domain.gettext; +import { Direction, DynamicKeybindings, Settings, Shortcuts } from '../common.js'; +import { Rect, Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; /** * Class to handle the keyboard shortcuts (on the extension side) except the * ones related to the Layouts. For those, see layoutsManager.js. */ -var Handler = class TilingKeybindingHandler { +export default class TilingKeybindingHandler { constructor() { const allowInOverview = [Shortcuts.TOGGLE_POPUP]; this._keyBindings = Shortcuts.getAllKeys(); @@ -42,7 +33,7 @@ var Handler = class TilingKeybindingHandler { /** * @param {string} shortcutName */ - _onCustomKeybindingPressed(shortcutName) { + async _onCustomKeybindingPressed(shortcutName) { // Debugging const debugging = [Shortcuts.DEBUGGING, Shortcuts.DEBUGGING_FREE_RECTS]; if (debugging.includes(shortcutName)) { @@ -53,7 +44,7 @@ var Handler = class TilingKeybindingHandler { const createIndicators = shortcutName === Shortcuts.DEBUGGING ? Util.___debugShowTiledRects : Util.___debugShowFreeScreenRects; - this._debuggingIndicators = createIndicators.call(Util); + this._debuggingIndicators = await createIndicators.call(Util); } return; @@ -85,7 +76,7 @@ var Handler = class TilingKeybindingHandler { // Tile Editing Mode } else if (shortcutName === Shortcuts.EDIT_MODE) { - const TileEditingMode = Me.imports.src.extension.tileEditingMode; + const TileEditingMode = await import('./tileEditingMode.js'); const tileEditor = new TileEditingMode.TileEditor(); tileEditor.open(); @@ -559,4 +550,4 @@ var Handler = class TilingKeybindingHandler { toggleTiling(); } } -}; +} diff --git a/tiling-assistant@leleat-on-github/src/extension/layoutsManager.js b/tiling-assistant@leleat-on-github/src/extension/layoutsManager.js index 2bad919..7722980 100644 --- a/tiling-assistant@leleat-on-github/src/extension/layoutsManager.js +++ b/tiling-assistant@leleat-on-github/src/extension/layoutsManager.js @@ -1,18 +1,15 @@ -'use strict'; - -const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; -const { main: Main, panelMenu: PanelMenu, popupMenu: PopupMenu } = imports.ui; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Layout, Settings } = Me.imports.src.common; -const { Rect, Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; - -const Gettext = imports.gettext; -const Domain = Gettext.domain(Me.metadata.uuid); -const _ = Domain.gettext; +import { Clutter, Gio, GObject, Meta, Shell, St } from '../dependencies/gi.js'; +import { + _, + Extension, + Main, + PanelMenu, + PopupMenu +} from '../dependencies/shell.js'; + +import { Layout, Settings } from '../common.js'; +import { Rect, Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; /** * Here are the classes to handle PopupLayouts on the shell / extension side. @@ -32,7 +29,7 @@ const _ = Domain.gettext; * the Edge Tiling. */ -var LayoutManager = class TilingLayoutsManager { +export default class TilingLayoutsManager { constructor() { // this._items is an array of LayoutItems (see explanation above). // this._currItem is 1 LayoutItem. A LayoutItem's rect only hold ratios @@ -79,7 +76,9 @@ var LayoutManager = class TilingLayoutsManager { // Add panel indicator this._panelIndicator = new PanelIndicator(); - Main.panel.addToStatusArea(Me.metadata.uuid, this._panelIndicator); + Main.panel.addToStatusArea( + 'tiling-assistant@leleat-on-github', + this._panelIndicator); this._settingsId = Settings.changed(Settings.SHOW_LAYOUT_INDICATOR, () => { this._panelIndicator.visible = Settings.getBoolean(Settings.SHOW_LAYOUT_INDICATOR); }); @@ -211,7 +210,7 @@ var LayoutManager = class TilingLayoutsManager { this._step(); } - _openTilingPopup() { + async _openTilingPopup() { // There are no open windows left to tile using the Tiling Popup. // However there may be items with appIds, which we want to open. // So continue... @@ -232,7 +231,7 @@ var LayoutManager = class TilingLayoutsManager { }); // Create the Tiling Popup - const TilingPopup = Me.imports.src.extension.tilingPopup; + const TilingPopup = await import('./tilingPopup.js'); const popup = new TilingPopup.TilingSwitcherPopup( this._remainingWindows, this._currRect, @@ -285,7 +284,7 @@ var LayoutManager = class TilingLayoutsManager { this._step(this._currItem.loopType); } } -}; +} /** * The GUI class for the Layout search. @@ -456,7 +455,10 @@ const PanelIndicator = GObject.registerClass({ _init() { super._init(0.0, 'Layout Indicator (Tiling Assistant)'); - const path = Me.dir.get_child('media/preferences-desktop-apps-symbolic.svg').get_path(); + const path = Extension.lookupByURL(import.meta.url) + .dir + .get_child('media/preferences-desktop-apps-symbolic.svg') + .get_path(); const gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(path) }); this.add_child(new St.Icon({ gicon, @@ -515,15 +517,8 @@ const PanelIndicator = GObject.registerClass({ // Center button without changing the size (for the hover highlight) settingsButton._icon.set_x_expand(true); settingsButton.label.set_x_expand(true); - settingsButton.connect('activate', () => { - try { - Main.overview.hide(); - const cmd = `gnome-extensions prefs ${Me.metadata.uuid}`; - GLib.spawn_command_line_async(cmd); - } catch (e) { - logError(e); - } - }); + settingsButton.connect('activate', + () => Extension.lookupByURL(import.meta.url).openPreferences()); this.menu.addMenuItem(settingsButton); } }); diff --git a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js index 7c2211c..9418296 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -1,14 +1,10 @@ -'use strict'; +import { Clutter, GLib, GObject, Gio, Meta } from '../dependencies/gi.js'; +import { Main, WindowManager } from '../dependencies/shell.js'; +import { WINDOW_ANIMATION_TIME } from '../dependencies/unexported/windowManager.js'; -const { Clutter, GLib, GObject, Meta } = imports.gi; -const { main: Main, windowManager: WindowManager } = imports.ui; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Orientation, RestoreOn, MoveModes, Settings, Shortcuts } = Me.imports.src.common; -const { Rect, Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; +import { Orientation, RestoreOn, MoveModes, Settings, Shortcuts } from '../common.js'; +import { Rect, Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; /** * This class gets to handle the move events (grab & monitor change) of windows. @@ -18,7 +14,7 @@ const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; * is a setting to restore a tiled window's size on the actual grab end. */ -var Handler = class TilingMoveHandler { +export default class TilingMoveHandler { constructor() { const moveOps = [Meta.GrabOp.MOVING, Meta.GrabOp.KEYBOARD_MOVING]; @@ -48,7 +44,9 @@ var Handler = class TilingMoveHandler { // The mouse button mod to move/resize a window may be changed to Alt. // So switch Alt and Super in our own prefs, if the user switched from // Super to Alt. - const wmPrefs = ExtensionUtils.getSettings('org.gnome.desktop.wm.preferences'); + const wmPrefs = new Gio.Settings({ + schema_id: 'org.gnome.desktop.wm.preferences' + }); const altAsMod = wmPrefs.get_string('mouse-button-modifier') === ''; if (altAsMod) { for (const s of [Settings.ADAPTIVE_TILING_MOD, Settings.FAVORITE_LAYOUT_MOD]) { @@ -864,7 +862,7 @@ var Handler = class TilingMoveHandler { this._tileRect = null; this._tilePreview.close(); } -}; +} const TilePreview = GObject.registerClass( class TilePreview extends WindowManager.TilePreview { @@ -919,7 +917,7 @@ class TilePreview extends WindowManager.TilePreview { width: tileRect.width, height: tileRect.height, opacity: 255, - duration: WindowManager.WINDOW_ANIMATION_TIME, + duration: WINDOW_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD }; } else { @@ -928,7 +926,7 @@ class TilePreview extends WindowManager.TilePreview { animateTo.width === undefined && this.set_width(tileRect.width); animateTo.height === undefined && this.set_height(tileRect.height); animateTo.opacity === undefined && this.set_opacity(255); - animateTo.duration = animateTo.duration ?? WindowManager.WINDOW_ANIMATION_TIME; + animateTo.duration = animateTo.duration ?? WINDOW_ANIMATION_TIME; animateTo.mode = animateTo.mode ?? Clutter.AnimationMode.EASE_OUT_QUAD; } diff --git a/tiling-assistant@leleat-on-github/src/extension/resizeHandler.js b/tiling-assistant@leleat-on-github/src/extension/resizeHandler.js index 1a37338..2400fe2 100644 --- a/tiling-assistant@leleat-on-github/src/extension/resizeHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/resizeHandler.js @@ -1,14 +1,8 @@ -'use strict'; +import { Clutter, Meta } from '../dependencies/gi.js'; -const { Clutter, Meta } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Orientation } = Me.imports.src.common; -const Settings = Me.imports.src.common.Settings; -const { Rect, Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; +import { Orientation, Settings } from '../common.js'; +import { Rect, Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; const Side = { NONE: 0, @@ -25,7 +19,7 @@ const Side = { * resizing is split into its [H]orizontal and [V]ertical components. */ -var Handler = class TilingResizeHandler { +export default class TilingResizeHandler { constructor() { const isResizing = grabOp => { switch (grabOp) { @@ -493,7 +487,7 @@ var Handler = class TilingResizeHandler { findBorderingWindows(tileGroup, sameSide, oppositeSide, resizeIsNOrW); return [...sameSide, ...oppositeSide]; } -}; +} /** * Saves information on which side a window will resize to complement the diff --git a/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js b/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js index 588a364..99930e7 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js +++ b/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js @@ -1,18 +1,9 @@ -'use strict'; +import { Clutter, GObject, Meta, St } from '../dependencies/gi.js'; +import { _, Main } from '../dependencies/shell.js'; -const { Clutter, GObject, Meta, St } = imports.gi; -const Main = imports.ui.main; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Direction, Orientation, Settings } = Me.imports.src.common; -const { Rect, Util } = Me.imports.src.extension.utility; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; - -const Gettext = imports.gettext; -const Domain = Gettext.domain(Me.metadata.uuid); -const _ = Domain.gettext; +import { Direction, Orientation, Settings } from '../common.js'; +import { Rect, Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; const SCALE_SIZE = 100; const Modes = { @@ -31,7 +22,7 @@ const Modes = { * 'on key released' function. */ -var TileEditor = GObject.registerClass( +export const TileEditor = GObject.registerClass( class TileEditingMode extends St.Widget { _init() { super._init({ reactive: true }); @@ -47,6 +38,9 @@ class TileEditingMode extends St.Widget { this._keyHandler = null; Main.uiGroup.add_child(this); + + this.connect('key-press-event', (__, event) => + this._onKeyPressEvent(event)); } open() { @@ -120,8 +114,8 @@ class TileEditingMode extends St.Widget { this.close(); } - vfunc_key_press_event(keyEvent) { - const mods = keyEvent.modifier_state; + async _onKeyPressEvent(keyEvent) { + const mods = keyEvent.get_state(); let newMode; // Swap windows @@ -142,7 +136,7 @@ class TileEditingMode extends St.Widget { this._switchMode(newMode); // Handle the key press and get mode depending on that. - newMode = this._keyHandler.handleKeyPress(keyEvent); + newMode = await this._keyHandler.handleKeyPress(keyEvent); if (newMode && newMode !== this._mode) this._switchMode(newMode); @@ -256,8 +250,8 @@ const DefaultKeyHandler = class DefaultKeyHandler { * @param {number} keyEvent * @returns {Modes} The mode to enter after the event was handled. */ - handleKeyPress(keyEvent) { - const keyVal = keyEvent.keyval; + async handleKeyPress(keyEvent) { + const keyVal = keyEvent.get_key_symbol(); // [Directions] to move focus with WASD, hjkl or arrow keys const dir = Util.getDirection(keyVal); @@ -344,8 +338,8 @@ const DefaultKeyHandler = class DefaultKeyHandler { } else if (keyVal === Clutter.KEY_space) { const allWs = Settings.getBoolean(Settings.POPUP_ALL_WORKSPACES); const openWindows = Twm.getWindows(allWs).filter(w => !this._windows.includes(w)); - const { TilingSwitcherPopup } = Me.imports.src.extension.tilingPopup; - const tilingPopup = new TilingSwitcherPopup( + const TilingPopup = await import('./tilingPopup.js'); + const tilingPopup = new TilingPopup.TilingSwitcherPopup( openWindows, this._selectIndicator.rect, false @@ -437,21 +431,21 @@ const SwapKeyHandler = class SwapKeyHandler extends DefaultKeyHandler { } handleKeyPress(keyEvent) { - const direction = Util.getDirection(keyEvent.keyval); + const direction = Util.getDirection(keyEvent.get_key_symbol()); // [Directions] to choose a window to swap with WASD, hjkl or arrow keys if (direction) this._focusInDir(direction); // [Esc]ape Tile Editing Mode - else if (keyEvent.keyval === Clutter.KEY_Escape) + else if (keyEvent.get_key_symbol() === Clutter.KEY_Escape) return Modes.DEFAULT; return Modes.SWAP; } handleKeyRelease(keyEvent) { - const keyVal = keyEvent.keyval; + const keyVal = keyEvent.get_key_symbol(); const ctrlKeys = [Clutter.KEY_Control_L, Clutter.KEY_Control_R]; if (ctrlKeys.includes(keyVal)) { @@ -485,8 +479,8 @@ const SwapKeyHandler = class SwapKeyHandler extends DefaultKeyHandler { */ const MoveKeyHandler = class MoveKeyHandler extends DefaultKeyHandler { handleKeyPress(keyEvent) { - const direction = Util.getDirection(keyEvent.keyval); - const moveWorkspace = keyEvent.modifier_state & Clutter.ModifierType.MOD1_MASK; + const direction = Util.getDirection(keyEvent.get_key_symbol()); + const moveWorkspace = keyEvent.get_state() & Clutter.ModifierType.MOD1_MASK; // [Directions] to move the tile group if (direction) { @@ -533,7 +527,7 @@ const MoveKeyHandler = class MoveKeyHandler extends DefaultKeyHandler { return Modes.CLOSE; // [Esc] to return to default mode - } else if (keyEvent.keyval === Clutter.KEY_Escape) { + } else if (keyEvent.get_key_symbol() === Clutter.KEY_Escape) { return Modes.DEFAULT; } @@ -561,7 +555,7 @@ const ResizeKeyHandler = class ResizeKeyHandler extends DefaultKeyHandler { handleKeyPress(keyEvent) { // [Directions] to resize with WASD, hjkl or arrow keys - const direction = Util.getDirection(keyEvent.keyval); + const direction = Util.getDirection(keyEvent.get_key_symbol()); if (direction) { const window = this._selectIndicator.window; if (!window) @@ -599,7 +593,7 @@ const ResizeKeyHandler = class ResizeKeyHandler extends DefaultKeyHandler { this._resizeSideIndicator.updatePos(window.tiledRect); // [Esc]ape Tile Editing Mode - } else if (keyEvent.keyval === Clutter.KEY_Escape) { + } else if (keyEvent.get_key_symbol() === Clutter.KEY_Escape) { return Modes.CLOSE; } @@ -607,7 +601,7 @@ const ResizeKeyHandler = class ResizeKeyHandler extends DefaultKeyHandler { } handleKeyRelease(keyEvent) { - const keyVal = keyEvent.keyval; + const keyVal = keyEvent.get_key_symbol(); const superKeys = [Clutter.KEY_Super_L, Clutter.KEY_Super_R]; return superKeys.includes(keyVal) ? Modes.DEFAULT : Modes.RESIZE; } diff --git a/tiling-assistant@leleat-on-github/src/extension/tilingPopup.js b/tiling-assistant@leleat-on-github/src/extension/tilingPopup.js index ab971f8..72afd45 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tilingPopup.js +++ b/tiling-assistant@leleat-on-github/src/extension/tilingPopup.js @@ -1,15 +1,10 @@ -'use strict'; +import { Clutter, GObject, Meta, St } from '../dependencies/gi.js'; +import { Main, SwitcherPopup } from '../dependencies/shell.js'; -const { Clutter, GObject, Meta, Shell, St } = imports.gi; -const { main: Main, switcherPopup: SwitcherPopup } = imports.ui; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Direction, Orientation } = Me.imports.src.common; -const Util = Me.imports.src.extension.utility.Util; -const Twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; -const AltTab = Me.imports.src.extension.altTab; +import { Direction, Orientation } from '../common.js'; +import { Util } from './utility.js'; +import { TilingWindowManager as Twm } from './tilingWindowManager.js'; +import * as AltTab from './altTab.js'; /** * Classes for the Tiling Popup, which opens when tiling a window @@ -17,7 +12,7 @@ const AltTab = Me.imports.src.extension.altTab; * Mostly based on GNOME's altTab.js */ -var TilingSwitcherPopup = GObject.registerClass({ +export const TilingSwitcherPopup = GObject.registerClass({ Signals: { // Bool indicates whether the Tiling Popup was canceled // (or if a window was tiled with this popup) @@ -210,7 +205,7 @@ var TilingSwitcherPopup = GObject.registerClass({ } vfunc_button_press_event(buttonEvent) { - const btn = buttonEvent.button; + const btn = buttonEvent.get_button(); if (btn === Clutter.BUTTON_MIDDLE || btn === Clutter.BUTTON_SECONDARY) { this._finish(global.get_current_time()); return Clutter.EVENT_PROPAGATE; diff --git a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js index 1c39c3e..c4d7bc7 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js +++ b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js @@ -1,20 +1,15 @@ -'use strict'; +import { Clutter, GLib, GObject, Meta, Shell } from '../dependencies/gi.js'; +import { Main } from '../dependencies/shell.js'; +import { getWindows } from '../dependencies/unexported/altTab.js'; -const { altTab: AltTab, main: Main } = imports.ui; -const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Orientation, Settings, Shortcuts } = Me.imports.src.common; -const { Axis, Rect, Util } = Me.imports.src.extension.utility; -const GNOME_VERSION = parseFloat(imports.misc.config.PACKAGE_VERSION); +import { Orientation, Settings, Shortcuts } from '../common.js'; +import { Rect, Util } from './utility.js'; /** * Singleton responsible for tiling. Implement the signals in a separate Clutter * class so this doesn't need to be instanced. */ -var TilingWindowManager = class TilingWindowManager { +export class TilingWindowManager { static initialize() { this._signals = new TilingSignals(); @@ -76,7 +71,7 @@ var TilingWindowManager = class TilingWindowManager { */ static getWindows(allWorkspaces = false) { const activeWs = global.workspace_manager.get_active_workspace(); - const openWindows = AltTab.getWindows(allWorkspaces ? null : activeWs); + const openWindows = getWindows(allWorkspaces ? null : activeWs); // The open windows are not sorted properly when tiling with the Tiling // Popup because altTab sorts by focus. const sorted = global.display.sort_windows_by_stacking(openWindows); @@ -125,7 +120,7 @@ var TilingWindowManager = class TilingWindowManager { * @param {boolean} [fakeTile=false] don't create a new tile group, don't * emit 'tiled' signal or open the Tiling Popup */ - static tile(window, newRect, { + static async tile(window, newRect, { openTilingPopup = true, ignoreTA = false, monitorNr = null, @@ -232,7 +227,7 @@ var TilingWindowManager = class TilingWindowManager { this.emit('window-tiled', window); if (openTilingPopup) - this.tryOpeningTilingPopup(); + await this.tryOpeningTilingPopup(); } } @@ -928,7 +923,7 @@ var TilingWindowManager = class TilingWindowManager { * Opens the Tiling Popup, if there is unambiguous free screen space, * and offer to tile an open window to that spot. */ - static tryOpeningTilingPopup() { + static async tryOpeningTilingPopup() { if (!Settings.getBoolean(Settings.ENABLE_TILING_POPUP)) return; @@ -945,7 +940,7 @@ var TilingWindowManager = class TilingWindowManager { if (!freeSpace) return; - const TilingPopup = Me.imports.src.extension.tilingPopup; + const TilingPopup = await import('./tilingPopup.js'); const popup = new TilingPopup.TilingSwitcherPopup(openWindows, freeSpace); if (!popup.show(topTileGroup)) popup.destroy(); @@ -1284,7 +1279,7 @@ var TilingWindowManager = class TilingWindowManager { this.untile(window, { restoreFullPos: false, clampToWorkspace: true, skipAnim: Main.overview.visible }); } } -}; +} /** * This is instanced by the 'TilingWindowManager'. It implements the tiling diff --git a/tiling-assistant@leleat-on-github/src/extension/utility.js b/tiling-assistant@leleat-on-github/src/extension/utility.js index cd73b7e..5f64075 100644 --- a/tiling-assistant@leleat-on-github/src/extension/utility.js +++ b/tiling-assistant@leleat-on-github/src/extension/utility.js @@ -1,22 +1,14 @@ -'use strict'; +import { Clutter, Gio, GLib, Meta, St } from '../dependencies/gi.js'; +import { Main } from '../dependencies/shell.js'; -const { Clutter, Gio, GLib, Meta, St } = imports.gi; -const { main: Main } = imports.ui; -const ByteArray = imports.byteArray; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const { Direction, Orientation, Settings } = Me.imports.src.common; - -const GNOME_VERSION = parseFloat(imports.misc.config.PACKAGE_VERSION); +import { Direction, Orientation, Settings } from '../common.js'; /** * Library of commonly used functions for the extension.js' files * (and *not* the prefs files) */ -var Util = class Utility { +export class Util { /** * Performs an approximate equality check. There will be times when * there will be inaccuracies. For example, the user may enable window @@ -141,7 +133,7 @@ var Util = class Utility { if (!success || !contents.length) return []; - return JSON.parse(ByteArray.toString(contents)); + return JSON.parse(new TextDecoder().decode(contents)); } /** @@ -212,8 +204,8 @@ var Util = class Utility { * * @returns {St.Widget[]} an array of St.Widgets to indicate the tiled rects. */ - static ___debugShowTiledRects() { - const twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; + static async ___debugShowTiledRects() { + const twm = (await import('./tilingWindowManager.js')).TilingWindowManager; const topTileGroup = twm.getTopTileGroup(); if (!topTileGroup.length) { Main.notify('Tiling Assistant', 'No tiled windows / tiled rects.'); @@ -243,11 +235,11 @@ var Util = class Utility { * @returns {St.Widget[]} an array of St.Widgets to indicate the free * screen rects. */ - static ___debugShowFreeScreenRects() { + static async ___debugShowFreeScreenRects() { const activeWs = global.workspace_manager.get_active_workspace(); const monitor = global.display.get_current_monitor(); const workArea = new Rect(activeWs.get_work_area_for_monitor(monitor)); - const twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; + const twm = (await import('./tilingWindowManager.js')).TilingWindowManager; const topTileGroup = twm.getTopTileGroup(); const tRects = topTileGroup.map(w => w.tiledRect); const freeScreenSpace = twm.getFreeScreen(tRects); @@ -276,9 +268,9 @@ var Util = class Utility { /** * Print the tile groups to the logs. */ - static __debugPrintTileGroups() { + static async __debugPrintTileGroups() { log('--- Tiling Assistant: Start ---'); - const twm = Me.imports.src.extension.tilingWindowManager.TilingWindowManager; + const twm = await import('./tilingWindowManager.js'); const openWindows = twm.getWindows(); openWindows.forEach(w => { if (!w.isTiled) @@ -291,12 +283,12 @@ var Util = class Utility { }); log('--- Tiling Assistant: End ---'); } -}; +} /** * Wrapper for Meta.Rectangle to add some more functions. */ -var Rect = class Rect { +export class Rect { /** * @param {...any} params No parameters, 1 Meta.Rectangle or the x, y, * width and height values should be passed to the constructor. @@ -817,4 +809,4 @@ var Rect = class Rect { set height(value) { this._rect.height = Math.floor(value); } -}; +} diff --git a/tiling-assistant@leleat-on-github/src/prefs/layoutRow.js b/tiling-assistant@leleat-on-github/src/prefs/layoutRow.js index 5fe0ab3..437b97c 100644 --- a/tiling-assistant@leleat-on-github/src/prefs/layoutRow.js +++ b/tiling-assistant@leleat-on-github/src/prefs/layoutRow.js @@ -1,16 +1,8 @@ -'use strict'; +import { Gdk, Gtk, GObject } from '../dependencies/prefs/gi.js'; +import { _ } from '../dependencies/prefs.js'; -const { Gdk, Gio, Gtk, GObject } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const Layout = Me.imports.src.common.Layout; -const LayoutRowEntry = Me.imports.src.prefs.layoutRowEntry.LayoutRowEntry; - -const Gettext = imports.gettext; -const Domain = Gettext.domain(Me.metadata.uuid); -const _ = Domain.gettext; +import { Layout } from '../common.js'; +import { LayoutRowEntry } from './layoutRowEntry.js'; /** * 1 LayoutRow represents 1 Layout in the preference window. It's just instanced @@ -20,9 +12,9 @@ const _ = Domain.gettext; * { rect, appId, loopType }. The rect is mandatory, the rest not. */ -var LayoutRow = GObject.registerClass({ +export const LayoutRow = GObject.registerClass({ GTypeName: 'TilingLayoutRow', - Template: Gio.File.new_for_path(`${Me.path}/src/ui/layoutRow.ui`).get_uri(), + Template: import.meta.url.replace(/prefs\/(.*)\.js$/, 'ui/$1.ui'), InternalChildren: [ 'addRowEntryButton', 'deleteButton', diff --git a/tiling-assistant@leleat-on-github/src/prefs/layoutRowEntry.js b/tiling-assistant@leleat-on-github/src/prefs/layoutRowEntry.js index c83501f..073b684 100644 --- a/tiling-assistant@leleat-on-github/src/prefs/layoutRowEntry.js +++ b/tiling-assistant@leleat-on-github/src/prefs/layoutRowEntry.js @@ -1,21 +1,13 @@ -'use strict'; - -const { Gio, Gtk, GObject } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const Gettext = imports.gettext; -const Domain = Gettext.domain(Me.metadata.uuid); -const _ = Domain.gettext; +import { Gio, Gtk, GObject } from '../dependencies/prefs/gi.js'; +import { _ } from '../dependencies/prefs.js'; /** * Multiple LayoutRowEntries make up a LayoutRow.js. See that file for more info. */ -var LayoutRowEntry = GObject.registerClass({ +export const LayoutRowEntry = GObject.registerClass({ GTypeName: 'TilingLayoutRowEntry', - Template: Gio.File.new_for_path(`${Me.path}/src/ui/layoutRowEntry.ui`).get_uri(), + Template: import.meta.url.replace(/prefs\/(.*)\.js$/, 'ui/$1.ui'), InternalChildren: [ 'rectEntry', 'rectLabel', diff --git a/tiling-assistant@leleat-on-github/src/prefs/layoutsPrefs.js b/tiling-assistant@leleat-on-github/src/prefs/layoutsPrefs.js index 46d1ef6..8e93ae7 100644 --- a/tiling-assistant@leleat-on-github/src/prefs/layoutsPrefs.js +++ b/tiling-assistant@leleat-on-github/src/prefs/layoutsPrefs.js @@ -1,12 +1,6 @@ -'use strict'; +import { Gio, GLib } from '../dependencies/prefs/gi.js'; -const { Gio, GLib } = imports.gi; -const ByteArray = imports.byteArray; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const LayoutRow = Me.imports.src.prefs.layoutRow.LayoutRow; +import { LayoutRow } from './layoutRow.js'; /** * This class takes care of everything related to layouts (at least on the @@ -37,8 +31,8 @@ const LayoutRow = Me.imports.src.prefs.layoutRow.LayoutRow; * an existing feature... thus it's hidden */ -var Prefs = class TilingLayoutsPrefs { - constructor(settings, builder) { +export default class { + constructor(settings, builder, path) { // Keep a reference to the settings for the shortcuts this._settings = settings; @@ -74,10 +68,10 @@ var Prefs = class TilingLayoutsPrefs { }); // Finally, load the existing settings. - this._loadLayouts(); + this._loadLayouts(path); } - _loadLayouts() { + _loadLayouts(path) { this._applySaveButtonStyle(''); this._forEachLayoutRow(row => row.destroy()); @@ -93,7 +87,7 @@ var Prefs = class TilingLayoutsPrefs { // Custom layouts are already defined in the file. if (contents.length) { - layouts = JSON.parse(ByteArray.toString(contents)); + layouts = JSON.parse(new TextDecoder().decode(contents)); // Ensure at least 1 empty row otherwise the listbox won't have // a height but a weird looking shadow only. layouts.length @@ -108,12 +102,12 @@ var Prefs = class TilingLayoutsPrefs { return; this._settings.set_boolean(importExamples, false); - const exampleFile = this._makeFile(`${Me.path}/src`, 'layouts_example.json'); + const exampleFile = this._makeFile(`${path}/src`, 'layouts_example.json'); const [succ, c] = exampleFile.load_contents(null); if (!succ) return; - layouts = c.length ? JSON.parse(ByteArray.toString(c)) : []; + layouts = c.length ? JSON.parse(new TextDecoder().decode(c)) : []; layouts.forEach((layout, idx) => this._createLayoutRow(idx, layout)); this._saveLayouts(); } @@ -225,4 +219,4 @@ var Prefs = class TilingLayoutsPrefs { child = nxtSibling; } } -}; +} diff --git a/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js b/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js index 853879d..29e8d32 100644 --- a/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js +++ b/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js @@ -1,9 +1,4 @@ -'use strict'; - -const { Adw, Gdk, Gio, GObject, Gtk } = imports.gi; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +import { Adw, Gdk, GObject, Gtk } from '../dependencies/prefs/gi.js'; /** * A Widget to implement the shortcuts in the preference window. @@ -15,9 +10,9 @@ const Me = ExtensionUtils.getCurrentExtension(); * https://gitlab.com/rmnvgr/nightthemeswitcher-gnome-shell-extension/-/blob/main/src/utils.js */ -var ShortcutListener = GObject.registerClass({ +export const ShortcutListener = GObject.registerClass({ GTypeName: 'ShortcutListener', - Template: Gio.File.new_for_path(`${Me.path}/src/ui/shortcutListener.ui`).get_uri(), + Template: import.meta.url.replace(/prefs\/(.*)\.js$/, 'ui/$1.ui'), InternalChildren: ['keybindingLabel', 'clearButton', 'eventKeyController'], Properties: { keybinding: GObject.ParamSpec.string( From b5a9865347766701d193813e6b46d4407ac948ec Mon Sep 17 00:00:00 2001 From: Leleat Date: Thu, 17 Aug 2023 18:51:10 +0200 Subject: [PATCH 3/5] Port 45: Drop compatibility code --- .../src/extension/activeWindowHint.js | 37 +++++++++++-------- .../src/extension/moveHandler.js | 35 +----------------- .../src/extension/tileEditingMode.js | 4 +- .../src/extension/tilingWindowManager.js | 8 ++-- .../src/extension/utility.js | 23 ------------ 5 files changed, 28 insertions(+), 79 deletions(-) diff --git a/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js b/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js index 5c04f80..f0943ea 100644 --- a/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js +++ b/tiling-assistant@leleat-on-github/src/extension/activeWindowHint.js @@ -2,7 +2,6 @@ import { Clutter, GObject, Meta, St } from '../dependencies/gi.js'; import { Main } from '../dependencies/shell.js'; import { Settings } from '../common.js'; -import { Util } from './utility.js'; import { TilingWindowManager as Twm } from './tilingWindowManager.js'; export default class ActiveWindowHintHandler { @@ -106,7 +105,7 @@ class MinimalActiveWindowHint extends Hint { _reset() { if (this._laterId) { - Util.laterRemove(this._laterId); + global.compositor.get_laters().remove(this._laterId); delete this._laterId; } this._windowClone?.destroy(); @@ -195,11 +194,14 @@ class MinimalActiveWindowHint extends Hint { return; if (!this._laterId) { - this._laterId = Util.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => { - global.window_group.set_child_below_sibling(this, actor); - delete this._laterId; - return false; - }); + this._laterId = global.compositor.get_laters().add( + Meta.LaterType.BEFORE_REDRAW, + () => { + global.window_group.set_child_below_sibling(this, actor); + delete this._laterId; + return false; + } + ); } const { x, y, width, height } = window.get_frame_rect(); @@ -226,9 +228,9 @@ class MinimalActiveWindowHint extends Hint { // TODO a solid bg color looks better than a border when launching an app since // the border will appear before the window is fully visible. However there was // an issue with global.window_group.set_child_below_sibling not putting the hint -// below the window for some reason. Util.laterAdd solved it but I don't know +// below the window for some reason. laters-add solved it but I don't know // why. So as to not potentially cover the entire window's content use the border -// style until I figure out if Util.laterAdd is the proper solution... +// style until I figure out if laters-add is the proper solution... const AlwaysHint = GObject.registerClass( class AlwaysActiveWindowHint extends Hint { _init() { @@ -279,7 +281,7 @@ class AlwaysActiveWindowHint extends Hint { return; - Util.laterRemove(this._showLater); + global.compositor.get_laters().remove(this._showLater); delete this._showLater; } @@ -311,12 +313,15 @@ class AlwaysActiveWindowHint extends Hint { if (!actor || this._showLater) return; - this._showLater = Util.laterAdd(Meta.LaterType.IDLE, () => { - global.window_group.set_child_below_sibling(this, actor); - this.show(); - delete this._showLater; - return false; - }); + this._showLater = global.compositor.get_laters().add( + Meta.LaterType.IDLE, + () => { + global.window_group.set_child_below_sibling(this, actor); + this.show(); + delete this._showLater; + return false; + } + ); } _updateStyle() { diff --git a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js index 9418296..50e3757 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -427,46 +427,13 @@ export default class TilingMoveHandler { } _restoreSizeAndRestartGrab(window, px, py, grabOp) { - global.display.end_grab_op?.(global.get_current_time()); - - const rect = window.get_frame_rect(); - const x = px - rect.x; - const relativeX = x / rect.width; - let untiledRect = window.untiledRect; Twm.untile(window, { restoreFullPos: false, xAnchor: px, skipAnim: this._wasMaximizedOnStart }); - if (!global.display.begin_grab_op) { - this._onMoveStarted(window, grabOp); - return; - } - - // untiledRect is null, if the window was maximized via non-extension - // way (dblc-ing the titlebar, maximize button...). So just get the - // restored window's rect directly... doesn't work on Wayland because - // get_frame_rect() doesn't return the correct size immediately after - // calling untile()... in that case just guess a random size - if (!untiledRect && !Meta.is_wayland_compositor()) - untiledRect = new Rect(window.get_frame_rect()); - - const untiledWidth = untiledRect?.width ?? 1000; - const postUntileRect = window.get_frame_rect(); - - global.display.begin_grab_op( - window, - grabOp, - true, // Pointer already grabbed - true, // Frame action - -1, // Button - global.get_pointer()[2], // modifier - global.get_current_time(), - postUntileRect.x + untiledWidth * relativeX, - // So the pointer isn't above the window in some cases. - Math.max(py, postUntileRect.y) - ); + this._onMoveStarted(window, grabOp); } /** diff --git a/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js b/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js index 99930e7..599d85e 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js +++ b/tiling-assistant@leleat-on-github/src/extension/tileEditingMode.js @@ -75,7 +75,7 @@ class TileEditingMode extends St.Widget { // The windows may not be at the foreground. They just weren't // overlapping other windows. So raise the entire tile group. - this._windows.forEach(w => w.raise_and_make_recent?.() ?? w.raise()); + this._windows.forEach(w => w.raise_and_make_recent()); // Create the active selection indicator. const window = this._windows[0]; @@ -327,7 +327,7 @@ const DefaultKeyHandler = class DefaultKeyHandler { return Modes.CLOSE; // Re-raise tile group, so it isn't below the just-untiled window - this._windows[0].raise_and_make_recent?.() ?? this._windows[0].raise(); + this._windows[0].raise_and_make_recent(); this._selectIndicator.focus(selectedRect, null); // [Enter] / [Esc]ape Tile Editing Mode diff --git a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js index c4d7bc7..6ed7a79 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js +++ b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js @@ -147,7 +147,7 @@ export class TilingWindowManager { window.unminimize(); // Raise window since tiling with the popup means that // the window can be below others. - window.raise_and_make_recent?.() ?? window.raise(); + window.raise_and_make_recent(); const oldRect = new Rect(window.get_frame_rect()); const monitor = monitorNr ?? window.get_monitor(); @@ -256,7 +256,7 @@ export class TilingWindowManager { // one. So untiling the initial window after tiling more windows with // the popup (without re-focusing the initial window), means the // untiled window will be below the others. - window.raise_and_make_recent?.() ?? window.raise(); + window.raise_and_make_recent(); // Animation const untileAnim = Settings.getBoolean(Settings.ENABLE_UNTILE_ANIMATIONS); @@ -433,7 +433,7 @@ export class TilingWindowManager { // Prevent an infinite loop of windows raising each other w.block_signal_handler(otherRaiseId); - w.raise_and_make_recent?.() ?? w.raise(); + w.raise_and_make_recent(); w.unblock_signal_handler(otherRaiseId); }); @@ -442,7 +442,7 @@ export class TilingWindowManager { // it may be below other tiled windows. const signalId = this._signals.getSignalsFor(raisedWindowId).get(TilingSignals.RAISE); raisedWindow.block_signal_handler(signalId); - raisedWindow.raise_and_make_recent?.() ?? raisedWindow.raise(); + raisedWindow.raise_and_make_recent(); raisedWindow.unblock_signal_handler(signalId); } diff --git a/tiling-assistant@leleat-on-github/src/extension/utility.js b/tiling-assistant@leleat-on-github/src/extension/utility.js index 5f64075..8ada3c6 100644 --- a/tiling-assistant@leleat-on-github/src/extension/utility.js +++ b/tiling-assistant@leleat-on-github/src/extension/utility.js @@ -176,29 +176,6 @@ export class Util { return favoriteLayout; } - /** - * Add a Meta.Later depending on the shell version - * - * @param laterType - * @param callback - */ - static laterAdd(laterType, callback) { - return global.compositor?.get_laters?.().add(laterType, callback) ?? - Meta.later_add(laterType, callback); - } - - /** - * Removes a Meta.Later depending on the shell version - * - * @param id - */ - static laterRemove(id) { - if (global.compositor?.get_laters) - global.compositor?.get_laters().remove(id); - else - Meta.later_remove(id); - } - /** * Shows the tiled rects of the top tile group. * From 0626aab5da46b411883e6e9b88406fcec5d370e6 Mon Sep 17 00:00:00 2001 From: Leleat Date: Sat, 2 Sep 2023 12:56:27 +0200 Subject: [PATCH 4/5] Port 45: Replace Meta.Rectangle with Mtk.Rectangle See https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3128 --- .../src/dependencies/gi.js | 1 + .../src/extension/moveHandler.js | 4 +-- .../src/extension/tilingWindowManager.js | 4 +-- .../src/extension/utility.js | 28 +++++++++---------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/tiling-assistant@leleat-on-github/src/dependencies/gi.js b/tiling-assistant@leleat-on-github/src/dependencies/gi.js index f9f0600..937881a 100644 --- a/tiling-assistant@leleat-on-github/src/dependencies/gi.js +++ b/tiling-assistant@leleat-on-github/src/dependencies/gi.js @@ -4,5 +4,6 @@ export { default as Gio } from 'gi://Gio'; export { default as GLib } from 'gi://GLib'; export { default as GObject } from 'gi://GObject'; export { default as Meta } from 'gi://Meta'; +export { default as Mtk } from 'gi://Mtk'; export { default as Shell } from 'gi://Shell'; export { default as St } from 'gi://St'; diff --git a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js index 50e3757..43d3c96 100644 --- a/tiling-assistant@leleat-on-github/src/extension/moveHandler.js +++ b/tiling-assistant@leleat-on-github/src/extension/moveHandler.js @@ -1,4 +1,4 @@ -import { Clutter, GLib, GObject, Gio, Meta } from '../dependencies/gi.js'; +import { Clutter, GLib, GObject, Gio, Meta, Mtk } from '../dependencies/gi.js'; import { Main, WindowManager } from '../dependencies/shell.js'; import { WINDOW_ANIMATION_TIME } from '../dependencies/unexported/windowManager.js'; @@ -862,7 +862,7 @@ class TilePreview extends WindowManager.TilePreview { const monitor = Main.layoutManager.monitors[monitorIndex]; if (!this._showing || changeMonitor) { - const monitorRect = new Meta.Rectangle({ + const monitorRect = new Mtk.Rectangle({ x: monitor.x, y: monitor.y, width: monitor.width, diff --git a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js index 6ed7a79..730ee8a 100644 --- a/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js +++ b/tiling-assistant@leleat-on-github/src/extension/tilingWindowManager.js @@ -1,4 +1,4 @@ -import { Clutter, GLib, GObject, Meta, Shell } from '../dependencies/gi.js'; +import { Clutter, GLib, GObject, Meta, Mtk, Shell } from '../dependencies/gi.js'; import { Main } from '../dependencies/shell.js'; import { getWindows } from '../dependencies/unexported/altTab.js'; @@ -680,7 +680,7 @@ export class TilingWindowManager { // Orientation doesn't matter here since we are always comparing sides // of the same orientation. So just make the side always horizontal. - const makeSide = (startPoint, endPoint) => new Meta.Rectangle({ + const makeSide = (startPoint, endPoint) => new Mtk.Rectangle({ x: startPoint, width: endPoint - startPoint, height: 1 diff --git a/tiling-assistant@leleat-on-github/src/extension/utility.js b/tiling-assistant@leleat-on-github/src/extension/utility.js index 8ada3c6..2de6665 100644 --- a/tiling-assistant@leleat-on-github/src/extension/utility.js +++ b/tiling-assistant@leleat-on-github/src/extension/utility.js @@ -1,4 +1,4 @@ -import { Clutter, Gio, GLib, Meta, St } from '../dependencies/gi.js'; +import { Clutter, Gio, GLib, Mtk, St } from '../dependencies/gi.js'; import { Main } from '../dependencies/shell.js'; import { Direction, Orientation, Settings } from '../common.js'; @@ -263,15 +263,15 @@ export class Util { } /** - * Wrapper for Meta.Rectangle to add some more functions. + * Wrapper for Mtk.Rectangle to add some more functions. */ export class Rect { /** - * @param {...any} params No parameters, 1 Meta.Rectangle or the x, y, + * @param {...any} params No parameters, 1 Mtk.Rectangle or the x, y, * width and height values should be passed to the constructor. */ constructor(...params) { - this._rect = new Meta.Rectangle(); + this._rect = new Mtk.Rectangle(); switch (params.length) { case 0: @@ -386,7 +386,7 @@ export class Rect { * @returns {boolean} */ containsRect(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.contains_rect(rect); } @@ -402,7 +402,7 @@ export class Rect { * @returns {boolean} */ couldFitRect(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.could_fit_rect(rect); } @@ -411,7 +411,7 @@ export class Rect { * @returns {boolean} */ equal(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.equal(rect); } @@ -544,7 +544,7 @@ export class Rect { * @returns {boolean} */ horizOverlap(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.horiz_overlap(rect); } @@ -553,7 +553,7 @@ export class Rect { * @returns {[boolean, Rect]} */ intersect(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; const [ok, intersection] = this._rect.intersect(rect); return [ok, new Rect(intersection)]; } @@ -585,7 +585,7 @@ export class Rect { * @returns {Rect[]} an array of Rects. It contains 0 - 4 rects. */ _minusRect(rect) { - rect = rect instanceof Meta.Rectangle ? new Rect(rect) : rect; + rect = rect instanceof Mtk.Rectangle ? new Rect(rect) : rect; if (rect.containsRect(this)) return []; @@ -657,7 +657,7 @@ export class Rect { * @returns {boolean} */ overlap(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.overlap(rect); } @@ -672,7 +672,7 @@ export class Rect { * @returns {Rect} a reference to this. */ tryAlignWith(rect, margin = 4) { - rect = rect instanceof Meta.Rectangle ? new Rect(rect) : rect; + rect = rect instanceof Mtk.Rectangle ? new Rect(rect) : rect; const equalApprox = (value1, value2) => Math.abs(value1 - value2) <= margin; if (equalApprox(rect.x, this.x)) @@ -703,7 +703,7 @@ export class Rect { * @returns {Rect} */ union(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return new Rect(this._rect.union(rect)); } @@ -712,7 +712,7 @@ export class Rect { * @returns {boolean} */ vertOverlap(rect) { - rect = rect instanceof Meta.Rectangle ? rect : rect.meta; + rect = rect instanceof Mtk.Rectangle ? rect : rect.meta; return this._rect.vert_overlap(rect); } From 7fd82a53154dad1af3271f3dd5375886a2c16446 Mon Sep 17 00:00:00 2001 From: Leleat Date: Sun, 17 Sep 2023 19:50:56 +0200 Subject: [PATCH 5/5] README: Update support table --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f066230..bbc1d79 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ Here is a table showing the GNOME Shell releases and the latest extension versio | GNOME Shell | Tiling Assistant | |:-------------:|:-----------:| -| 43 | 39 | +| 45 | 44 | +| 44 | 43 | +| 43 | 43 | | 42 | 36 | | 41 | 32 | | 40 | 32 |