diff --git a/Makefile b/Makefile index dd494d8..fabecfa 100644 --- a/Makefile +++ b/Makefile @@ -17,5 +17,8 @@ install: build dev: install ifeq ($(XDG_SESSION_TYPE),x11) - pkill gnome-shell & + busctl --user call org.gnome.Shell /org/gnome/Shell org.gnome.Shell Eval s 'Meta.restart("Restarting…", global.context)' endif + +log: + journalctl /usr/bin/gnome-shell -f -q --output cat | grep '\[EXTENSION QSTweaks\] ' diff --git a/extension.js b/extension.js index b8d8ed1..d199c98 100644 --- a/extension.js +++ b/extension.js @@ -2,52 +2,79 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() const Features = Me.imports.features const { logger } = Me.imports.libs.utility +const { QuickSettingsGrid } = Me.imports.libs.gnome const { GLib } = imports.gi class Extension { - constructor() { - logger("Init") - this.features = [ - new Features.dndQuickToggle.dndQuickToggleFeature(), - new Features.notifications.notificationsFeature(), - new Features.volumeMixer.volumeMixerFeature(), - new Features.dateMenu.dateMenuFeature(), - new Features.buttonRemover.buttonRemoverFeature(), - new Features.inputOutput.inputOutputFeature() - ] - } + constructor() {} disable() { logger("Unloading ...") - - if (this.timeout) { - GLib.Source.remove(this.timeout) - this.timeout = null - } + let start = +Date.now() + + // unload menu open tracker + QuickSettingsGrid.disconnect(this.menuOpenTracker) + QuickSettingsGrid.disconnect(this.menuItemAddedTracker) + + // unload features for (const feature of this.features) { logger(`Unload feature '${feature.constructor.name}'`) feature.unload() feature.settings = null } - - logger("Diabled") + + // Null out + this.menuItemAddedTracker = this.features = this.updating = this.menuOpenTracker = null + + logger("Diabled. " + (+new Date() - start) + "ms taken") } enable() { logger("Loading ...") - + let start = +Date.now() + + // load modules + this.features = [ + new Features.dndQuickToggle.dndQuickToggleFeature(), + new Features.unsafeQuickToggle.unsafeQuickToggleFeature(), + new Features.notifications.notificationsFeature(), + new Features.volumeMixer.volumeMixerFeature(), + new Features.dateMenu.dateMenuFeature(), + new Features.buttonRemover.buttonRemoverFeature(), + new Features.inputOutput.inputOutputFeature(), + ] + + // load settings let settings = ExtensionUtils.getSettings(Me.metadata['settings-schema']) ExtensionUtils.initTranslations(Me.metadata['gettext-domain']) - - // Add timeout for waitting other extensions such as GSConnect - // This is necessary behavior due to ordering qs panel - this.timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 400, () => { + + // load features + for (const feature of this.features) { + logger(`Loading feature '${feature.constructor.name}'`) + feature.settings = settings + feature.load() + } + + // load menu open tracker + this.updating = false + this.menuOpenTracker = QuickSettingsGrid.connect("notify::mapped",()=>{ + if (!QuickSettingsGrid.mapped) return + this.updating = true for (const feature of this.features) { - logger(`Loading feature '${feature.constructor.name}'`) - feature.settings = settings - feature.load() + if (feature.onMenuOpen) feature.onMenuOpen() } - logger("Loaded") - return GLib.SOURCE_REMOVE + this.updating = false }) + + // load menu item added tracker + this.menuItemAddedTracker = QuickSettingsGrid.connect("actor-added",()=>{ + if (this.updating) return + this.updating = true + for (const feature of this.features) { + if (feature.onMenuItemAdded) feature.onMenuItemAdded() + } + this.updating = false + }) + + logger("Loaded. " + (+Date.now() - start) + "ms taken") } } diff --git a/features/buttonRemover.js b/features/buttonRemover.js index b434b7d..417a766 100644 --- a/features/buttonRemover.js +++ b/features/buttonRemover.js @@ -1,11 +1,9 @@ // forked from https://github.com/qwreey75/gnome-quick-settings-button-remover -// ! NEED TO REWRITE - const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() -const featureReloader = Me.imports.libs.featureReloader +const { featureReloader } = Me.imports.libs.utility const { QuickSettingsGrid } = Me.imports.libs.gnome var buttonRemoverFeature = class { @@ -13,6 +11,10 @@ var buttonRemoverFeature = class { this.removedItems = [] this.visibleListeners = [] } + onMenuItemAdded() { + this._unapply() + this._apply(this.userRemovedItems) + } _apply(removedItems) { for (const item of QuickSettingsGrid.get_children()) { let name = item.constructor.name.toString() @@ -50,12 +52,10 @@ var buttonRemoverFeature = class { this.settings.set_string("list-buttons",JSON.stringify(listButtons)) } - let items; { - items = this.settings.get_strv("user-removed-buttons") - if (!items) { - items = [] - this.settings.set_strv("user-removed-buttons",items) - } + let items = this.userRemovedItems = this.settings.get_strv("user-removed-buttons") + if (!items) { + items = this.userRemovedItems = [] + this.settings.set_strv("user-removed-buttons",items) } this._apply(items) @@ -63,7 +63,7 @@ var buttonRemoverFeature = class { this._removedItemsConnection = this.settings.connect('changed::user-removed-buttons', (settings, key) => { this._unapply() - this._apply(this.settings.get_strv("user-removed-buttons")) + this._apply(this.userRemovedItems = this.settings.get_strv("user-removed-buttons")) }) } unload() { diff --git a/features/dateMenu.js b/features/dateMenu.js index 3584110..1e370bb 100644 --- a/features/dateMenu.js +++ b/features/dateMenu.js @@ -2,74 +2,72 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() const { GLib } = imports.gi -const featureReloader = Me.imports.libs.featureReloader +const { featureReloader } = Me.imports.libs.utility const { - DateMenuNotifications, - DateMenuMediaControl, - DateMenuHolder, - DateMenuBox + DateMenuNotifications, + DateMenuMediaControl, + DateMenuHolder, + DateMenuBox, } = Me.imports.libs.gnome -const { Indicator } = Me.imports.libs.dndQuickToogleHandler +const { Indicator } = Me.imports.libs.dndQuickToggleHandler var dateMenuFeature = class { - load() { - // setup reloader - featureReloader.enableWithSettingKeys(this,[ - "datemenu-remove-media-control", - "datemenu-remove-notifications", - "datemenu-fix-weather-widget" - ]) + load() { + // setup reloader + featureReloader.enableWithSettingKeys(this, [ + "datemenu-remove-media-control", + "datemenu-remove-notifications", + "datemenu-fix-weather-widget", + ]) - // remove media control from date menu - if (this.settings.get_boolean("datemenu-remove-media-control")) { - this.dateMenuMediaControlRemoved = true - DateMenuMediaControl.hide() - this.dateMenuMediaControlConnection = DateMenuMediaControl.connect("show", ()=>{ - DateMenuMediaControl.hide() - }) - } + // remove media control from date menu + if (this.settings.get_boolean("datemenu-remove-media-control")) { + this.dateMenuMediaControlRemoved = true + DateMenuMediaControl.hide() + this.dateMenuMediaControlConnection = DateMenuMediaControl.connect( + "show",DateMenuMediaControl.hide.bind(DateMenuMediaControl) + ) + } - // remove notifications from date menu - if (this.settings.get_boolean("datemenu-remove-notifications")) { - this.dateMenuNotificationsRemoved = true - DateMenuNotifications.hide() - DateMenuBox.style = "padding: 4px 6px 4px 0px;" - this.dateMenuConnection = DateMenuNotifications.connect("show", ()=>{ - DateMenuNotifications.hide() - }) - } - - // datemenu fix weather widget - if (this.settings.get_boolean("datemenu-fix-weather-widget")) { - this.weatherFixBackupClass = DateMenuBox.style_class - DateMenuBox.style_class += " qwreey-fixed-weather" - } + // remove notifications from date menu + if (this.settings.get_boolean("datemenu-remove-notifications")) { + this.dateMenuNotificationsRemoved = true + DateMenuNotifications.hide() + DateMenuBox.style = "padding: 4px 6px 4px 0px;" + this.dateMenuConnection = DateMenuNotifications.connect("show", DateMenuNotifications.hide.bind(DateMenuNotifications)) } - unload() { - // disable feature reloader - featureReloader.disable(this) + // datemenu fix weather widget + if (this.settings.get_boolean("datemenu-fix-weather-widget")) { + this.weatherFixBackupClass = DateMenuBox.style_class + DateMenuBox.style_class += " qwreey-fixed-weather" + } + } - // restore media control - if (this.dateMenuMediaControlRemoved) { - DateMenuMediaControl.disconnect(this.dateMenuMediaControlConnection) - if (DateMenuMediaControl._shouldShow()) DateMenuMediaControl.show() - this.dateMenuMediaControlRemoved = null - this.dateMenuMediaControlConnection = null - } + unload() { + // disable feature reloader + featureReloader.disable(this); - // restore notifications to date menu - if (this.dateMenuNotificationsRemoved) { - DateMenuNotifications.disconnect(this.dateMenuConnection) - DateMenuNotifications.show() - DateMenuBox.style = "" - this.dateMenuNotificationsRemoved = null - } - // undo weather fix - if (this.weatherFixBackupClass) { - DateMenuBox.style_class = this.weatherFixBackupClass - this.weatherFixBackupClass = null - } + // restore media control + if (this.dateMenuMediaControlRemoved) { + DateMenuMediaControl.disconnect(this.dateMenuMediaControlConnection); + if (DateMenuMediaControl._shouldShow()) DateMenuMediaControl.show(); + this.dateMenuMediaControlRemoved = null; + this.dateMenuMediaControlConnection = null; } -} + // restore notifications to date menu + if (this.dateMenuNotificationsRemoved) { + DateMenuNotifications.disconnect(this.dateMenuConnection); + DateMenuNotifications.show(); + DateMenuBox.style = ""; + this.dateMenuNotificationsRemoved = null; + } + + // undo weather fix + if (this.weatherFixBackupClass) { + DateMenuBox.style_class = this.weatherFixBackupClass; + this.weatherFixBackupClass = null; + } + } +}; diff --git a/features/dndQuickToggle.js b/features/dndQuickToggle.js index f54243d..0ab3177 100644 --- a/features/dndQuickToggle.js +++ b/features/dndQuickToggle.js @@ -1,55 +1,58 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() -const featureReloader = Me.imports.libs.featureReloader -const { QuickSettings } = Me.imports.libs.gnome -const { Indicator } = Me.imports.libs.dndQuickToogleHandler -const { DateMenu } = Me.imports.libs.gnome -const { Gio, GObject } = imports.gi; - +const { featureReloader, addQuickSettingsItems } = Me.imports.libs.utility +const { QuickSettings, DateMenu, QuickSettingsGrid } = Me.imports.libs.gnome +const { Indicator } = Me.imports.libs.dndQuickToggleHandler +const { Gio, GObject } = imports.gi var dndQuickToggleFeature = class { - load() { - // setup reloader - featureReloader.enableWithSettingKeys(this,[ - "add-dnd-quick-toggle-enabled" - ]) - - // check is feature enabled - if (!this.settings.get_boolean("add-dnd-quick-toggle-enabled")) return - - // Add DND Quick Toggle - this.dndToggle = new Indicator() - QuickSettings._indicators.add_child(this.dndToggle) - QuickSettings._addItems(this.dndToggle.quickSettingsItems) - - //remove DND button from datemenu - this.datemenu_dnd = DateMenu.last_child.last_child - this.datemenu_dnd.hide() - this.datemenu_dnd_connection = this.datemenu_dnd.connect("show", () => { this.datemenu_dnd.hide() }) - - } + load() { + // setup reloader + featureReloader.enableWithSettingKeys(this, [ + "add-dnd-quick-toggle-enabled", + ]) + + this.datemenu_dnd = null + // check is feature enabled + if (!this.settings.get_boolean("add-dnd-quick-toggle-enabled")) return + + // Add DND Quick Toggle + this.dndToggle = new Indicator() + QuickSettings._indicators.add_child(this.dndToggle) + // QuickSettings._addItems(this.dndToggle.quickSettingsItems) + addQuickSettingsItems(this.dndToggle.quickSettingsItems) + + //remove DND button from datemenu + this.datemenu_dnd = DateMenu.last_child.last_child + this.datemenu_dnd.hide() + this.datemenu_dnd_connection = this.datemenu_dnd.connect("show", () => { + this.datemenu_dnd.hide() + }) + } - unload() { - // disable feature reloader - featureReloader.disable(this) - //put back the button to the datemenu - this.datemenu_dnd.disconnect(this.datemenu_dnd_connection) - this.datemenu_dnd_connection = null; - const _settings = new Gio.Settings({ - schema_id: 'org.gnome.desktop.notifications', - }); - if (!_settings.get_boolean('show-banners')){ - this.datemenu_dnd.show(); - } - // Remove DND Quick Toggle - if (this.dndToggle) { - const dndQSItems = this.dndToggle.quickSettingsItems[0] - dndQSItems.get_parent().remove_child(dndQSItems) - this.dndToggle.get_parent().remove_child(this.dndToggle) - this.dndToggle.destroy() - this.dndToggle = null - } + unload() { + // disable feature reloader + featureReloader.disable(this) + + if (this.datemenu_dnd == null) return + + //put back the button to the datemenu + this.datemenu_dnd.disconnect(this.datemenu_dnd_connection) + this.datemenu_dnd_connection = null + const _settings = new Gio.Settings({ + schema_id: "org.gnome.desktop.notifications", + }) + if (!_settings.get_boolean("show-banners")) { + this.datemenu_dnd.show() } + // Remove DND Quick Toggle + if (this.dndToggle) { + const dndQSItems = this.dndToggle.quickSettingsItems[0] + dndQSItems.get_parent().remove_child(dndQSItems) + this.dndToggle.get_parent().remove_child(this.dndToggle) + this.dndToggle.destroy() + this.dndToggle = null + } + } } - diff --git a/features/inputOutput.js b/features/inputOutput.js index d8115b2..b38b638 100644 --- a/features/inputOutput.js +++ b/features/inputOutput.js @@ -1,12 +1,11 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() -const featureReloader = Me.imports.libs.featureReloader -const { addChildWithIndex } = Me.imports.libs.utility +const { featureReloader } = Me.imports.libs.utility const { QuickSettingsGrid } = Me.imports.libs.gnome const { Label } = imports.gi.St const Volume = imports.ui.status.volume -const PopupMenu = imports.ui.popupMenu; +const PopupMenu = imports.ui.popupMenu var inputOutputFeature = class { load() { @@ -17,26 +16,42 @@ var inputOutputFeature = class { "input-always-show" ]) - this._setupOutputChangedListener() - this._setupInputChangedListener() - this._setupInputVisibilityObserver() + this._outputListener = null + this._inputListener = null + this._inputVisibilityListener = null + + this._inputStreamSlider = this._getInputStreamSlider() + if (this._inputStreamSlider && this.settings.get_boolean("input-show-selected")) { + this._setupInputChangedListener() + } + if (this._inputStreamSlider && this.settings.get_boolean("input-always-show")) { + this._setupInputVisibilityObserver() + } + this._outputStreamSlider = this._getOutputStreamSlider() + if (this._outputStreamSlider && this.settings.get_boolean("output-show-selected")) { + this._setupOutputChangedListener() + } } unload() { // disable feature reloader featureReloader.disable(this) - this._detachOutputLabel() - Volume.getMixerControl().disconnect(this._outputListener) - this._outputListener = null - - this._detachInputLabel() - Volume.getMixerControl().disconnect(this._inputListener) - this._inputListener = null - - this._getInputStreamSlider().disconnect(this._inputVisibilityListener) - this._inputVisibilityListener = null - this._getInputStreamSlider().visible = this._getInputStreamSlider()._shouldBeVisible() + if (this._inputStreamSlider && this._inputListener) { + this._detachInputLabel() + Volume.getMixerControl().disconnect(this._inputListener) + this._inputListener = null + } + if (this._inputStreamSlider && this._inputVisibilityListener) { + this._inputStreamSlider.disconnect(this._inputVisibilityListener) + this._inputVisibilityListener = null + this._inputStreamSlider.visible = this._inputStreamSlider._shouldBeVisible() + } + if (this._outputStreamSlider && this._outputListener) { + this._detachOutputLabel() + Volume.getMixerControl().disconnect(this._outputListener) + this._outputListener = null + } } // =========================================== Ouput =========================================== @@ -53,10 +68,10 @@ var inputOutputFeature = class { _attachOutputLabel() { this.outputLabel = new Label() this.outputLabel.style_class = "QSTWEAKS-volume-mixer-label" - addChildWithIndex(QuickSettingsGrid, this.outputLabel, this._getOutputStreamSliderIndex() - 1); + QuickSettingsGrid.insert_child_at_index(this.outputLabel, this._getOutputStreamSliderIndex() - 1) this._spanTwoColumns(this.outputLabel) this.outputLabel.visible = this.settings.get_boolean("output-show-selected") - this.outputLabel.text = this._findActiveDevice(this._getOutputStreamSlider()) + this.outputLabel.text = this._findActiveDevice(this._outputStreamSlider) } _detachOutputLabel() { @@ -69,16 +84,16 @@ var inputOutputFeature = class { // =========================================== Input =========================================== _setupInputChangedListener() { this._attachInputLabel() - this._outputListener = Volume.getMixerControl().connect('active-input-update', (c, id) => this._onInputDeviceChanged(id)) + this._inputListener = Volume.getMixerControl().connect('active-input-update', (c, id) => this._onInputDeviceChanged(id)) } _attachInputLabel() { this.inputLabel = new Label() this.inputLabel.style_class = "QSTWEAKS-volume-mixer-label" - addChildWithIndex(QuickSettingsGrid, this.inputLabel, this._getInputStreamSliderIndex() - 1) + QuickSettingsGrid.insert_child_at_index(this.inputLabel, this._getInputStreamSliderIndex() - 1) this._spanTwoColumns(this.inputLabel) this._setInputLabelVisibility() - this.inputLabel.text = this._findActiveDevice(this._getInputStreamSlider()) + this.inputLabel.text = this._findActiveDevice(this._inputStreamSlider) } _onInputDeviceChanged(deviceId) { @@ -96,21 +111,23 @@ var inputOutputFeature = class { // =========================================== Input Visbility =========================================== _setupInputVisibilityObserver() { - this._inputVisibilityListener = this._getInputStreamSlider().connect("notify::visible", () => this._onInputStreamSliderSynced()) + this._inputVisibilityListener = this._inputStreamSlider.connect("notify::visible", () => this._onInputStreamSliderSynced()) this._onInputStreamSliderSynced() } _onInputStreamSliderSynced() { this._setInputStreamSliderVisibility() - this._setInputLabelVisibility() + if (this._inputListener) { + this._setInputLabelVisibility() + } } _setInputStreamSliderVisibility() { - this._getInputStreamSlider().visible = this._getInputStreamSlider()._shouldBeVisible() || this.settings.get_boolean("input-always-show") + this._inputStreamSlider.visible = this._inputStreamSlider._shouldBeVisible() || this.settings.get_boolean("input-always-show") } _setInputLabelVisibility() { - this.inputLabel.visible = this._getInputStreamSlider().visible && this.settings.get_boolean("input-show-selected") + this.inputLabel.visible = this._inputStreamSlider.visible && this.settings.get_boolean("input-show-selected") } @@ -167,7 +184,7 @@ var inputOutputFeature = class { if (!device) return - const {description, origin} = device; + const {description, origin} = device const name = origin ? `${description} – ${origin}` : description @@ -178,4 +195,4 @@ var inputOutputFeature = class { _spanTwoColumns(object) { QuickSettingsGrid.layout_manager.child_set_property(QuickSettingsGrid, object, 'column-span', 2) } -}; +} diff --git a/features/notifications.js b/features/notifications.js index c366e0a..f0d0590 100644 --- a/features/notifications.js +++ b/features/notifications.js @@ -1,9 +1,8 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() -const featureReloader = Me.imports.libs.featureReloader +const { featureReloader } = Me.imports.libs.utility const { Notifications } = Me.imports.libs.notificationHandler -const { addChildWithIndex } = Me.imports.libs.utility const { QuickSettingsGrid, QuickSettingsBox, @@ -12,6 +11,22 @@ const { } = Me.imports.libs.gnome var notificationsFeature = class { + onMenuOpen() { + // reorder on menu open + if (this.mediaControlEnabled) { + QuickSettingsGrid.set_child_at_index( + this.notificationHandler.mediaSection, + -1 + ) + } + if (this.notificationsEnabled && this.notificationsIntegrated) { + QuickSettingsGrid.set_child_at_index( + this.notificationHandler, + this.notificationsPosition == "top" ? QuickSettingsGrid.get_children().findIndex((child)=>child.constructor?.name == "SystemItem")+1 : -1 + ) + } + } + load() { let settings = this.settings @@ -29,8 +44,8 @@ var notificationsFeature = class { ]) // check is feature enabled - let notificationsEnabled = this.settings.get_boolean("notifications-enabled") - let mediaControlEnabled = this.settings.get_boolean("media-control-enabled") + let notificationsEnabled = this.notificationsEnabled = this.settings.get_boolean("notifications-enabled") + let mediaControlEnabled = this.mediaControlEnabled = this.settings.get_boolean("media-control-enabled") let disableAdjustBorder = this.settings.get_boolean("disable-adjust-content-border-radius") let disableRemoveShadow = this.settings.get_boolean("disable-remove-shadow") let nativeControls = this.settings.get_boolean("notifications-use-native-controls") @@ -43,7 +58,7 @@ var notificationsFeature = class { hideWhenNoNotifications: this.settings.get_boolean("notifications-hide-when-no-notifications") }) - let notificationStyle = this.notificationHandler.style_class = + this.notificationHandler.style_class = // If separated, style as popup menu (isIntegrated ? "" : "popup-menu-content quick-settings ") // Integrated or separated @@ -58,10 +73,10 @@ var notificationsFeature = class { // Max height this.notificationHandler.style - = `max-height: ${this.settings.get_int("notifications-max-height")}px;` + = `max-height: ${this.settings.get_int("notifications-max-height")}px` this.maxHeigthListen = this.settings.connect("changed::notifications-max-height",()=>{ this.notificationHandler.style - = `max-height: ${this.settings.get_int("notifications-max-height")}px;` + = `max-height: ${this.settings.get_int("notifications-max-height")}px` }) // Insert media control @@ -96,20 +111,17 @@ var notificationsFeature = class { } // Insert notifications + let notificationsPosition = this.notificationsPosition = this.settings.get_string("notifications-position") + let notificationsIntegrated = this.notificationsIntegrated = this.settings.get_string("notifications-position") if (notificationsEnabled) { - if (this.settings.get_boolean("notifications-integrated")) { + if (notificationsIntegrated) { // Insert notification modal - switch (this.settings.get_string("notifications-position")) { + switch (notificationsPosition) { case "top": - // get system item index - let gridChildren = QuickSettingsGrid.get_children() - let systemItemIndex = null - for (let index = 0; indexchild.constructor?.name == "SystemItem")+1 + ) break case "bottom": QuickSettingsGrid.add_child(this.notificationHandler) @@ -143,7 +155,7 @@ var notificationsFeature = class { } // Insert notification modal - switch (this.settings.get_string("notifications-position")) { + switch (notificationsPosition) { case "top": let quickSettingsModal = QuickSettingsBox.first_child QuickSettingsBox.remove_child(quickSettingsModal) diff --git a/features/unsafeQuickToggle.js b/features/unsafeQuickToggle.js new file mode 100644 index 0000000..d9a0749 --- /dev/null +++ b/features/unsafeQuickToggle.js @@ -0,0 +1,34 @@ +const ExtensionUtils = imports.misc.extensionUtils +const Me = ExtensionUtils.getCurrentExtension() + +const { featureReloader, addQuickSettingsItems } = Me.imports.libs.utility +const { QuickSettings, DateMenu, QuickSettingsGrid } = Me.imports.libs.gnome +const { UnsafeQuickToggle } = Me.imports.libs.unsafeQuickToggleHandler +const { Gio, GObject } = imports.gi + +var unsafeQuickToggleFeature = class { + load() { + // setup reloader + featureReloader.enableWithSettingKeys(this, [ + "add-unsafe-quick-toggle-enabled", + ]) + + // check is feature enabled + if (!this.settings.get_boolean("add-unsafe-quick-toggle-enabled")) return + global.context.unsafe_mode = this.settings.get_boolean("last-unsafe-state") + + // Add Unsafe Quick Toggle + this.unsafeToggle = new UnsafeQuickToggle((state)=>this.settings.set_boolean("last-unsafe-state",state)) + addQuickSettingsItems([this.unsafeToggle]) + } + + unload() { + // disable feature reloader + featureReloader.disable(this) + + if (this.unsafeToggle) { + this.unsafeToggle.destroy() + global.context.unsafe_mode = false + } + } +} diff --git a/features/volumeMixer.js b/features/volumeMixer.js index d2ebac4..d74846b 100644 --- a/features/volumeMixer.js +++ b/features/volumeMixer.js @@ -1,12 +1,26 @@ const ExtensionUtils = imports.misc.extensionUtils const Me = ExtensionUtils.getCurrentExtension() -const featureReloader = Me.imports.libs.featureReloader +const { featureReloader } = Me.imports.libs.utility const { VolumeMixer } = Me.imports.libs.volumeMixerHandler const { QuickSettingsGrid } = Me.imports.libs.gnome -const { addChildWithIndex } = Me.imports.libs.utility var volumeMixerFeature = class { + onMenuOpen() { + // reorder on menu open + if (this.volumeMixer) { + QuickSettingsGrid.set_child_below_sibling( + this.volumeMixer.actor, + this._getInputStreamSlider() + ) + } + } + + _getInputStreamSlider() { + return this.inputStreamSlider + || (this.inputStreamSlider = QuickSettingsGrid.get_children().find((child)=>child.constructor?.name == "InputStreamSlider")) + } + load() { let settings = this.settings @@ -35,20 +49,11 @@ var volumeMixerFeature = class { 'volume-mixer-use-regex': settings.get_boolean("volume-mixer-use-regex") }) - // Find Input slider index - let inputSliderIndex - let gridChildren = QuickSettingsGrid.get_children() - for (let index = 0; index this._sync()); - - this.connectObject( - 'destroy', () => this._settings.run_dispose(), - 'clicked', () => this._toggleMode(), - this); - - this._sync(); - } - - _toggleMode() { - this._settings.set_boolean('show-banners', - !this._settings.get_boolean('show-banners')); - } - - _sync() { - const checked = !this._settings.get_boolean('show-banners'); - if (this.checked !== checked) - this.set({checked}); - } -}); - -var Indicator = GObject.registerClass( -class Indicator extends SystemIndicator { - _init() { - super._init(); - - this._indicator = this._addIndicator(); - - this._indicator.icon_name = 'notifications-disabled-symbolic' - this.quickSettingsItems.push(new DndQuickToogle()); - - this._settings = new Gio.Settings({ - schema_id: 'org.gnome.desktop.notifications', - }); - - this._changedId = this._settings.connect('changed::show-banners', - () => this._sync()); - - this._sync(); - } - - _sync() { - const checked = !this._settings.get_boolean('show-banners'); - if (checked){ - this._indicator.visible = true; - } - else{ - this._indicator.visible = false; - } - - } - - -}); diff --git a/libs/featureReloader.js b/libs/featureReloader.js deleted file mode 100644 index 8d26c25..0000000 --- a/libs/featureReloader.js +++ /dev/null @@ -1,27 +0,0 @@ -// Enable feature reloader with specific setting keys -function enableWithSettingKeys(feature,settingKeys) { - // save connections here and destroy when disable called - let settingsListeners = feature.settingsListeners - if (!settingsListeners) { - settingsListeners = [] - feature.settingsListeners = settingsListeners - } - - const reload = ()=>{ - feature.unload() - feature.load() - } - - for (const key of settingKeys) { - settingsListeners.push(feature.settings.connect("changed::"+key,reload)) - } -} - -// Disable feature reloader -function disable(feature) { - if (!feature.settingsListeners) return - for (const connection of feature.settingsListeners) { - feature.settings.disconnect(connection) - } - feature.settingsListeners = null -} diff --git a/libs/notificationHandler.js b/libs/notificationHandler.js index 06ea52c..0377307 100644 --- a/libs/notificationHandler.js +++ b/libs/notificationHandler.js @@ -2,110 +2,9 @@ const { GObject, St, Clutter } = imports.gi const Main = imports.ui.main const Calendar = imports.ui.calendar const ExtensionUtils = imports.misc.extensionUtils -const Me = ExtensionUtils.getCurrentExtension() - -var Notifications = GObject.registerClass( - class Notifications extends St.BoxLayout{ - _init(options){ - let useNativeControls = options.useNativeControls - let hideWhenNoNotifications = options.hideWhenNoNotifications - - super._init({ - vertical: true, - }) - - let datemenu = new imports.ui.dateMenu.DateMenuButton() - let messageList = datemenu._messageList - this.notificationList = messageList._notificationSection - this.nativeDndSwitch = messageList._dndButton - this.nativeClearButton = messageList._clearButton - - // media controls - this.mediaSection = messageList._mediaSection - this.mediaSection.get_parent().remove_child(this.mediaSection) - - // notification list scroll - this.list = messageList._scrollView - this.list.get_parent().remove_child(this.list) - - // header / title - let headerBox = new St.BoxLayout({ style_class: "QSTWEAKS-notifications-header" }) - this.add_child(headerBox) - this.add_child(this.list) - let titleLabel = new St.Label({ - text: ExtensionUtils.gettext('Notifications'), - style_class: "QSTWEAKS-notifications-title", - y_align: Clutter.ActorAlign.CENTER, - x_align: Clutter.ActorAlign.START, - x_expand: true - }) - headerBox.add_child(titleLabel) - - // no notifications text - let noNotiBox = new St.BoxLayout({x_align: Clutter.ActorAlign.CENTER}) - noNotiBox.style_class = "QSTWEAKS-notifications-no-notifications-box" - const noNotiPlaceholder = new NoNotifPlaceholder() - noNotiBox.add_child(noNotiPlaceholder) - noNotiBox.hide() - this.add_child(noNotiBox) - - // clear button / dnd switch - if (useNativeControls) { - // if use native controls - { - let parent = this.nativeClearButton.get_parent() - parent.remove_child(this.nativeClearButton) - parent.remove_child(this.nativeDndSwitch) - this.nativeDndText = parent.first_child - parent.remove_child(this.nativeDndText) - } - - let nativeControlBox = new St.BoxLayout() - nativeControlBox.add_child(this.nativeDndText) - nativeControlBox.add_child(this.nativeDndSwitch) - nativeControlBox.add_child(this.nativeClearButton) - this.nativeControlBox = nativeControlBox - this.add_child(nativeControlBox) - } else { - this.clearButton = new ClearNotificationsButton() - this.clearButton.connect("clicked",()=>{ - messageList._sectionList.get_children().forEach(s => s.clear()) - }) - headerBox.add_child(this.clearButton) - } - - // sync notifications - let stockNotifications = Main.panel.statusArea.dateMenu._messageList._notificationSection - let notifications = stockNotifications._messages - notifications.forEach(n => { - let notification = new Calendar.NotificationMessage(n.notification) - this.notificationList.addMessage(notification) - }) - // sync no-notification placeholder and clear button - const updateNoNotifications = ()=>{ - if (this.nativeClearButton.reactive) { - this.list.show() - noNotiBox.hide() - if (this.clearButton) this.clearButton.show() - if (hideWhenNoNotifications) this.show() - } else { - this.list.hide() - noNotiBox.show() - if (this.clearButton) this.clearButton.hide() - if (hideWhenNoNotifications) this.hide() - } - } - this.nativeClearButton.connect('notify::reactive', updateNoNotifications) - updateNoNotifications() - - this.connect('destroy', () => { - datemenu.destroy() - datemenu = null - }) - } - } -) +const Me = ExtensionUtils.getCurrentExtension() +const { fixStScrollViewScrollbarOverflow } = Me.imports.libs.utility const NoNotifPlaceholder = GObject.registerClass( class NoNotifPlaceholder extends St.BoxLayout { @@ -157,3 +56,134 @@ class ClearNotificationsButton extends St.Button { container.add_child(this._label) } }) + +var Notifications = GObject.registerClass( +class Notifications extends St.BoxLayout{ + + // prepare date menu items + _prepareDateMenu() { + // create gnome datemenu + this.datemenu = new imports.ui.dateMenu.DateMenuButton() + + // notifications + let messageList = this.messageList = this.datemenu._messageList + this.notificationList = messageList._notificationSection + this.nativeDndSwitch = messageList._dndButton + this.nativeClearButton = messageList._clearButton + + // media controls + this.mediaSection = messageList._mediaSection + this.mediaSection.get_parent().remove_child(this.mediaSection) + + // notification list scroll + this.list = messageList._scrollView + this.list.get_parent().remove_child(this.list) + this.add_child(this.list) // mount + fixStScrollViewScrollbarOverflow(this.list) // fix fade effect + } + + // Create 'Notifications' text and ClearButton + _createHeaderArea() { + // header / title + let headerBox = new St.BoxLayout({ style_class: "QSTWEAKS-notifications-header" }) + let titleLabel = new St.Label({ + text: ExtensionUtils.gettext('Notifications'), + style_class: "QSTWEAKS-notifications-title", + y_align: Clutter.ActorAlign.CENTER, + x_align: Clutter.ActorAlign.START, + x_expand: true + }) + headerBox.add_child(titleLabel) + + // clear button + if (!this.options.useNativeControls) { + let clearButton = this.clearButton = new ClearNotificationsButton() + clearButton.connect("clicked",()=>{ + this.messageList._sectionList.get_children().forEach(s => s.clear()) + }) + headerBox.add_child(clearButton) + } + + // mount + this.insert_child_at_index(headerBox,0) + } + + // Create 'NoNotification' placeholder + _createNoNotificationArea() { + // container + let noNotiBox = this.noNotiBox = new St.BoxLayout({x_align: Clutter.ActorAlign.CENTER}) + noNotiBox.style_class = "QSTWEAKS-notifications-no-notifications-box" + noNotiBox.hide() + + // no notifications text + noNotiBox.add_child(new NoNotifPlaceholder()) + + // mount + this.add_child(noNotiBox) + } + + // sync no-notification placeholder and clear button + _updateNoNotifications() { + if (this.nativeClearButton.reactive) { + this.list.show() + this.noNotiBox.hide() + if (this.clearButton) this.clearButton.show() + if (this.options.hideWhenNoNotifications) this.show() + } else { + this.list.hide() + this.noNotiBox.show() + if (this.clearButton) this.clearButton.hide() + if (this.options.hideWhenNoNotifications) this.hide() + } + } + + // Sync + _syncNotifications() { + // sync notifications from gnome stock notifications + Main.panel.statusArea.dateMenu._messageList._notificationSection._messages.forEach((notification)=>{ + // for (notification of Main.panel.statusArea.dateMenu._messageList._notificationSection._messages) { + // clone message + this.notificationList.addMessage(new Calendar.NotificationMessage(notification.notification)) + }) + + // sync no-notification placeholder and clear button + this.nativeClearButton.connect('notify::reactive', this._updateNoNotifications.bind(this)) + this._updateNoNotifications() + } + + _createNativeControls() { + // unmount dnd/clear/text from parent + { + let parent = this.nativeClearButton.get_parent() + parent.remove_child(this.nativeClearButton) + parent.remove_child(this.nativeDndSwitch) + parent.remove_child(this.nativeDndText = parent.first_child) + } + + // create container + let nativeControlBox = this.nativeControlBox = new St.BoxLayout() + nativeControlBox.add_child(this.nativeDndText) + nativeControlBox.add_child(this.nativeDndSwitch) + nativeControlBox.add_child(this.nativeClearButton) + this.add_child(nativeControlBox) + } + + // options { + // hideWhenNoNotifications + // useNativeControls + // } + _init(options){ + super._init({ + vertical: true, + }) + this.options = options + + this._prepareDateMenu() + this._createHeaderArea() + this._createNoNotificationArea() + this._syncNotifications() + if (options.useNativeControls) this._createNativeControls() // native clear/dnd + + this.connect('destroy', this.datemenu.destroy.bind(this.datemenu)) + } +}) diff --git a/libs/streamSlider.js b/libs/streamSlider.js index acc443e..2f6c5c6 100644 --- a/libs/streamSlider.js +++ b/libs/streamSlider.js @@ -9,257 +9,257 @@ const { GObject, Gio, GLib, Gvc } = imports.gi const Volume = imports.ui.status.volume -const ALLOW_AMPLIFIED_VOLUME_KEY = 'allow-volume-above-100-percent'; -const { QuickSlider } = imports.ui.quickSettings; -const PopupMenu = imports.ui.popupMenu; +const ALLOW_AMPLIFIED_VOLUME_KEY = 'allow-volume-above-100-percent' +const { QuickSlider } = imports.ui.quickSettings +const PopupMenu = imports.ui.popupMenu var StreamSlider = GObject.registerClass({ Signals: { 'stream-updated': {}, }, }, class StreamSlider extends QuickSlider { _init(control) { - super._init(); + super._init() - this._connections = []; // ADDED BY QWREEY - this._control = control; + this._connections = [] // ADDED BY QWREEY + this._control = control - this._inDrag = false; - this._notifyVolumeChangeId = 0; + this._inDrag = false + this._notifyVolumeChangeId = 0 this._soundSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.sound', - }); + }) // MODED BY QWREEY this._connections.push([ this._soundSettings, this._soundSettings.connect(`changed::${ALLOW_AMPLIFIED_VOLUME_KEY}`, () => this._amplifySettingsChanged()) - ]); - this._amplifySettingsChanged(); + ]) + this._amplifySettingsChanged() this._sliderChangedId = this.slider.connect('notify::value', - () => this._sliderChanged()); + () => this._sliderChanged()) this._connections.push([ // ADDED BY QWREEY this.slider,this._sliderChangedId - ]); + ]) this._connections.push([ // MODED BY QWREEY this.slider, this.slider.connect('drag-begin', () => (this._inDrag = true)) - ]); + ]) this._connections.push([ // MODED BY QWREEY this.slider, this.slider.connect('drag-end', () => { - this._inDrag = false; - this._notifyVolumeChange(); + this._inDrag = false + this._notifyVolumeChange() }) - ]); + ]) - this._deviceItems = new Map(); + this._deviceItems = new Map() - this._deviceSection = new PopupMenu.PopupMenuSection(); - this.menu.addMenuItem(this._deviceSection); + this._deviceSection = new PopupMenu.PopupMenuSection() + this.menu.addMenuItem(this._deviceSection) - this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - this.menu.addSettingsAction(_('Sound Settings'),'gnome-sound-panel.desktop'); + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()) + this.menu.addSettingsAction(_('Sound Settings'),'gnome-sound-panel.desktop') - this._stream = null; - this._volumeCancellable = null; - this._icons = []; + this._stream = null + this._volumeCancellable = null + this._icons = [] - this._sync(); + this._sync() this._connections.push([ // ADDED BY QWREEY this, this.connect('destroy', this._destroy.bind(this)) - ]); + ]) } get stream() { - return this._stream; + return this._stream } set stream(stream) { - this._stream?.disconnectObject(this); + this._stream?.disconnectObject(this) - this._stream = stream; + this._stream = stream if (this._stream) { - this._connectStream(this._stream); - this._updateVolume(); + this._connectStream(this._stream) + this._updateVolume() } else { - this.emit('stream-updated'); + this.emit('stream-updated') } - this._sync(); + this._sync() } _connectStream(stream) { stream.connectObject( 'notify::is-muted', this._updateVolume.bind(this), - 'notify::volume', this._updateVolume.bind(this), this); + 'notify::volume', this._updateVolume.bind(this), this) } _lookupDevice(_id) { throw new GObject.NotImplementedError( - `_lookupDevice in ${this.constructor.name}`); + `_lookupDevice in ${this.constructor.name}`) } _activateDevice(_device) { throw new GObject.NotImplementedError( - `_activateDevice in ${this.constructor.name}`); + `_activateDevice in ${this.constructor.name}`) } _addDevice(id) { if (this._deviceItems.has(id)) - return; + return - const device = this._lookupDevice(id); + const device = this._lookupDevice(id) if (!device) - return; + return - const {description, origin} = device; + const {description, origin} = device const name = origin ? `${description} – ${origin}` - : description; - const item = new PopupMenu.PopupImageMenuItem(name, device.get_gicon()); + : description + const item = new PopupMenu.PopupImageMenuItem(name, device.get_gicon()) this._connections.push([ item, item.connect('activate', () => this._activateDevice(device)) - ]); + ]) - this._deviceSection.addMenuItem(item); - this._deviceItems.set(id, item); + this._deviceSection.addMenuItem(item) + this._deviceItems.set(id, item) - this._sync(); + this._sync() } _removeDevice(id) { - this._deviceItems.get(id)?.destroy(); + this._deviceItems.get(id)?.destroy() if (this._deviceItems.delete(id)) - this._sync(); + this._sync() } _setActiveDevice(activeId) { for (const [id, item] of this._deviceItems) { item.setOrnament(id === activeId ? PopupMenu.Ornament.CHECK - : PopupMenu.Ornament.NONE); + : PopupMenu.Ornament.NONE) } } _shouldBeVisible() { - return this._stream != null; + return this._stream != null } _sync() { - this.visible = this._shouldBeVisible(); - this.menuEnabled = this._deviceItems.size > 1; + this.visible = this._shouldBeVisible() + this.menuEnabled = this._deviceItems.size > 1 } _sliderChanged() { if (!this._stream) - return; + return - let value = this.slider.value; - let volume = value * this._control.get_vol_max_norm(); - let prevMuted = this._stream.is_muted; - let prevVolume = this._stream.volume; + let value = this.slider.value + let volume = value * this._control.get_vol_max_norm() + let prevMuted = this._stream.is_muted + let prevVolume = this._stream.volume if (volume < 1) { - this._stream.volume = 0; + this._stream.volume = 0 if (!prevMuted) - this._stream.change_is_muted(true); + this._stream.change_is_muted(true) } else { - this._stream.volume = volume; + this._stream.volume = volume if (prevMuted) - this._stream.change_is_muted(false); + this._stream.change_is_muted(false) } - this._stream.push_volume(); + this._stream.push_volume() - let volumeChanged = this._stream.volume !== prevVolume; + let volumeChanged = this._stream.volume !== prevVolume if (volumeChanged && !this._notifyVolumeChangeId && !this._inDrag) { this._notifyVolumeChangeId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 30, () => { - this._notifyVolumeChange(); - this._notifyVolumeChangeId = 0; - return GLib.SOURCE_REMOVE; - }); + this._notifyVolumeChange() + this._notifyVolumeChangeId = 0 + return GLib.SOURCE_REMOVE + }) GLib.Source.set_name_by_id(this._notifyVolumeChangeId, - '[gnome-shell] this._notifyVolumeChangeId'); + '[gnome-shell] this._notifyVolumeChangeId') } } _notifyVolumeChange() { if (this._volumeCancellable) - this._volumeCancellable.cancel(); - this._volumeCancellable = null; + this._volumeCancellable.cancel() + this._volumeCancellable = null if (this._stream.state === Gvc.MixerStreamState.RUNNING) - return; // feedback not necessary while playing + return // feedback not necessary while playing - this._volumeCancellable = new Gio.Cancellable(); - let player = global.display.get_sound_player(); + this._volumeCancellable = new Gio.Cancellable() + let player = global.display.get_sound_player() player.play_from_theme('audio-volume-change', - _('Volume changed'), this._volumeCancellable); + _('Volume changed'), this._volumeCancellable) } _changeSlider(value) { - this.slider.block_signal_handler(this._sliderChangedId); - this.slider.value = value; - this.slider.unblock_signal_handler(this._sliderChangedId); + this.slider.block_signal_handler(this._sliderChangedId) + this.slider.value = value + this.slider.unblock_signal_handler(this._sliderChangedId) } _updateVolume() { - let muted = this._stream.is_muted; + let muted = this._stream.is_muted this._changeSlider(muted - ? 0 : this._stream.volume / this._control.get_vol_max_norm()); - this.emit('stream-updated'); + ? 0 : this._stream.volume / this._control.get_vol_max_norm()) + this.emit('stream-updated') } _amplifySettingsChanged() { - this._allowAmplified = this._soundSettings.get_boolean(ALLOW_AMPLIFIED_VOLUME_KEY); + this._allowAmplified = this._soundSettings.get_boolean(ALLOW_AMPLIFIED_VOLUME_KEY) this.slider.maximum_value = this._allowAmplified - ? this.getMaxLevel() : 1; + ? this.getMaxLevel() : 1 if (this._stream) - this._updateVolume(); + this._updateVolume() } getIcon() { if (!this._stream) - return null; + return null - let volume = this._stream.volume; - let n; + let volume = this._stream.volume + let n if (this._stream.is_muted || volume <= 0) { - n = 0; + n = 0 } else { - n = Math.ceil(3 * volume / this._control.get_vol_max_norm()); - n = Math.clamp(n, 1, this._icons.length - 1); + n = Math.ceil(3 * volume / this._control.get_vol_max_norm()) + n = Math.clamp(n, 1, this._icons.length - 1) } - return this._icons[n]; + return this._icons[n] } getLevel() { if (!this._stream) - return null; + return null - return this._stream.volume / this._control.get_vol_max_norm(); + return this._stream.volume / this._control.get_vol_max_norm() } getMaxLevel() { - let maxVolume = this._control.get_vol_max_norm(); + let maxVolume = this._control.get_vol_max_norm() if (this._allowAmplified) - maxVolume = this._control.get_vol_max_amplified(); + maxVolume = this._control.get_vol_max_amplified() - return maxVolume / this._control.get_vol_max_norm(); + return maxVolume / this._control.get_vol_max_norm() } // ADDED BY QWREEY _destroy() { - GLib.Source.remove(this._notifyVolumeChangeId); + GLib.Source.remove(this._notifyVolumeChangeId) for (item of this._connections) { - item[0].disconnect(item[1]); + item[0].disconnect(item[1]) } - this._connections = null; + this._connections = null } -}); +}) diff --git a/libs/unsafeQuickToggleHandler.js b/libs/unsafeQuickToggleHandler.js new file mode 100644 index 0000000..c589e6a --- /dev/null +++ b/libs/unsafeQuickToggleHandler.js @@ -0,0 +1,40 @@ +const { Gio, GObject } = imports.gi +const { QuickToggle, SystemIndicator } = imports.ui.quickSettings +const { St } = imports.gi +const ExtensionUtils = imports.misc.extensionUtils + +var UnsafeQuickToggle = GObject.registerClass( + class UnsafeQuickToggle extends QuickToggle { + _updateIcon() { + this.iconName = this.checked ? "channel-insecure-symbolic" : "channel-secure-symbolic" + } + + _init(onUpdate) { + super._init({ + label: ExtensionUtils.gettext("Unsafe Mode"), + iconName: "channel-insecure-symbolic", + }) + this._onUpdate = onUpdate + + // bind click + this.connect("clicked", this._toggleMode.bind(this)) + + // Fetch global context + this._sync() + } + + // Toggle context + _toggleMode() { + this.checked = !global.context.unsafe_mode + global.context.unsafe_mode = this.checked + this._updateIcon() + this._onUpdate(this.checked) + } + + // Sync context + _sync() { + this.checked = global.context.unsafe_mode + this._updateIcon() + } + } +) diff --git a/libs/utility.js b/libs/utility.js index 83b33e6..a69da83 100644 --- a/libs/utility.js +++ b/libs/utility.js @@ -1,24 +1,63 @@ -// this module exports many useful functions -// for simplify main codes +const ExtensionUtils = imports.misc.extensionUtils +const Me = ExtensionUtils.getCurrentExtension() -function addChildWithIndex(parent,child,addIndex) { - let children = parent.get_children() - let tmp = [] - let tmp_visible = [] - for (let index = addIndex+1; indexstScrollView.overlay_scrollbars = !stScrollView.vscrollbar_visible + stScrollView.connect("notify::vscrollbar-visible",update) + update() +} + function logger(str) { log("[EXTENSION QSTweaks] " + str) } + +var featureReloader = { + // Enable feature reloader with specific setting keys + enableWithSettingKeys(feature,settingKeys) { + // save connections here and destroy when disable called + let settingsListeners = feature.settingsListeners + if (!settingsListeners) { + settingsListeners = [] + feature.settingsListeners = settingsListeners + } + + const reload = ()=>{ + feature.unload() + feature.load() + } + + for (const key of settingKeys) { + settingsListeners.push(feature.settings.connect("changed::"+key,reload)) + } + }, + + // Disable feature reloader + disable(feature) { + if (!feature.settingsListeners) return + for (const connection of feature.settingsListeners) { + feature.settings.disconnect(connection) + } + feature.settingsListeners = null + } +} diff --git a/metadata.json b/metadata.json index 0045034..3587992 100644 --- a/metadata.json +++ b/metadata.json @@ -1,10 +1,8 @@ { "description": "Let's tweak gnome 43's quick settings! You can add Media Controls, Notifications, Volume Mixer on quick settings and remove useless buttons!", - "name": "Quick Settings Tweaker", + "name": "[QSTweak] Quick Setting Tweaker", "url": "https://github.com/qwreey75/quick-settings-tweaks", - "shell-version": [ - "43" - ], + "shell-version": ["43", "44"], "uuid": "quick-settings-tweaks@qwreey", "settings-schema": "org.gnome.shell.extensions.quick-settings-tweaks", "gettext-domain": "quick-settings-tweaks" diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..a402349 --- /dev/null +++ b/po/cs.po @@ -0,0 +1,3 @@ +# Quick settings notifications title text +msgid "Notifications" +msgstr "Upozornění" diff --git a/po/en.po b/po/en.po index 750ca3b..42482d3 100644 --- a/po/en.po +++ b/po/en.po @@ -3,3 +3,7 @@ # Notifications title msgid "Notifications" msgstr "Notifications" + +# Unsafe mode +msgid "Unsafe Mode" +msgstr "Unsafe Mode" diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..566922b --- /dev/null +++ b/po/ru.po @@ -0,0 +1,5 @@ +# Quick settings notifications title text + +# Notifications title +msgid "Notifications" +msgstr "Уведомления" diff --git a/prefPages/quickToggles.js b/prefPages/quickToggles.js index 640ae81..979bdce 100644 --- a/prefPages/quickToggles.js +++ b/prefPages/quickToggles.js @@ -30,6 +30,13 @@ var quickTogglesPage = GObject.registerClass({ value: settings.get_boolean("add-dnd-quick-toggle-enabled"), bind: [settings, "add-dnd-quick-toggle-enabled"] }) + makeSwitch({ + parent: newTogglesGroup, + title: "Unsafe Mode Quick Toggle", + subtitle: "Turn on to make the unsafe quick toggle visible on the Quick Settings panel", + value: settings.get_boolean("add-unsafe-quick-toggle-enabled"), + bind: [settings, "add-unsafe-quick-toggle-enabled"] + }) this.add(newTogglesGroup) // description / enable diff --git a/schemas/org.gnome.shell.extensions.quick-settings-tweaks.gschema.xml b/schemas/org.gnome.shell.extensions.quick-settings-tweaks.gschema.xml index 8e77acb..72cb160 100644 --- a/schemas/org.gnome.shell.extensions.quick-settings-tweaks.gschema.xml +++ b/schemas/org.gnome.shell.extensions.quick-settings-tweaks.gschema.xml @@ -12,6 +12,9 @@ true + + false + true @@ -98,5 +101,10 @@ false + + + + false + diff --git a/stylesheet.css b/stylesheet.css index 4b22aa9..cd7ecd0 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -102,10 +102,6 @@ } .QSTWEAKS-notifications-separated { padding: 16px 4px 16px 16px; - margin: 6px 12px 6px 12px; - } - .QSTWEAKS-quick-settings-separated { - margin: 6px 12px 6px 12px; } .QSTWEAKS-notifications-separated .QSTWEAKS-notifications-no-notifications-placeholder { margin: 16px 0px;