diff --git a/extension.js b/extension.js index f385b79..b32f939 100644 --- a/extension.js +++ b/extension.js @@ -44,19 +44,11 @@ import TVEffect from './src/effects/TVEffect.js'; import TVGlitch from './src/effects/TVGlitch.js'; import Wisps from './src/effects/Wisps.js'; -const Main = imports.ui.main; -const Workspace = imports.ui.workspace.Workspace; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import {Workspace} from 'resource:///org/gnome/shell/ui/workspace.js'; +import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js'; +import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; -// The WindowPreview class is only available on GNOME Shell 3.38+; -let WindowPreview = null; -try { - WindowPreview = imports.ui.windowPreview.WindowPreview; -} catch (error) { - // Nothing to be done, we are on GNOME Shell 3.36. -} - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = imports.misc.extensionUtils.getCurrentExtension(); ////////////////////////////////////////////////////////////////////////////////////////// // This extensions modifies the window-close and window-open animations with all kinds // @@ -67,7 +59,7 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); // to get this working. For more details, read the other comments in this file... // ////////////////////////////////////////////////////////////////////////////////////////// -export default class Extension { +export default class BurnMyWindows extends Extension { // ------------------------------------------------------------------------ public stuff @@ -101,22 +93,23 @@ export default class Extension { ]; // Load all of our resources. - this._resources = Gio.Resource.load(Me.path + '/resources/burn-my-windows.gresource'); + this._resources = + Gio.Resource.load(this.path + '/resources/burn-my-windows.gresource'); Gio.resources_register(this._resources); // Store a reference to the settings object. - this._settings = ExtensionUtils.getSettings(); + this._settings = this.getSettings(); // Now we check whether the extension settings need to be migrated from a previous // version. If this is the case, we defer the profile loading until this is finished. const lastVersion = this._settings.get_int('last-extension-version'); - if (lastVersion < Me.metadata.version) { + if (lastVersion < this.metadata.version) { if (lastVersion <= 26) { // If the profile migration fails for some reason, the callback will create a // default profile instead. fromVersion26().finally(() => { this._loadProfiles(); - this._settings.set_int('last-extension-version', Me.metadata.version); + this._settings.set_int('last-extension-version', this.metadata.version); }); } else { this._loadProfiles(); @@ -324,53 +317,50 @@ export default class Extension { }; // With the code below, we hide the window-overlay (icon, label, close button) in the - // overview once the close-animation is running. As the WindowPreview class is only - // available on GNOME 3.38 and beyond, we cannot hide the overlay on GNOME 3.36. - if (WindowPreview) { + // overview once the close-animation is running. - // We will monkey-patch these methods. - this._origDeleteAll = WindowPreview.prototype._deleteAll; - this._origRestack = WindowPreview.prototype._restack; - this._origInit = WindowPreview.prototype._init; + // We will monkey-patch these methods. + this._origDeleteAll = WindowPreview.prototype._deleteAll; + this._origRestack = WindowPreview.prototype._restack; + this._origInit = WindowPreview.prototype._init; - // Whenever a WindowPreview is created, we connect to the referenced Meta.Window's - // 'unmanaged' signal to hide the overlay. - WindowPreview.prototype._init = function(...params) { - extensionThis._origInit.apply(this, params); + // Whenever a WindowPreview is created, we connect to the referenced Meta.Window's + // 'unmanaged' signal to hide the overlay. + WindowPreview.prototype._init = function(...params) { + extensionThis._origInit.apply(this, params); - // Hide the window's icon, name, and close button. - const connectionID = this.metaWindow.connect('unmanaged', () => { - if (this.window_container) { - this.overlayEnabled = false; - this._icon.visible = false; - } - }); - - // Make sure to not call the callback above if the Meta.Window was not unmanaged - // before leaving the overview. - this.connect('destroy', () => { - this.metaWindow.disconnect(connectionID); - }); - }; - - // The _deleteAll is called when the user clicks the X in the overview. We should - // not attempt to close windows twice. Due to the animation in the overview, the - // close button can be clicked twice which normally would lead to a crash. - WindowPreview.prototype._deleteAll = function() { - if (!this._closeRequested) { - extensionThis._origDeleteAll.apply(this); + // Hide the window's icon, name, and close button. + const connectionID = this.metaWindow.connect('unmanaged', () => { + if (this.window_container) { + this.overlayEnabled = false; + this._icon.visible = false; } - }; + }); - // This is required, else WindowPreview's _restack() which is called by the - // "this.overlayEnabled = false", sometimes tries to access an already delete - // WindowPreview. - WindowPreview.prototype._restack = function() { - if (!this._closeRequested) { - extensionThis._origRestack.apply(this); - } - }; - } + // Make sure to not call the callback above if the Meta.Window was not unmanaged + // before leaving the overview. + this.connect('destroy', () => { + this.metaWindow.disconnect(connectionID); + }); + }; + + // The _deleteAll is called when the user clicks the X in the overview. We should + // not attempt to close windows twice. Due to the animation in the overview, the + // close button can be clicked twice which normally would lead to a crash. + WindowPreview.prototype._deleteAll = function() { + if (!this._closeRequested) { + extensionThis._origDeleteAll.apply(this); + } + }; + + // This is required, else WindowPreview's _restack() which is called by the + // "this.overlayEnabled = false", sometimes tries to access an already delete + // WindowPreview. + WindowPreview.prototype._restack = function() { + if (!this._closeRequested) { + extensionThis._origRestack.apply(this); + } + }; } // This function could be called after the extension is uninstalled, disabled in GNOME @@ -395,11 +385,9 @@ export default class Extension { Main.wm._shouldAnimateActor = this._origShouldAnimateActor; Main.wm._waitForOverviewToHide = this._origWaitForOverviewToHide; - if (WindowPreview) { - WindowPreview.prototype._deleteAll = this._origDeleteAll; - WindowPreview.prototype._restack = this._origRestack; - WindowPreview.prototype._init = this._origInit; - } + WindowPreview.prototype._deleteAll = this._origDeleteAll; + WindowPreview.prototype._restack = this._origRestack; + WindowPreview.prototype._init = this._origInit; this._settings = null; } @@ -415,7 +403,7 @@ export default class Extension { _loadProfiles() { // Get all currently available profiles. - const profileManager = new ProfileManager(); + const profileManager = new ProfileManager(this.metadata); this._profiles = profileManager.getProfiles(); // Whenever the properties of a profile is changed in the settings, we may have to @@ -666,8 +654,3 @@ export default class Extension { return shader == null; } } - -// This function is called once when the extension is loaded, not enabled. -function init() { - return new Extension(); -} diff --git a/prefs.js b/prefs.js index ecb9f64..d47c0cd 100644 --- a/prefs.js +++ b/prefs.js @@ -45,10 +45,7 @@ import TVEffect from './src/effects/TVEffect.js'; import TVGlitch from './src/effects/TVGlitch.js'; import Wisps from './src/effects/Wisps.js'; -const _ = imports.gettext.domain('burn-my-windows').gettext; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = imports.misc.extensionUtils.getCurrentExtension(); +import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; ////////////////////////////////////////////////////////////////////////////////////////// // The preferences dialog is organized in pages, each of which is loaded from a // @@ -56,15 +53,10 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); // from the respective effects. // ////////////////////////////////////////////////////////////////////////////////////////// -export default class ExtensionPreferences { +export default class BurnMyWindowsPreferences extends ExtensionPreferences { // ------------------------------------------------------------ constructor / destructor - // This is used for setting up the translations. - constructor() { - ExtensionUtils.initTranslations(); - } - fillPreferencesWindow(window) { window.set_default_size(650, 750); @@ -94,7 +86,8 @@ export default class ExtensionPreferences { ]; // Load all of our resources. - this._resources = Gio.Resource.load(Me.path + '/resources/burn-my-windows.gresource'); + this._resources = + Gio.Resource.load(this.path + '/resources/burn-my-windows.gresource'); Gio.resources_register(this._resources); // Load the CSS file for the settings dialog. If using libadwaita, we do not need an @@ -125,10 +118,10 @@ export default class ExtensionPreferences { this._builder.add_from_resource(`/ui/${getUIDir()}/prefs.ui`); // Store a reference to the general settings object. - this._settings = ExtensionUtils.getSettings(); + this._settings = this.getSettings(); // This tracks all available effect profiles. - this._profileManager = new ProfileManager(); + this._profileManager = new ProfileManager(this.metadata); // This is used to track all connections to properties of the current effect profile. // Whenever the current effect profile changes, all connections are disconnected. @@ -396,7 +389,7 @@ export default class ExtensionPreferences { const window = isGTK4() ? widget.get_root() : widget.get_toplevel(); // Show the version number in the title bar. - window.set_title(`Burn-My-Windows ${Me.metadata.version}`); + window.set_title(`Burn-My-Windows ${this.metadata.version}`); this._loadActiveProfile(); this._updateProfileMenu(); @@ -404,7 +397,7 @@ export default class ExtensionPreferences { // Show an Adw.Toast or a Gtk.InfoBar whenever Burn-My-Windows was updated. We use a // small timeout so that it is not shown instantaneously. const lastVersion = this._settings.get_int('last-prefs-version'); - if (lastVersion < Me.metadata.version) { + if (lastVersion < this.metadata.version) { this._showUpdateInfoTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 2000, () => { this._showUpdateInfoTimeout = 0; @@ -415,15 +408,15 @@ export default class ExtensionPreferences { toast.set_action_name('prefs.changelog'); toast.set_timeout(0); - toast.connect( - 'dismissed', - () => this._settings.set_int('last-prefs-version', Me.metadata.version)); + toast.connect('dismissed', + () => this._settings.set_int('last-prefs-version', + this.metadata.version)); window.add_toast(toast); } else { const infoBar = this._builder.get_object('update-info'); infoBar.connect('response', i => { - this._settings.set_int('last-prefs-version', Me.metadata.version); + this._settings.set_int('last-prefs-version', this.metadata.version); i.set_revealed(false); }); infoBar.set_revealed(true); @@ -555,7 +548,7 @@ GitHub: https://github.com/spon null, 'https://github.com/Schneegans/Burn-My-Windows/blob/main/docs/changelog.md', Gdk.CURRENT_TIME); - this._settings.set_int('last-prefs-version', Me.metadata.version) + this._settings.set_int('last-prefs-version', this.metadata.version) }); group.add_action(changelogAction); @@ -588,7 +581,7 @@ GitHub: https://github.com/spon dialog = new Adw.AboutWindow({transient_for: window, modal: true}); dialog.set_application_icon('burn-my-windows-symbolic'); dialog.set_application_name('Burn-My-Windows'); - dialog.set_version(`${Me.metadata.version}`); + dialog.set_version(`${this.metadata.version}`); dialog.set_developer_name('Simon Schneegans'); dialog.set_issue_url('https://github.com/Schneegans/Burn-My-Windows/issues'); if (sponsors.gold.length > 0) { @@ -621,7 +614,7 @@ GitHub: https://github.com/spon dialog = new Gtk.AboutDialog({transient_for: window, modal: true}); dialog.set_logo_icon_name('burn-my-windows-symbolic'); - dialog.set_program_name(`Burn-My-Windows ${Me.metadata.version}`); + dialog.set_program_name(`Burn-My-Windows ${this.metadata.version}`); dialog.set_authors(['Simon Schneegans']); if (sponsors.gold.length > 0) { dialog.add_credit_section(_('Gold Sponsors'), @@ -904,7 +897,7 @@ GitHub: https://github.com/spon // configuration option of the profile is changed. _updateProfileButton() { this._builder.get_object('choose-profile-button').label = - this._profileManager.getProfileName(this.getProfileSettings()); + this._getProfileName(this.getProfileSettings()); } // This updates the list of available profiles in the dropdown of the profile-selection @@ -918,7 +911,7 @@ GitHub: https://github.com/spon // Then add an entry for each available profile. this._profileManager.getProfiles().forEach(profile => { - const label = this._profileManager.getProfileName(profile.settings); + const label = this._getProfileName(profile.settings); const item = Gio.MenuItem.new(label, 'prefs.active-profile'); item.set_action_and_target_value('prefs.active-profile', GLib.Variant.new_string(profile.path)); @@ -926,6 +919,58 @@ GitHub: https://github.com/spon }); } + // Profiles are named according to their configuration. This method returns a localized + // string describing the settings of the profile. + _getProfileName(settings) { + let items = []; + + // If an app is configured, use it as first component for the profile's name. + const app = settings.get_string('profile-app'); + if (app != '') { + items.push(app); + } + + // Now add components to the name for each non-default profile option. Make sure that + // these are the same strings as used in the UI files! + const addComponent = (settingsKey, options) => { + const option = settings.get_int(settingsKey); + if (option > 0) { + items.push(options[option - 1]); + } + }; + + // clang-format off + addComponent('profile-animation-type', [_('Opening Windows'), + _('Closing Windows')]); + addComponent('profile-window-type', [_('Normal Windows'), + _('Dialog Windows')]); + addComponent('profile-color-scheme', [_('Default Color Scheme'), + _('Dark Color Scheme')]); + addComponent('profile-power-mode', [_('On Battery'), + _('Plugged In')]); + addComponent('profile-power-profile', [_('Power-Saver Mode'), + _('Balanced Mode'), + _('Performance Mode'), + _('Power Saver or Balanced'), + _('Balanced or Performance')]); + // clang-format on + + // If the profile is a high-priority profile, also add this.. + if (settings.get_boolean('profile-high-priority')) { + items.push(_('High Priority')); + } + + let name = ''; + + if (items.length == 0) { + name = _('Standard Profile'); + } else { + name = items.join(' · '); + } + + return name; + } + // This wrapper connects a given callback to a profile settings key. Use this instead of // the direct connect() call on the profile settings to make sure that the callback is // disconnected when the active profile changes. diff --git a/src/ProfileManager.js b/src/ProfileManager.js index 1d43014..38201d1 100644 --- a/src/ProfileManager.js +++ b/src/ProfileManager.js @@ -17,10 +17,6 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; -const _ = imports.gettext.domain('burn-my-windows').gettext; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); - ////////////////////////////////////////////////////////////////////////////////////////// // Burn-My-Windows allows applying different sets of effect settings under specific // // circumstances. These settings are called "effect profiles". The ProfileManager // @@ -33,7 +29,9 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); export class ProfileManager { // ------------------------------------------------------------------------- constructor - constructor() { + constructor(metadata) { + this._metadata = metadata; + this._makeProfilesDir(); this.reloadProfiles(); } @@ -150,58 +148,6 @@ export class ProfileManager { return priority; } - // Profiles are named according to their configuration. This method returns a localized - // string describing the settings of the profile. - getProfileName(settings) { - let items = []; - - // If an app is configured, use it as first component for the profile's name. - const app = settings.get_string('profile-app'); - if (app != '') { - items.push(app); - } - - // Now add components to the name for each non-default profile option. Make sure that - // these are the same strings as used in the UI files! - const addComponent = (settingsKey, options) => { - const option = settings.get_int(settingsKey); - if (option > 0) { - items.push(options[option - 1]); - } - }; - - // clang-format off - addComponent('profile-animation-type', [_('Opening Windows'), - _('Closing Windows')]); - addComponent('profile-window-type', [_('Normal Windows'), - _('Dialog Windows')]); - addComponent('profile-color-scheme', [_('Default Color Scheme'), - _('Dark Color Scheme')]); - addComponent('profile-power-mode', [_('On Battery'), - _('Plugged In')]); - addComponent('profile-power-profile', [_('Power-Saver Mode'), - _('Balanced Mode'), - _('Performance Mode'), - _('Power Saver or Balanced'), - _('Balanced or Performance')]); - // clang-format on - - // If the profile is a high-priority profile, also add this.. - if (settings.get_boolean('profile-high-priority')) { - items.push(_('High Priority')); - } - - let name = ''; - - if (items.length == 0) { - name = _('Standard Profile'); - } else { - name = items.join(' · '); - } - - return name; - } - // ----------------------------------------------------------------------- private stuff // Creates the directory ~/.config/burn-my-windows/profiles if it does not yet exist. @@ -220,7 +166,7 @@ export class ProfileManager { // Expect USER extensions to have a schemas/ subfolder, otherwise assume a // SYSTEM extension that has been installed in the same prefix as the shell - const schemaDir = Me.dir.get_child('schemas'); + const schemaDir = this._metadata.dir.get_child('schemas'); let source; if (schemaDir.query_exists(null)) { source = Gio.SettingsSchemaSource.new_from_directory( diff --git a/src/WindowPicker.js b/src/WindowPicker.js index 0cd2945..157ef28 100644 --- a/src/WindowPicker.js +++ b/src/WindowPicker.js @@ -18,8 +18,8 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; -const Main = imports.ui.main; -const LookingGlass = imports.ui.lookingGlass; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js'; import {getStringResource} from './utils.js'; diff --git a/src/utils.js b/src/utils.js index 6999fa9..1c5b0c6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -33,12 +33,9 @@ function toNumericVersion(x) { return x; } -const Me = imports.misc.extensionUtils.getCurrentExtension(); const Config = imports.misc.config; const [GS_MAJOR, GS_MINOR] = Config.PACKAGE_VERSION.split('.').map(toNumericVersion); -const _ = imports.gettext.domain('burn-my-windows').gettext; - // This method can be used to write a message to GNOME Shell's log. This is enhances // the standard log() functionality by prepending the extension's name and the location // where the message was logged. As the extensions name is part of the location, you @@ -54,9 +51,10 @@ export function debug(message) { // Find the index of the extension directory (e.g. particles@schneegans.github.com) in // the stack entry. We do not want to print the entire absolute file path. - const extensionRoot = stack[0].indexOf(Me.metadata.uuid); + // const extensionRoot = stack[0].indexOf(Extension.uuid); - log('[' + stack[0].slice(extensionRoot) + '] ' + message); + log('[' + stack[0] + '] ' + message); + // log('[' + stack[0].slice(extensionRoot) + '] ' + message); } // This method simply returns true if we are currently using GTK4.