🎉 Implement profile selection
This commit is contained in:
+77
-30
@@ -32,6 +32,7 @@ const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||
const utils = Me.imports.src.utils;
|
||||
const ProfileManager = Me.imports.src.ProfileManager.ProfileManager;
|
||||
const WindowPicker = Me.imports.src.WindowPicker.WindowPicker;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This extensions modifies the window-close and window-open animations with all kinds //
|
||||
@@ -90,6 +91,13 @@ class Extension {
|
||||
|
||||
this._loadProfiles();
|
||||
|
||||
// This is used to get the desktop's color scheme.
|
||||
this._shellSettings = new Gio.Settings({schema: 'org.gnome.desktop.interface'});
|
||||
|
||||
// Enable the window-picking D-Bus API for the preferences dialog.
|
||||
this._windowPicker = new WindowPicker();
|
||||
this._windowPicker.export();
|
||||
|
||||
// We will use extensionThis to refer to the extension inside the patched methods.
|
||||
const extensionThis = this;
|
||||
|
||||
@@ -348,6 +356,9 @@ class Extension {
|
||||
// Unregister our resources.
|
||||
Gio.resources_unregister(this._resources);
|
||||
|
||||
// Disable the window-picking D-Bus API.
|
||||
this._windowPicker.unexport();
|
||||
|
||||
// Restore the original window-open and window-close animations.
|
||||
global.window_manager.disconnect(this._destroyConnection);
|
||||
global.display.disconnect(this._windowCreatedConnection);
|
||||
@@ -373,41 +384,50 @@ class Extension {
|
||||
|
||||
// ----------------------------------------------------------------------- private stuff
|
||||
|
||||
// This loads all effect profiles and assigns a priority to each profile. Whenever a
|
||||
// window is opened or closed, the matching effect profile with the highest priority
|
||||
// will be chosen.
|
||||
// This method is called whenever the currently edited profile in the/ preferences
|
||||
// dialog changes. This is most likely a bit too often, but it will also happen whenever
|
||||
// a new profile is created and whenever an old profile is deleted.
|
||||
_loadProfiles() {
|
||||
|
||||
// Get all currently available profiles.
|
||||
const profileManager = new ProfileManager();
|
||||
this._profiles = profileManager.getProfiles();
|
||||
|
||||
utils.debug('load profiles!');
|
||||
|
||||
// Whenever the properties of a profile is changed in the settings, we may have to
|
||||
// resort all profiles according to their priority.
|
||||
const updatePriority = (p) => {
|
||||
utils.debug('sort profiles!');
|
||||
p.priority = profileManager.getProfilePriority(p.settings);
|
||||
this._profiles.sort((a, b) => b.priority - a.priority);
|
||||
};
|
||||
|
||||
this._profiles = profileManager.getProfiles();
|
||||
// For each profile, assign an initial priority and update the priority whenever a
|
||||
// related setting changes.
|
||||
this._profiles.forEach(p => {
|
||||
p.priority = profileManager.getProfilePriority(p.settings);
|
||||
p.settings.connect('changed::profile-app', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-animation-type', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-window-type', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-desktop-style', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-color-scheme', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-power-mode', () => updatePriority(p));
|
||||
p.settings.connect('changed::profile-power-profile', () => updatePriority(p));
|
||||
|
||||
p.priority = profileManager.getProfilePriority(p.settings);
|
||||
});
|
||||
|
||||
utils.debug('sort profiles!');
|
||||
// Sort all profiles initially according to their initial priority.
|
||||
this._profiles.sort((a, b) => b.priority - a.priority);
|
||||
}
|
||||
|
||||
// This method adds one of the configured effects to the given actor. If forOpening is
|
||||
// set to true, a effect from the enabled window-open animations is chosen, else an
|
||||
// enabled window-close animation is used. This will also tweak the transitions of the
|
||||
// given actor (e.g. scale it up if required).
|
||||
// This method adds one of the configured effects to the given actor. First, a profile
|
||||
// matching the current circumstances is chosen. Then a random effect from its enabled
|
||||
// effects will be selected. This will also tweak the transitions of the given actor
|
||||
// (e.g. scale it up if required).
|
||||
_setupEffect(actor, forOpening) {
|
||||
// In case we return early, make sure that the animation times are reset properly.
|
||||
this._fixAnimationTimes(forOpening, null);
|
||||
|
||||
// Only add effects to normal windows and dialog windows.
|
||||
// For now, we only add effects to normal windows and dialog windows.
|
||||
const isNormalWindow = actor.meta_window.window_type == Meta.WindowType.NORMAL;
|
||||
const isDialogWindow =
|
||||
actor.meta_window.window_type == Meta.WindowType.MODAL_DIALOG ||
|
||||
@@ -417,20 +437,6 @@ class Extension {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// TODO!.
|
||||
// We may have to do nothing if running on battery power or if in power-save mode.
|
||||
// let disableOnBattery =
|
||||
// this._settings.get_boolean('disable-on-battery') && this._upowerProxy.OnBattery;
|
||||
// let disableOnPowerSave = this._settings.get_boolean('disable-on-power-save') &&
|
||||
// this._powerProfilesProxy && this._powerProfilesProxy.ActiveProfile ==
|
||||
// 'power-saver';
|
||||
|
||||
// if (!skip! && previewNick == '') {
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
// There is the weird case where an animation is already ongoing. This happens when a
|
||||
// window is closed which has been created before the session was started (e.g. when
|
||||
// GNOME Shell has been restarted in the meantime).
|
||||
@@ -441,7 +447,7 @@ class Extension {
|
||||
oldShader.returnToFactory();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ choose an effect
|
||||
// ----------------------------------------------- choose a profile and then an effect
|
||||
|
||||
// Usually, we use the effect profile with the highest priority which matches the
|
||||
// current circumstances. From this profile, we choose a random effect. However, if an
|
||||
@@ -468,22 +474,62 @@ class Extension {
|
||||
// effect.
|
||||
else {
|
||||
|
||||
// These numbers match the indices in the Gtk.StringLists defined in the UI files
|
||||
// (e.g. resources/ui/adw/prefs.ui).
|
||||
const animationType = forOpening ? 1 : 2;
|
||||
const windowType = isNormalWindow ? 1 : 2;
|
||||
const powerMode = this._upowerProxy.OnBattery ? 1 : 2;
|
||||
|
||||
// Get the first profile whose constraints match the circumstances. The list is
|
||||
// sorted by priority, so we are good to take the first match.
|
||||
profile = this._profiles.find(p => {
|
||||
const profileApp = p.settings.get_string('profile-app');
|
||||
const profileAnimationType = p.settings.get_int('profile-animation-type');
|
||||
const profileWindowType = p.settings.get_int('profile-window-type');
|
||||
const profileDesktopStyle = p.settings.get_int('profile-desktop-style');
|
||||
const profilePowerMode = p.settings.get_int('profile-power-mode');
|
||||
const profileColorScheme = p.settings.get_int('profile-color-scheme');
|
||||
const profilePowerProfile = p.settings.get_int('profile-power-profile');
|
||||
|
||||
return (profileAnimationType == 0 || profileAnimationType == animationType) &&
|
||||
// First we check whether the animation type, window type, and power mode are
|
||||
// matching.
|
||||
let matches =
|
||||
(profileAnimationType == 0 || profileAnimationType == animationType) &&
|
||||
(profileWindowType == 0 || profileWindowType == windowType) &&
|
||||
(profilePowerMode == 0 || profilePowerMode == powerMode);
|
||||
|
||||
// If that was the case, we also check the application name.
|
||||
if (matches && profileApp != '') {
|
||||
const app = actor.meta_window.get_wm_class().toLowerCase();
|
||||
matches = app == profileApp.toLowerCase();
|
||||
}
|
||||
|
||||
// If the profile is still matching, we also check the color scheme.
|
||||
if (matches && profileColorScheme != 0 && utils.shellVersionIsAtLeast(42, 0)) {
|
||||
const colorScheme = this._shellSettings.get_string('color-scheme');
|
||||
matches &= (profileColorScheme == 1 && colorScheme == 'default') ||
|
||||
(profileColorScheme == 2 && colorScheme == 'prefer-dark');
|
||||
}
|
||||
|
||||
// Finally, we may also have to check the power profile.
|
||||
if (matches && profilePowerProfile != 0 && this._powerProfilesProxy) {
|
||||
const powerProfile = this._powerProfilesProxy.ActiveProfile;
|
||||
|
||||
// To understand the numbers, please refer to the indices in the Gtk.StringList
|
||||
// of the profile-power-profile Adw.ComboRow in resources/ui/adw/prefs.ui.
|
||||
if (powerProfile == 'power-saver') {
|
||||
matches &= profilePowerProfile == 1 || profilePowerProfile == 4;
|
||||
} else if (powerProfile == 'balanced') {
|
||||
matches &= profilePowerProfile == 2 || profilePowerProfile == 4 ||
|
||||
profilePowerProfile == 5;
|
||||
} else {
|
||||
matches &= profilePowerProfile == 3 || profilePowerProfile == 5;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
});
|
||||
|
||||
// If we found a matching profile, choose a random effect from it.
|
||||
if (profile) {
|
||||
|
||||
// Create a list of all enabled effects of this profile.
|
||||
@@ -503,6 +549,7 @@ class Extension {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------- tweak actor transitions
|
||||
|
||||
// If we are currently performing integration test, all animations are set to a fixed
|
||||
|
||||
@@ -131,6 +131,29 @@ var PreferencesDialog = class PreferencesDialog {
|
||||
|
||||
this._builder.get_object('profile-power-profile').set_visible(hasPowerProfiles);
|
||||
|
||||
// The global color scheme is only available starting with GNOME Shell 42. We hide the
|
||||
// corresponding settings row on older versions.
|
||||
this._builder.get_object('profile-color-scheme')
|
||||
.set_visible(utils.shellVersionIsAtLeast(42, 0));
|
||||
|
||||
this._builder.get_object('profile-choose-app-button').connect('clicked', () => {
|
||||
Gio.DBus.session.call('org.gnome.Shell',
|
||||
'/org/gnome/shell/extensions/BurnMyWindows',
|
||||
'org.gnome.shell.extensions.BurnMyWindows', 'PickWindow',
|
||||
null, null, Gio.DBusCallFlags.NO_AUTO_START, -1, null, null);
|
||||
});
|
||||
|
||||
this._dbusConnection = Gio.DBus.session.signal_subscribe(
|
||||
'org.gnome.Shell', 'org.gnome.shell.extensions.BurnMyWindows', 'WindowPicked',
|
||||
'/org/gnome/shell/extensions/BurnMyWindows', null, Gio.DBusSignalFlags.NONE,
|
||||
(conn, sender, obj_path, iface, signal, params) => {
|
||||
const val = params.get_child_value(0).get_string()[0];
|
||||
|
||||
if (val != 'window-not-found') {
|
||||
this._builder.get_object('profile-app').text = val;
|
||||
}
|
||||
});
|
||||
|
||||
// Starting with GNOME Shell 42, the settings dialog uses libadwaita. We use this to
|
||||
// create a completely different layout for the settings dialog.
|
||||
if (utils.isADW()) {
|
||||
@@ -479,6 +502,9 @@ var PreferencesDialog = class PreferencesDialog {
|
||||
this._widget.connect('destroy', () => {
|
||||
// Unregister our resources.
|
||||
Gio.resources_unregister(this._resources);
|
||||
|
||||
// Disconnect from the window-picker API.
|
||||
Gio.DBus.session.signal_unsubscribe(this._dbusConnection);
|
||||
});
|
||||
|
||||
// Show the widgets on GTK3.
|
||||
@@ -507,10 +533,16 @@ var PreferencesDialog = class PreferencesDialog {
|
||||
|
||||
// Connects a Gtk.ComboBox (or anything else which has an 'selected' property) to a
|
||||
// settings key. It also binds the corresponding reset button.
|
||||
bindComboRow(settingsKey) {
|
||||
bindComboBox(settingsKey) {
|
||||
this._bind(settingsKey, 'selected');
|
||||
}
|
||||
|
||||
// Connects a Gtk.Entry (or anything else which has an 'text' property) to a
|
||||
// settings key. It also binds the corresponding reset button.
|
||||
bindEntry(settingsKey) {
|
||||
this._bind(settingsKey, 'text');
|
||||
}
|
||||
|
||||
// Connects a Gtk.Adjustment (or anything else which has a 'value' property) to a
|
||||
// settings key. It also binds the corresponding reset button.
|
||||
bindAdjustment(settingsKey) {
|
||||
@@ -591,19 +623,27 @@ var PreferencesDialog = class PreferencesDialog {
|
||||
}
|
||||
|
||||
// Connect all profile options.
|
||||
const setupProfileOption = (settingsKey) => {
|
||||
this.bindComboRow(settingsKey);
|
||||
const updateProfileButtonMenuIfChanged = (settingsKey) => {
|
||||
this._connectProfileSetting('changed::' + settingsKey, () => {
|
||||
this._updateProfileButton();
|
||||
this._updateProfileMenu();
|
||||
});
|
||||
};
|
||||
|
||||
setupProfileOption('profile-animation-type');
|
||||
setupProfileOption('profile-window-type');
|
||||
setupProfileOption('profile-desktop-style');
|
||||
setupProfileOption('profile-power-mode');
|
||||
setupProfileOption('profile-power-profile');
|
||||
updateProfileButtonMenuIfChanged('profile-app');
|
||||
updateProfileButtonMenuIfChanged('profile-animation-type');
|
||||
updateProfileButtonMenuIfChanged('profile-window-type');
|
||||
updateProfileButtonMenuIfChanged('profile-color-scheme');
|
||||
updateProfileButtonMenuIfChanged('profile-power-mode');
|
||||
updateProfileButtonMenuIfChanged('profile-power-profile');
|
||||
|
||||
this.bindEntry('profile-app');
|
||||
this.bindComboBox('profile-animation-type');
|
||||
this.bindComboBox('profile-window-type');
|
||||
this.bindComboBox('profile-color-scheme');
|
||||
this.bindComboBox('profile-power-mode');
|
||||
this.bindComboBox('profile-power-profile');
|
||||
this.bindSwitch('profile-high-priority');
|
||||
|
||||
// Connect all effect settings.
|
||||
this._ALL_EFFECTS.forEach(effect => {
|
||||
|
||||
@@ -70,12 +70,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Edit Effect Profile</property>
|
||||
<property name="description" translatable="yes">Here you can specify under which circumstances the current effect settings should be applied.</property>
|
||||
<property name="title" translatable="yes">Effect Profile Constraints</property>
|
||||
<property name="description" translatable="yes">Define when the current effect settings should be used.</property>
|
||||
|
||||
<child>
|
||||
<object class="AdwEntryRow" id="profile-app">
|
||||
<property name="title" translatable="yes">Choose an Application</property>
|
||||
<property name="title" translatable="yes">Application Name</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="profile-choose-app-button">
|
||||
<property name="icon-name">bmw-select-app-symbolic</property>
|
||||
@@ -116,14 +116,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="AdwComboRow" id="profile-desktop-style">
|
||||
<property name="title" translatable="yes">Desktop Style</property>
|
||||
<object class="AdwComboRow" id="profile-color-scheme">
|
||||
<property name="title" translatable="yes">Color Scheme</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item translatable="yes">Any</item>
|
||||
<item translatable="yes">Bright Mode</item>
|
||||
<item translatable="yes">Dark Mode</item>
|
||||
<item translatable="yes">Default Color Scheme</item>
|
||||
<item translatable="yes">Dark Color Scheme</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
@@ -166,6 +166,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Advanced Profile Options</property>
|
||||
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">High Priority</property>
|
||||
<property name="subtitle" translatable="yes">Force using this profile even if it has less constraints than another matching profile.</property>
|
||||
<property name="subtitle-lines">1</property>
|
||||
<property name="activatable-widget">profile-high-priority</property>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="profile-high-priority">
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="action-name">prefs.profile-delete</property>
|
||||
|
||||
@@ -13,6 +13,12 @@ SPDX-License-Identifier: CC0-1.0
|
||||
<!-- Profile Options -->
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<key name="profile-app" type="s">
|
||||
<default>""</default>
|
||||
<summary>Profile App</summary>
|
||||
<description>The application name for which the current effect settings should be used.</description>
|
||||
</key>
|
||||
|
||||
<key name="profile-animation-type" type="i">
|
||||
<default>0</default>
|
||||
<range min="0" max="2" />
|
||||
@@ -27,11 +33,11 @@ SPDX-License-Identifier: CC0-1.0
|
||||
<description>The window types for which the current effect settings should be used.</description>
|
||||
</key>
|
||||
|
||||
<key name="profile-desktop-style" type="i">
|
||||
<key name="profile-color-scheme" type="i">
|
||||
<default>0</default>
|
||||
<range min="0" max="2" />
|
||||
<summary>Profile Desktop Style</summary>
|
||||
<description>The desktop styles for which the current effect settings should be used.</description>
|
||||
<summary>Profile Color Scheme</summary>
|
||||
<description>The desktop color schemes for which the current effect settings should be used.</description>
|
||||
</key>
|
||||
|
||||
<key name="profile-power-mode" type="i">
|
||||
@@ -48,6 +54,12 @@ SPDX-License-Identifier: CC0-1.0
|
||||
<description>The power profiles in which the current effect settings should be used.</description>
|
||||
</key>
|
||||
|
||||
<key name="profile-high-priority" type="b">
|
||||
<default>false</default>
|
||||
<summary>Profile High Priority</summary>
|
||||
<description>Force using this profile even if it has less constraints than another matching profile.</description>
|
||||
</key>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
<!-- Apparition Effect Options -->
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
+17
-4
@@ -32,7 +32,7 @@ const utils = Me.imports.src.utils;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var ProfileManager = class {
|
||||
// ------------------------------------------------------------ constructor / destructor
|
||||
// ------------------------------------------------------------------------- constructor
|
||||
|
||||
constructor() {
|
||||
this._makeProfilesDir();
|
||||
@@ -128,12 +128,17 @@ var ProfileManager = class {
|
||||
getProfilePriority(settings) {
|
||||
let priority = 0;
|
||||
|
||||
// Each setting which is not set to its default value increases the priority by one.
|
||||
if (settings.get_string('profile-app') != '') ++priority;
|
||||
if (settings.get_int('profile-animation-type') > 0) ++priority;
|
||||
if (settings.get_int('profile-window-type') > 0) ++priority;
|
||||
if (settings.get_int('profile-desktop-style') > 0) ++priority;
|
||||
if (settings.get_int('profile-color-scheme') > 0) ++priority;
|
||||
if (settings.get_int('profile-power-mode') > 0) ++priority;
|
||||
if (settings.get_int('profile-power-profile') > 0) ++priority;
|
||||
|
||||
// Increase the priority significantly for high-priority profiles.
|
||||
if (settings.get_boolean('profile-high-priority')) priority += 100;
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
@@ -142,6 +147,14 @@ var ProfileManager = class {
|
||||
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) {
|
||||
@@ -154,8 +167,8 @@ var ProfileManager = class {
|
||||
_('Closing Windows')]);
|
||||
addComponent('profile-window-type', [_('Normal Windows'),
|
||||
_('Dialog Windows')]);
|
||||
addComponent('profile-desktop-style', [_('Bright Mode'),
|
||||
_('Dark Mode')]);
|
||||
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'),
|
||||
|
||||
Reference in New Issue
Block a user