🎉 Implement profile selection

This commit is contained in:
Simon Schneegans
2023-01-30 13:57:33 +01:00
parent 35860c7b2d
commit 99b90377c5
5 changed files with 185 additions and 52 deletions
+77 -30
View File
@@ -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
+48 -8
View File
@@ -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 => {
+28 -7
View File
@@ -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
View File
@@ -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'),