From 37c3ec2c30e7192967778116dbb85716dda8691f Mon Sep 17 00:00:00 2001 From: Celio Date: Thu, 11 Sep 2025 11:22:58 -0300 Subject: [PATCH 1/8] quick-settings@celiopy: add applet --- quick-settings@celiopy | 1 + 1 file changed, 1 insertion(+) create mode 160000 quick-settings@celiopy diff --git a/quick-settings@celiopy b/quick-settings@celiopy new file mode 160000 index 00000000000..ac3d67d2f5a --- /dev/null +++ b/quick-settings@celiopy @@ -0,0 +1 @@ +Subproject commit ac3d67d2f5a326c617cda4725ffb06f39262eba4 From a180fbf8f96856e8bdf5a0a2a38695e6829ae9bf Mon Sep 17 00:00:00 2001 From: Celio Date: Sun, 14 Sep 2025 13:44:35 -0300 Subject: [PATCH 2/8] test --- quick-settings@celiopy | 1 - 1 file changed, 1 deletion(-) delete mode 160000 quick-settings@celiopy diff --git a/quick-settings@celiopy b/quick-settings@celiopy deleted file mode 160000 index ac3d67d2f5a..00000000000 --- a/quick-settings@celiopy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ac3d67d2f5a326c617cda4725ffb06f39262eba4 From 485afd5aedc7dd17984703e4fcb33eaf01241ed4 Mon Sep 17 00:00:00 2001 From: Celio Date: Sun, 14 Sep 2025 13:47:32 -0300 Subject: [PATCH 3/8] add applet: quick-settings --- quick-settings@celiopy/README.md | 58 ++ .../files/quick-settings@celiopy/applet.js | 653 ++++++++++++++++++ .../files/quick-settings@celiopy/icon.png | Bin 0 -> 591 bytes .../format-text-size-decrease-symbolic.svg | 43 ++ .../format-text-size-increase-symbolic.svg | 43 ++ .../icons/user-menu-symbolic.svg | 19 + .../quick-settings@celiopy/metadata.json | 6 + .../files/quick-settings@celiopy/po/pt_BR.po | 72 ++ .../po/user@celiopy.pot | 78 +++ .../settings-schema.json | 58 ++ .../quick-settings@celiopy/stylesheet.css | 121 ++++ quick-settings@celiopy/info.json | 1 + quick-settings@celiopy/screenshot.png | Bin 0 -> 21647 bytes 13 files changed, 1152 insertions(+) create mode 100644 quick-settings@celiopy/README.md create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/applet.js create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/icon.png create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-decrease-symbolic.svg create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-increase-symbolic.svg create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/icons/user-menu-symbolic.svg create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/metadata.json create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json create mode 100644 quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css create mode 100644 quick-settings@celiopy/info.json create mode 100644 quick-settings@celiopy/screenshot.png diff --git a/quick-settings@celiopy/README.md b/quick-settings@celiopy/README.md new file mode 100644 index 00000000000..5a58c7ef1c6 --- /dev/null +++ b/quick-settings@celiopy/README.md @@ -0,0 +1,58 @@ +# Quick Settings (Cinnamon Applet) + +**Quick Settings** is a modern and fully redesigned rework of the original \[cinnamon-user-applet]. +It provides an elegant and customizable way to access your essential desktop settings directly from the Cinnamon panel. + +![Screenshot](./screenshot.png) + +--- + +## ✨ Features + +* **Quick toggles** for common preferences: + + * Dark mode + * Night light + * Prevent sleep (inhibit suspend) + * …and more +* **User avatar** with optional username and host display +* **Session controls** (logout, lock, shutdown, restart, etc.) +* **Highly customizable** via applet preferences +* Define **separate themes** for light and dark mode + +--- + +## ⚙️ Preferences + +All aspects of the applet can be tuned in its preferences dialog: + +* Choose which toggles to display +* Enable/disable user information +* Configure session buttons +* Set light and dark mode themes + +--- + +## 🚧 Status + +This applet is still in active development. +Some **bugs are known and currently being fixed**, so please expect improvements over time. + +--- + +## 📦 Installation + +1. Clone this repository into your Cinnamon applets directory: + + ```bash + git clone https://github.com/celiopy/cinnamon-user-applet + cp -r cinnamon-user-applet/files/user@celiopy ~/.local/share/cinnamon/applets/ + ``` +2. Enable it from **Cinnamon Settings → Applets**. +3. Configure it to your liking through the **Preferences** panel. + +--- + +## 🙌 Acknowledgements + +This project is based on and inspired by the original \[cinnamon-user-applet], but rebuilt and modernized with new design principles. \ No newline at end of file diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js new file mode 100644 index 00000000000..e4b118f3507 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -0,0 +1,653 @@ +const Applet = imports.ui.applet; +const St = imports.gi.St; +const PopupMenu = imports.ui.popupMenu; +const Util = imports.misc.util; +const Lang = imports.lang; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; +const AccountsService = imports.gi.AccountsService; +const GnomeSession = imports.misc.gnomeSession; +const ScreenSaver = imports.misc.screenSaver; +const Settings = imports.ui.settings; +const UserWidget = imports.ui.userWidget; +const Main = imports.ui.main; +const Tooltips = imports.ui.tooltips; +const Clutter = imports.gi.Clutter; +const Gettext = imports.gettext; +const Slider = imports.ui.slider; + +const UUID = 'user@celiopy'; +const APPLET_DIR = imports.ui.appletManager.appletMeta[UUID].path; +const DIALOG_ICON_SIZE = 32; + +const INHIBIT_IDLE_FLAG = 8; +const INHIBIT_SLEEP_FLAG = 4; + +// l10n/translation support +Gettext.bindtextdomain(UUID, GLib.get_home_dir() + "/.local/share/locale"); + +function _(str) { + return Gettext.dgettext(UUID, str); +} + +class CinnamonUserApplet extends Applet.TextIconApplet { + constructor(orientation, panel_height, instance_id) { + super(orientation, panel_height, instance_id); + this.setAllowedLayout(Applet.AllowedLayout.BOTH); + + this.sessionCookie = null; + + this.set_applet_icon_symbolic_name('user-menu-symbolic'); + + // Mapa de modos + this._powerModes = { + "power-saver": { next: "balanced", label: _("Power Saver"), icon: "power-profile-power-saver" }, + "balanced": { next: "performance", label: _("Balanced"), icon: "power-profile-balanced" }, + "performance": { next: "power-saver", label: _("Performance"), icon: "power-profile-performance" } + }; + + // Inicializa schemas, bindings, UI e toggles + this._initSchemas(); + this._initUI(orientation); + + // Métodos iniciais + this._onUserChanged(); + this._setKeybinding(); + } + + // === Inicializa schemas e bindings === + _initSchemas() { + // Schemas do applet + this.settings = new Settings.AppletSettings(this, UUID, this.instance_id); + this.settings.bind("light-theme", "_lightTheme"); + this.settings.bind("dark-theme", "_darkTheme"); + this.settings.bind("keyOpen", "keyOpen", () => this._setKeybinding()); + this.settings.bind("display-name", "display_name", () => this._updateLabels()); + + // Schemas do sistema + this._schemas = { + color: new Gio.Settings({ schema_id: "org.cinnamon.settings-daemon.plugins.color" }), + interface: new Gio.Settings({ schema_id: "org.cinnamon.desktop.interface" }), + gtk: new Gio.Settings({ schema_id: "org.gnome.desktop.interface" }), + cinnamon: new Gio.Settings({ schema_id: "org.cinnamon.desktop.interface" }), + portal: new Gio.Settings({ schema_id: "org.x.apps.portal" }), + screensaver: new Gio.Settings({ schema_id: "org.cinnamon.desktop.screensaver" }) + }; + } + + // === Inicializa UI do menu e painel === + _initUI(orientation) { + // Sessão + this.sessionProxy = null; + this._session = new GnomeSession.SessionManager(Lang.bind(this, function(proxy, error) { + if (error) { + global.logError("Error initializing session proxy: " + error.message); + return; + } + this.sessionProxy = proxy; + global.log("Session proxy initialized successfully"); + })); + this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); + + // Menu + this.menuManager = new PopupMenu.PopupMenuManager(this); + this.menu = new Applet.AppletPopupMenu(this, orientation); + this.menuManager.addMenu(this.menu); + + // Seções do menu + this.prefsSection = new PopupMenu.PopupMenuSection(); + this.interfaceSection = new PopupMenu.PopupMenuSection(); + this.sessionSection = new PopupMenu.PopupMenuSection(); + + this.menu.addMenuItem(this.prefsSection); + this.menu.addMenuItem(this.interfaceSection); + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this.menu.addMenuItem(this.sessionSection); + + // Grid de toggles (3 colunas) + this.prefsGrid = new St.Widget({ + layout_manager: new Clutter.GridLayout(), + style_class: "prefs-grid-container", + x_expand: true, + x_align: Clutter.ActorAlign.FILL + }); + let gridLayout = this.prefsGrid.layout_manager; + gridLayout.set_column_spacing(12); + gridLayout.set_row_spacing(16); + gridLayout.set_column_homogeneous(true); + this.prefsSection.actor.add_child(this.prefsGrid); + + this.maxTogglesPerRow = 3; + this.currentRow = 0; + this.currentColumn = 0; + + // Sessão: container horizontal + this.sessionContainer = new St.BoxLayout({ + style_class: "session-container", + vertical: false, + x_expand: true + }); + this.sessionSection.actor.add_child(this.sessionContainer); + + // User info + this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); + this._user.connect('notify::is-loaded', () => this._onUserChanged()); + this._user.connect('changed', () => this._onUserChanged()); + + let userBox = new St.BoxLayout({ style_class: 'user-box', vertical: false, y_expand: false }); + // Avatar dentro de botão + this._userButton = new St.Button({ + style_class: "user-avatar-button", + reactive: true, + can_focus: true, + track_hover: true + }); + + // Coloca o avatar dentro do botão + this._userIcon = new UserWidget.Avatar(this._user, { iconSize: DIALOG_ICON_SIZE }); + this._userButton.set_child(this._userIcon); + + // Adiciona o botão no userBox + userBox.add(this._userButton); + + // Clique no avatar abre "Detalhes da conta" + this._userButton.connect('clicked', () => { + Util.spawnCommandLine("cinnamon-settings user"); + }); + + this._labelBox = new St.BoxLayout({ style_class: 'label-box', vertical: true, y_align: Clutter.ActorAlign.CENTER }); + this.userLabel = new St.Label({ style_class: 'user-label' }); + this.hostLabel = new St.Label({ style_class: 'host-label' }); + this._labelBox.add(this.userLabel); + this._labelBox.add(this.hostLabel); + userBox.add(this._labelBox); + + // Adiciona user info e spacer + this.sessionContainer.add_child(userBox); + this.sessionContainer.add_child(new St.BoxLayout({ x_expand: true })); // Spacer + + // Session buttons + this.sessionButtonsBox = new St.BoxLayout({ style_class: 'session-buttons-box', vertical: false, x_align: Clutter.ActorAlign.END, y_align: Clutter.ActorAlign.CENTER }); + this.sessionContainer.add_child(this.sessionButtonsBox); + + this._initSessionButtons(); + this._initToggles(); + this._initTextScaling(); + } + + // === Inicializa session buttons === + _initSessionButtons() { + const addBtn = (iconName, tooltip, callback) => { + let btn = new St.Button({ style_class: "system-button", reactive: true, can_focus: true, track_hover: true }); + let icon = new St.Icon({ icon_name: iconName, icon_type: St.IconType.SYMBOLIC, style_class: "system-status-icon" }); + btn.set_child(icon); + new Tooltips.Tooltip(btn, tooltip); + btn.connect("clicked", () => { + callback(); + this.menu.toggle(); + }); + this.sessionButtonsBox.add_child(btn); + }; + + // System Settings + addBtn("applications-system", _("Settings"), () => { + Util.spawnCommandLine("cinnamon-settings"); + }); + + // Lock + addBtn("system-lock-screen", _("Lock Screen"), () => { + let screensaver_file = Gio.file_new_for_path("/usr/bin/cinnamon-screensaver-command"); + if (screensaver_file.query_exists(null)) { + let ask = this._schemas.screensaver.get_boolean("ask-for-away-message"); + Util.spawnCommandLine(ask ? "cinnamon-screensaver-lock-dialog" : "cinnamon-screensaver-command --lock"); + } else { + this._screenSaverProxy.LockRemote(); + } + }); + + // Logout + addBtn("system-log-out", _("Log Out"), () => this._session.LogoutRemote(0)); + + // Shutdown + addBtn("system-shutdown", _("Shut Down"), () => this._session.ShutdownRemote()); + } + + // === Inicializa toggles do applet === + _initToggles() { + // Dark Mode (applet setting) + this.darkModeToggle = this._createToggle( + "weather-clear-night-symbolic", + _("Dark mode"), + this.settings, + "dark-mode", + (newValue) => this._setDarkMode(newValue) + ); + this._addToggleToGrid(this.darkModeToggle.actor); + + // Night Light (Gio.Settings) + this.nightLightToggle = this._createToggle( + "night-light-symbolic", + _("Night Light"), + this._schemas.color, + "night-light-enabled", + null, + "nightlight" + ); + this._addToggleToGrid(this.nightLightToggle.actor); + + // Prevent Sleep toggle + this.preventSleepToggle = this._createToggle( + "preferences-desktop-screensaver-symbolic", + _("Prevent Sleep"), + null, + null, + (active) => this._togglePreventSleep(active), + "power" + ); + this._addToggleToGrid(this.preventSleepToggle.actor); + + // Power Mode toggle (no settings key) + this.powerModeToggle = this._createToggle( + "power-profile-balanced", // ícone inicial + _("Power Mode"), + null, + null, + () => this._togglePowerMode(), + "power" + ); + this._addToggleToGrid(this.powerModeToggle.actor); + + // Inicializa estado ao iniciar + this._updatePowerModeIcon(); + } + + // === Cria toggle com container extra para botão === + _createToggle(iconName, labelText, settingsObj = null, settingsKey = null, onChange = null, settingsUri = null) { + let toggleBox = new St.BoxLayout({ vertical: true, style_class: "settings-toggle-box", x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, x_expand: false, y_expand: false }); + let buttonContainer = new St.BoxLayout({ style_class: "settings-toggle-icon-container", x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER }); + + let button = new St.Button({ style_class: "settings-toggle-button", reactive: true, can_focus: true, track_hover: true, toggle_mode: true, x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, x_expand: false, y_expand: false }); + let icon = new St.Icon({ icon_name: iconName, icon_type: St.IconType.SYMBOLIC, style_class: "settings-toggle-icon" }); + + button.set_child(icon); + buttonContainer.add_child(button); + toggleBox.add_child(buttonContainer); + + let label = new St.Label({ text: labelText, style_class: "settings-toggle-label", x_align: Clutter.ActorAlign.CENTER }); + toggleBox.add_child(label); + + if (settingsObj && settingsKey) { + const updateState = () => { + let value = (settingsObj instanceof Gio.Settings) ? settingsObj.get_boolean(settingsKey) : settingsObj.getValue(settingsKey); + button.checked = value; + + if (onChange) onChange(value); + }; + + updateState(); + + settingsObj.connect(`changed::${settingsKey}`, updateState); + } + + // MOVE THE CLICKED HANDLER OUTSIDE THE if BLOCK + // So it works for both settings-based and custom toggles + button.connect("clicked", () => { + let newValue; + if (settingsObj && settingsKey) { + // Settings-based toggle + let current = (settingsObj instanceof Gio.Settings) ? settingsObj.get_boolean(settingsKey) : settingsObj.getValue(settingsKey); + newValue = !current; + if (settingsObj instanceof Gio.Settings) settingsObj.set_boolean(settingsKey, newValue); + else settingsObj.setValue(settingsKey, newValue); + } else { + // Custom toggle: manually flip + newValue = !button._active; + button._active = newValue; // store state manually + } + + if (onChange) onChange(newValue); + }); + + if (settingsUri) { + button.connect("button-press-event", (actor, event) => { + if (event.get_button() === 2) { // Middle-click + Util.spawnCommandLine(`cinnamon-settings ${settingsUri}`); + this.menu.toggle(); + return Clutter.EVENT_STOP; + } + return Clutter.EVENT_PROPAGATE; + }); + } + + return { actor: toggleBox, button, icon, label }; + } + + // === Adiciona toggle ao grid === + _addToggleToGrid(toggleActor) { + let gridLayout = this.prefsGrid.layout_manager; + gridLayout.attach(toggleActor, this.currentColumn, this.currentRow, 1, 1); + this.currentColumn++; + if (this.currentColumn >= this.maxTogglesPerRow) { + this.currentColumn = 0; + this.currentRow++; + } + } + + // === Inicializa slider de text scaling === + _initTextScaling() { + const FACTORS = [0.9, 1.0, 1.1, 1.2, 1.3]; + const MAX_INDEX = FACTORS.length - 1; + + let currentFactor = this._schemas.interface.get_double("text-scaling-factor"); + let idx = FACTORS.indexOf(currentFactor); + if (idx < 0) idx = 1; + + // Container do slider + let fakeSlider = new St.BoxLayout({ + style_class: "fake-slider", + vertical: false, + x_expand: true, + y_align: Clutter.ActorAlign.CENTER, + x_align: Clutter.ActorAlign.FILL + }); + + // Track em grid + let track = new St.Widget({ + style_class: "fake-slider-track", + x_expand: true, + y_expand: true, + layout_manager: new Clutter.GridLayout() + }); + let grid = track.layout_manager; + grid.set_row_homogeneous(true); + grid.set_column_homogeneous(true); + + // Fill do slider + let fill = new St.BoxLayout({ style_class: "fake-slider-fill" }); + + // Atualiza fill conforme índice + const updateFakeSlider = (idx) => { + track.remove_all_children(); + + let weight = (idx + 1); + + // Fill ocupa "weight" colunas + track.add_child(fill); + grid.attach(fill, 0, 0, weight, 1); + + // Spacer ocupa o resto + if (weight < FACTORS.length) { + let spacer = new St.BoxLayout({ x_expand: true }); + track.add_child(spacer); + grid.attach(spacer, weight, 0, FACTORS.length - weight, 1); + } + + // === Marcador fixo no 1.0 === + let markerIndex = FACTORS.indexOf(1.0); + if (markerIndex >= 0) { + let marker = new St.BoxLayout({ + style_class: "fake-slider-marker", + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.END + }); + track.add_child(marker); + grid.attach(marker, markerIndex, 0, 1, 1); + } + }; + + fakeSlider.add_child(track); + + // Função para alterar escala + const setScale = (newIdx) => { + newIdx = Math.max(0, Math.min(MAX_INDEX, newIdx)); + idx = newIdx; + let factor = FACTORS[idx]; + this._schemas.interface.set_double("text-scaling-factor", factor); + updateFakeSlider(idx); + }; + + // Escuta mudanças externas do text-scaling-factor + this._schemas.interface.connect("changed::text-scaling-factor", () => { + let f = this._schemas.interface.get_double("text-scaling-factor"); + let i = FACTORS.indexOf(f); + if (i >= 0) { + idx = i; + updateFakeSlider(idx); + } + }); + + // Botões de menos/mais + let minusBtn = new St.Button({ style_class: "system-button", reactive: true, can_focus: true, track_hover: true }); + minusBtn.set_child(new St.Icon({ icon_name: 'format-text-size-decrease-symbolic', icon_type: St.IconType.SYMBOLIC, style_class: "system-status-icon" })); + minusBtn.connect("clicked", () => setScale(idx - 1)); + + let plusBtn = new St.Button({ style_class: "system-button icon-large", reactive: true, can_focus: true, track_hover: true }); + plusBtn.set_child(new St.Icon({ icon_name: 'format-text-size-increase-symbolic', icon_type: St.IconType.SYMBOLIC, style_class: "system-status-icon" })); + plusBtn.connect("clicked", () => setScale(idx + 1)); + + // Container final + let scalingContainer = new St.BoxLayout({ style_class: "scaling-container", vertical: false, x_expand: true }); + scalingContainer.add_child(minusBtn); + scalingContainer.add_child(fakeSlider); + scalingContainer.add_child(plusBtn); + + this.interfaceSection.actor.add_child(scalingContainer); + + updateFakeSlider(idx); + } + + _populateThemeOptions() { + let themes = {}; + + const readThemesFromDir = (dir) => { + try { + let file = Gio.file_new_for_path(dir); + let enumerator = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null); + + let info; + while ((info = enumerator.next_file(null))) { + let themeName = info.get_name(); + if (info.get_file_type() === Gio.FileType.DIRECTORY) { + themes[themeName] = themeName; // Use theme name as both key and value + } + } + } catch (e) { + log(`Error reading themes from ${dir}: ${e.message}`); + } + }; + + // Read themes from both system and user directories + readThemesFromDir('/usr/share/themes'); + readThemesFromDir(GLib.get_home_dir() + '/.themes'); + + // Clear existing options in the ComboBox (assuming you have a ComboBox defined) + this._clearComboBoxOptions(); + + // Add new options to the ComboBox + for (let theme in themes) { + this._addComboBoxOption(theme, theme); // Add each theme to the ComboBox + } + + // Log found themes + if (Object.keys(themes).length === 0) { + log("No themes found."); + } else { + log(`Found themes: ${JSON.stringify(themes)}`); + } + + // Set the options for light and dark themes + this.settings.setOptions("light-theme", themes); + this.settings.setOptions("dark-theme", themes); + } + + _clearComboBoxOptions() { + // Clear the ComboBox options + // Assuming you have a reference to your ComboBox, for example: + if (this.lightThemeComboBox) { + this.lightThemeComboBox.remove_all(); + } + if (this.darkThemeComboBox) { + this.darkThemeComboBox.remove_all(); + } + } + + _addComboBoxOption(value, label) { + // Add an option to the ComboBox + if (this.lightThemeComboBox) { + this.lightThemeComboBox.add_option(label, value); + } + if (this.darkThemeComboBox) { + this.darkThemeComboBox.add_option(label, value); + } + } + + // === Dark mode === + _setDarkMode(dark) { + let theme = dark ? this._darkTheme : this._lightTheme; + let colorScheme = dark ? "prefer-dark" : "default"; + + this._schemas.gtk.set_string("gtk-theme", theme); + this._schemas.cinnamon.set_string("gtk-theme", theme); + this._schemas.portal.set_string("color-scheme", colorScheme); + + this._darkMode = dark; + global.log(dark); + global.log(theme); + + this._populateThemeOptions(); + } + + // === Toggle Prevent Sleep === + _togglePreventSleep(active) { + if (active) { + // Activate prevent sleep + this.sessionProxy.InhibitRemote( + "inhibit@cinnamon.org", + 0, + "prevent system sleep and suspension", + INHIBIT_SLEEP_FLAG, + Lang.bind(this, function(cookie) { + this.sessionCookie = cookie; + global.log("Prevent sleep activated, cookie: " + cookie); + // Ensure UI reflects the active state + this.preventSleepToggle.button.checked = true; + }) + ); + } else if (this.sessionCookie) { + // Deactivate prevent sleep + this.sessionProxy.UninhibitRemote( + this.sessionCookie, + Lang.bind(this, function() { + global.log("Prevent sleep deactivated"); + this.sessionCookie = null; + // Ensure UI reflects the inactive state + this.preventSleepToggle.button.checked = false; + }) + ); + } else { + // No cookie to uninhibit, just update UI + this.preventSleepToggle.button.checked = false; + } + } + + // === Keybinding === + _setKeybinding() { + if (this.keybindingId) { + Main.keybindingManager.removeHotKey("user-applet-open-" + this.instance_id); + } + Main.keybindingManager.addHotKey("user-applet-open-" + this.instance_id, this.keyOpen, () => this._openMenu()); + } + + _openMenu() { this.menu.toggle(); this._updatePowerModeIcon(); } + + // === Atualiza labels e avatar === + _updateLabels() { + this.set_applet_label(""); + + if (this.display_name) { + // === UserBox === + this.userLabel.set_text(this._user.get_real_name()); + this.hostLabel.set_text(`${GLib.get_user_name()}@${GLib.get_host_name()}`); + this._labelBox.show(); + } else { + // === UserBox === + this.userLabel.set_text(""); + this.hostLabel.set_text(""); + this._labelBox.hide(); + } + } + + _onUserChanged() { + if (this._user && this._user.is_loaded) { + this.set_applet_tooltip(this._user.get_real_name()); + + if (this.display_name) { + let hostname = GLib.get_host_name(); + this.hostLabel.set_text(`${GLib.get_user_name()}@${hostname}`); + this.userLabel.set_text(this._user.get_real_name()); + } + + this._userIcon.update(); + this._updateLabels(); + } + } + + // Alterna entre os modos + // Toggle + _togglePowerMode() { + this._getCurrentPowerMode((current) => { + let mode = this._powerModes[current] || this._powerModes["balanced"]; + + // Muda o modo de energia + Util.spawnCommandLine(`powerprofilesctl set ${mode.next}`); + this._updatePowerModeIcon(); + }); + } + + // Atualiza ícone externo + _updatePowerModeIcon() { + this._getCurrentPowerMode((current) => { + let mode = this._powerModes[current] || this._powerModes["balanced"]; + this.powerModeToggle.icon.icon_name = mode?.icon; + this.powerModeToggle.label.set_text(mode?.label); + let isPerformance = (current === "performance"); + this.powerModeToggle.button.checked = isPerformance; + }); + } + + // Obtém modo atual do powerprofilesctl + _getCurrentPowerMode(callback) { + try { + let [res, out, err, status] = GLib.spawn_command_line_sync("powerprofilesctl get"); + if (res && out) { + let mode = out.toString().trim(); + callback(mode); + } else { + callback("balanced"); // fallback + } + } catch(e) { + global.logError("Erro ao obter power mode: " + e); + callback("balanced"); + } + } + + on_applet_clicked() { this._openMenu(); } + + on_applet_removed_from_panel() { + // Clean up inhibit cookie if active - FIXED variable name + if (this.sessionCookie !== null && this.sessionProxy) { + try { + this.sessionProxy.UninhibitRemote(this.sessionCookie); + global.log("Cleaned up prevent sleep cookie: " + this.sessionCookie); + } catch(e) { + global.logError("Erro ao limpar inhibit cookie: " + e); + } + } + this.settings.finalize(); + } +} + +function main(metadata, orientation, panel_height, instance_id) { + return new CinnamonUserApplet(orientation, panel_height, instance_id); +} \ No newline at end of file diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/icon.png b/quick-settings@celiopy/files/quick-settings@celiopy/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f2db0adcf1281717f0d292c943b47797e2f8bcf9 GIT binary patch literal 591 zcmeAS@N?(olHy`uVBq!ia0vp^5+KY0Bp8m$B&h%?g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWO>_45WdofcQhU(|I6+ zy~NYkmHj0rpMXAtTvpyz1_nksPZ!4!jq}NW&Ns*!JQPSzNKCk~`@B6Y({J5Ui%+kGnZx4y#J18e9C<# z#Q&sM?BqRa;5PZTPm{hxiG+m2*1)@8oF*)ceS4?<-6C6!vOo6AubupG@@&CJ?vRJ# z-8^^gjsMl(-||%6oO_aC{XV0gSGf%Sr6*L>e$*{GV8A8aVPIhJh)Y|Hjg75M-ahci z4NIV*5fb;ly|3tB7Ik8?L&rzg6UHHdjTNod-Id|SV(Z;SpWjzGe4ya|{kcECmxpv7 zZ~*E(xVyc;@vobNUGBwjS zFt9Q(NY#u5Mi`QY-29Zxv`RD$#+FtlmJkh}g1PSjHE6(XD9OxCEiOsSEkM&_Vh%JO VqNh2{1(aMEJYD@<);T3K0RSzd(f$Ac literal 0 HcmV?d00001 diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-decrease-symbolic.svg b/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-decrease-symbolic.svg new file mode 100644 index 00000000000..3bce77be1c2 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-decrease-symbolic.svg @@ -0,0 +1,43 @@ + + + + + + diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-increase-symbolic.svg b/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-increase-symbolic.svg new file mode 100644 index 00000000000..1d06800b097 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/icons/format-text-size-increase-symbolic.svg @@ -0,0 +1,43 @@ + + + + + + diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/icons/user-menu-symbolic.svg b/quick-settings@celiopy/files/quick-settings@celiopy/icons/user-menu-symbolic.svg new file mode 100644 index 00000000000..39b9f4a1344 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/icons/user-menu-symbolic.svg @@ -0,0 +1,19 @@ + + + + + + diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/metadata.json b/quick-settings@celiopy/files/quick-settings@celiopy/metadata.json new file mode 100644 index 00000000000..8d311e43add --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/metadata.json @@ -0,0 +1,6 @@ +{ + "description": "An applet to access your account details, switch users and quickly logout or power off the computer", + "uuid": "quick-settings@celiopy", + "name": "Quick Settings", + "max-instances": 1 +} diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po new file mode 100644 index 00000000000..cb9c64655e6 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po @@ -0,0 +1,72 @@ +msgid "" +msgstr "" +"Project-Id-Version: user@celiopy 1.0.0\n" +"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-applets/issues\n" +"POT-Creation-Date: 2025-09-10 23:22-0300\n" +"PO-Revision-Date: 2025-09-10 23:50-0300\n" +"Last-Translator: Você \n" +"Language-Team: pt_BR <>\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: applet.js:187 +msgid "Settings" +msgstr "Configurações" + +#: applet.js:192 +msgid "Lock Screen" +msgstr "Bloquear tela" + +#: applet.js:203 +msgid "Log Out" +msgstr "Encerrar sessão" + +#: applet.js:206 +msgid "Shut Down" +msgstr "Desligar" + +#: applet.js:214 +msgid "Dark mode" +msgstr "Modo escuro" + +#: applet.js:224 +msgid "Night Light" +msgstr "Luz noturna" + +#: applet.js:233 +msgid "Prevent Sleep" +msgstr "Impedir suspensão" + +#: applet.js:250 +msgid "Power Mode" +msgstr "Modo de Energia" + +msgid "Power Saver" +msgstr "Economia de Energia" + +msgid "Balanced" +msgstr "Equilibrado" + +msgid "Performance" +msgstr "Desempenho" + +# prefs +msgid "Theme" +msgstr "Tema" + +msgid "Enable dark mode" +msgstr "Ativar modo escuro" + +msgid "Select a light theme" +msgstr "Selecione um tema claro" + +msgid "Select a dark theme" +msgstr "Selecione um tema escuro" + +msgid "Show menu" +msgstr "Mostrar o menu" + +msgid "Set keybinding(s) to show menu." +msgstr "Defina atalho(s) para mostrar o menu." diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot b/quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot new file mode 100644 index 00000000000..8c799f5cb6e --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot @@ -0,0 +1,78 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-09-11 02:20-0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: applet.js:194 +msgid "Settings" +msgstr "" + +#: applet.js:199 +msgid "Lock Screen" +msgstr "" + +#: applet.js:210 +msgid "Log Out" +msgstr "" + +#: applet.js:213 +msgid "Shut Down" +msgstr "" + +#: applet.js:221 +msgid "Dark mode" +msgstr "" + +#: applet.js:231 +msgid "Night Light" +msgstr "" + +#: applet.js:240 +msgid "Prevent Sleep" +msgstr "" + +#: applet.js:250 +msgid "Power Mode" +msgstr "" + +msgid "Power Saver" +msgstr "" + +msgid "Balanced" +msgstr "" + +msgid "Performance" +msgstr "" + +# prefs +msgid "Theme" +msgstr "" + +msgid "Enable dark mode" +msgstr "" + +msgid "Select a light theme" +msgstr "" + +msgid "Select a dark theme" +msgstr "" + +msgid "Show menu" +msgstr "" + +msgid "Set keybinding(s) to show menu." +msgstr "" diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json new file mode 100644 index 00000000000..b8fca0fa6b3 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json @@ -0,0 +1,58 @@ +{ + "section1": { + "type": "section", + "description": "Settings" + }, + "display-name": { + "type": "switch", + "default": false, + "description": "Display user name on panel" + }, + "display-image": { + "type": "switch", + "default": false, + "description": "Display the user image on the panel" + }, + "section2": { + "type": "section", + "description": "Theme" + }, + "dark-mode": { + "type": "switch", + "default": false, + "description": "Enable dark mode" + }, + "light-theme": { + "type": "combobox", + "default": "Adwaita", + "options": { + "Adwaita": "Adwaita", + "Arc": "Arc", + "Numix": "Numix", + "Materia": "Materia", + "Materia-light-compact": "Materia Light Compact" + }, + "description": "Select a light theme" + }, + "dark-theme": { + "type": "combobox", + "default": "Adwaita-dark", + "options": { + "Adwaita-dark": "Adwaita Dark", + "Arc-Dark": "Arc Dark", + "Numix-Dark": "Numix Dark", + "Materia-dark-compact": "Materia Dark Compact" + }, + "description": "Select a dark theme" + }, + "section3": { + "type": "section", + "description": "Keyboard shortcuts" + }, + "keyOpen": { + "type": "keybinding", + "description": "Show menu", + "default": "s", + "tooltip": "Set keybinding(s) to show menu." + } +} diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css new file mode 100644 index 00000000000..cffbce10736 --- /dev/null +++ b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css @@ -0,0 +1,121 @@ +/* Session section layout */ +.session-container { + padding: 6px 12px; + spacing: 12px; +} + +.session-spacer { + min-width: 0; + min-height: 0; +} + +.session-buttons-box { + spacing: 6px; +} + +/* User box styles */ +.user-box { + spacing: 6px; +} + +.user-icon { + border-radius: 999px; + border: 0; +} + +.user-label { + font-size: 9pt; + font-weight: bold; +} + +.host-label { + font-size: 8pt; + color: rgb(150, 150, 150); +} + +/* Session button styles */ +.system-button { + border-radius: 9999px; + padding: 10px; + background-color: rgba(160, 160, 160, 0.08); + transition: background-color 150ms ease; +} + +.system-button:hover { + background-color: rgba(160, 160, 160, 0.18); +} + +.system-button:active { + background-color: rgba(160, 160, 160, 0.28); +} + +.system-button .system-status-icon { + icon-size: 14px; +} + +/* Grid Layout */ +.prefs-grid-container { + padding: 12px 6px; +} + +.settings-toggle-box { + spacing: 8px; + width: 100%; + min-width: 80px; + max-width: 100px; + margin: 0 auto; +} + +.settings-toggle-button { + border-radius: 10px; + background-color: rgba(160, 160, 160, 0.1); + padding: 12px; +} + +.settings-toggle-button:checked, +.settings-toggle-button.active { + background-color: #1c71d8; +} + +.settings-toggle-icon { + icon-size: 20px; +} + +.settings-toggle-label { + font-size: 9pt; + max-width: 80px; + text-align: center; + margin: 0 auto; +} + +/* Scaling slider */ +/* Container da seção de scaling */ +.scaling-container { + spacing: 8px; /* Espaço entre os elementos */ + margin: 6px; +} + +/* CSS */ +.fake-slider-track { + background-color: rgba(100,100,100,0.3); + border-radius: 4px; + height: 8px; +} + +.fake-slider-fill { + background-color: #5a9bd5; + border-radius: 4px; + height: 8px; +} + +.fake-slider-marker { + background-color: #ddffff; /* cor do marcador */ + width: 2px !important; + border-radius: 1px; +} + +.panel-icon-bin{ + padding: 0 !important; + margin: 0 !important; + spacing: 0 !important; +} \ No newline at end of file diff --git a/quick-settings@celiopy/info.json b/quick-settings@celiopy/info.json new file mode 100644 index 00000000000..ed7a1e9e769 --- /dev/null +++ b/quick-settings@celiopy/info.json @@ -0,0 +1 @@ +{ "author": "celiopy" } diff --git a/quick-settings@celiopy/screenshot.png b/quick-settings@celiopy/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c597f0f69903e5bd0fc4c45242547722538db6d3 GIT binary patch literal 21647 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 Date: Sun, 14 Sep 2025 14:00:16 -0300 Subject: [PATCH 4/8] Fix leftovers... --- quick-settings@celiopy/files/quick-settings@celiopy/applet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js index e4b118f3507..d471f1dde06 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -16,7 +16,7 @@ const Clutter = imports.gi.Clutter; const Gettext = imports.gettext; const Slider = imports.ui.slider; -const UUID = 'user@celiopy'; +const UUID = 'quick-settings@celiopy'; const APPLET_DIR = imports.ui.appletManager.appletMeta[UUID].path; const DIALOG_ICON_SIZE = 32; @@ -650,4 +650,4 @@ class CinnamonUserApplet extends Applet.TextIconApplet { function main(metadata, orientation, panel_height, instance_id) { return new CinnamonUserApplet(orientation, panel_height, instance_id); -} \ No newline at end of file +} From e315bc2dc63555d01c0bd534f947c601531deceb Mon Sep 17 00:00:00 2001 From: Celio Date: Sun, 14 Sep 2025 18:56:15 -0300 Subject: [PATCH 5/8] Replaced Util.spawnCommandLine and sync calls with async and Gio.Subprocess --- .../files/quick-settings@celiopy/applet.js | 123 ++++++++++++++---- 1 file changed, 95 insertions(+), 28 deletions(-) diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js index d471f1dde06..c4cb3042fea 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -1,7 +1,6 @@ const Applet = imports.ui.applet; const St = imports.gi.St; const PopupMenu = imports.ui.popupMenu; -const Util = imports.misc.util; const Lang = imports.lang; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; @@ -34,7 +33,8 @@ class CinnamonUserApplet extends Applet.TextIconApplet { constructor(orientation, panel_height, instance_id) { super(orientation, panel_height, instance_id); this.setAllowedLayout(Applet.AllowedLayout.BOTH); - + + this._signals = []; this.sessionCookie = null; this.set_applet_icon_symbolic_name('user-menu-symbolic'); @@ -49,6 +49,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // Inicializa schemas, bindings, UI e toggles this._initSchemas(); this._initUI(orientation); + this._initPowerProfiles(); // Métodos iniciais this._onUserChanged(); @@ -75,6 +76,29 @@ class CinnamonUserApplet extends Applet.TextIconApplet { }; } + _initPowerProfiles() { + this._powerProxy = new Gio.DBusProxy.new_for_bus_sync( + Gio.BusType.SYSTEM, + Gio.DBusProxyFlags.NONE, + null, + "net.hadess.PowerProfiles", + "/net/hadess/PowerProfiles", + "net.hadess.PowerProfiles", + null + ); + + // Atualiza inicialmente + this._updatePowerModeIcon(); + + // Escuta mudanças + this._signals.push({ + obj: this._powerProxy, + id: this._powerProxy.connect("g-properties-changed", () => { + this._updatePowerModeIcon(); + }) + }); + } + // === Inicializa UI do menu e painel === _initUI(orientation) { // Sessão @@ -131,8 +155,15 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // User info this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); - this._user.connect('notify::is-loaded', () => this._onUserChanged()); - this._user.connect('changed', () => this._onUserChanged()); + this._signals.push({ + obj: this._user, + id: this._user.connect('notify::is-loaded', this._onUserChanged.bind(this)) + }); + + this._signals.push({ + obj: this._user, + id: this._user.connect('changed', this._onUserChanged.bind(this)) + }); let userBox = new St.BoxLayout({ style_class: 'user-box', vertical: false, y_expand: false }); // Avatar dentro de botão @@ -152,7 +183,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // Clique no avatar abre "Detalhes da conta" this._userButton.connect('clicked', () => { - Util.spawnCommandLine("cinnamon-settings user"); + GLib.spawn_command_line_async("cinnamon-settings user"); }); this._labelBox = new St.BoxLayout({ style_class: 'label-box', vertical: true, y_align: Clutter.ActorAlign.CENTER }); @@ -191,7 +222,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // System Settings addBtn("applications-system", _("Settings"), () => { - Util.spawnCommandLine("cinnamon-settings"); + GLib.spawn_command_line_async("cinnamon-settings"); }); // Lock @@ -199,7 +230,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { let screensaver_file = Gio.file_new_for_path("/usr/bin/cinnamon-screensaver-command"); if (screensaver_file.query_exists(null)) { let ask = this._schemas.screensaver.get_boolean("ask-for-away-message"); - Util.spawnCommandLine(ask ? "cinnamon-screensaver-lock-dialog" : "cinnamon-screensaver-command --lock"); + GLib.spawn_command_line_async(ask ? "cinnamon-screensaver-lock-dialog" : "cinnamon-screensaver-command --lock"); } else { this._screenSaverProxy.LockRemote(); } @@ -278,7 +309,9 @@ class CinnamonUserApplet extends Applet.TextIconApplet { if (settingsObj && settingsKey) { const updateState = () => { - let value = (settingsObj instanceof Gio.Settings) ? settingsObj.get_boolean(settingsKey) : settingsObj.getValue(settingsKey); + let value = (settingsObj instanceof Gio.Settings) + ? settingsObj.get_boolean(settingsKey) + : settingsObj.getValue(settingsKey); button.checked = value; if (onChange) onChange(value); @@ -286,7 +319,10 @@ class CinnamonUserApplet extends Applet.TextIconApplet { updateState(); - settingsObj.connect(`changed::${settingsKey}`, updateState); + this._signals.push({ + obj: settingsObj, + id: settingsObj.connect(`changed::${settingsKey}`, updateState) + }); } // MOVE THE CLICKED HANDLER OUTSIDE THE if BLOCK @@ -311,7 +347,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { if (settingsUri) { button.connect("button-press-event", (actor, event) => { if (event.get_button() === 2) { // Middle-click - Util.spawnCommandLine(`cinnamon-settings ${settingsUri}`); + GLib.spawn_command_line_async(`cinnamon-settings ${settingsUri}`); this.menu.toggle(); return Clutter.EVENT_STOP; } @@ -408,13 +444,16 @@ class CinnamonUserApplet extends Applet.TextIconApplet { }; // Escuta mudanças externas do text-scaling-factor - this._schemas.interface.connect("changed::text-scaling-factor", () => { - let f = this._schemas.interface.get_double("text-scaling-factor"); - let i = FACTORS.indexOf(f); - if (i >= 0) { - idx = i; - updateFakeSlider(idx); - } + this._signals.push({ + obj: this._schemas.interface, + id: this._schemas.interface.connect("changed::text-scaling-factor", () => { + let f = this._schemas.interface.get_double("text-scaling-factor"); + let i = FACTORS.indexOf(f); + if (i >= 0) { + idx = i; + updateFakeSlider(idx); + } + }) }); // Botões de menos/mais @@ -559,7 +598,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { Main.keybindingManager.addHotKey("user-applet-open-" + this.instance_id, this.keyOpen, () => this._openMenu()); } - _openMenu() { this.menu.toggle(); this._updatePowerModeIcon(); } + _openMenu() { this.menu.toggle(); } // === Atualiza labels e avatar === _updateLabels() { @@ -600,7 +639,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { let mode = this._powerModes[current] || this._powerModes["balanced"]; // Muda o modo de energia - Util.spawnCommandLine(`powerprofilesctl set ${mode.next}`); + GLib.spawn_command_line_async(`powerprofilesctl set ${mode.next}`); this._updatePowerModeIcon(); }); } @@ -619,15 +658,27 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // Obtém modo atual do powerprofilesctl _getCurrentPowerMode(callback) { try { - let [res, out, err, status] = GLib.spawn_command_line_sync("powerprofilesctl get"); - if (res && out) { - let mode = out.toString().trim(); - callback(mode); - } else { - callback("balanced"); // fallback - } + let proc = new Gio.Subprocess({ + argv: ["powerprofilesctl", "get"], + flags: Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE + }); + + proc.init(null); + proc.communicate_utf8_async(null, null, (proc, res) => { + try { + let [, stdout, stderr] = proc.communicate_utf8_finish(res); + if (stdout && stdout.trim().length > 0) { + callback(stdout.trim()); + } else { + callback("balanced"); // fallback + } + } catch (e) { + global.logError("Erro ao processar power mode: " + e); + callback("balanced"); + } + }); } catch(e) { - global.logError("Erro ao obter power mode: " + e); + global.logError("Erro ao iniciar subprocesso: " + e); callback("balanced"); } } @@ -635,7 +686,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { on_applet_clicked() { this._openMenu(); } on_applet_removed_from_panel() { - // Clean up inhibit cookie if active - FIXED variable name + // Clean up inhibit cookie if (this.sessionCookie !== null && this.sessionProxy) { try { this.sessionProxy.UninhibitRemote(this.sessionCookie); @@ -644,7 +695,23 @@ class CinnamonUserApplet extends Applet.TextIconApplet { global.logError("Erro ao limpar inhibit cookie: " + e); } } + + // Desconectar todos os sinais registrados + this._signals.forEach(sig => { + try { sig.obj.disconnect(sig.id); } catch(e) {} + }); + this._signals = null; + + // Remover keybinding + Main.keybindingManager.removeHotKey("user-applet-open-" + this.instance_id); + + // Finalizar settings this.settings.finalize(); + + // Liberar proxies + this._powerProxy = null; + this.sessionProxy = null; + this._screenSaverProxy = null; } } From c420751fa3f017c3a9d0abcac39071d12ff65022 Mon Sep 17 00:00:00 2001 From: Celio Date: Mon, 15 Sep 2025 02:36:43 -0300 Subject: [PATCH 6/8] Redone logic behind button generation and isolated theming mechanism --- .../files/quick-settings@celiopy/applet.js | 589 +++++++++++------- .../files/quick-settings@celiopy/po/pt_BR.po | 21 +- ...celiopy.pot => quick-settings@celiopy.pot} | 21 +- .../settings-schema.json | 12 +- .../quick-settings@celiopy/stylesheet.css | 3 - 5 files changed, 376 insertions(+), 270 deletions(-) rename quick-settings@celiopy/files/quick-settings@celiopy/po/{user@celiopy.pot => quick-settings@celiopy.pot} (81%) diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js index c4cb3042fea..d5fa44c4e41 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -1,19 +1,14 @@ const Applet = imports.ui.applet; -const St = imports.gi.St; +const { St, GLib, Gio, AccountsService, Clutter } = imports.gi; const PopupMenu = imports.ui.popupMenu; -const Lang = imports.lang; -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const AccountsService = imports.gi.AccountsService; -const GnomeSession = imports.misc.gnomeSession; -const ScreenSaver = imports.misc.screenSaver; const Settings = imports.ui.settings; const UserWidget = imports.ui.userWidget; const Main = imports.ui.main; const Tooltips = imports.ui.tooltips; -const Clutter = imports.gi.Clutter; +const GnomeSession = imports.misc.gnomeSession; +const ScreenSaver = imports.misc.screenSaver; const Gettext = imports.gettext; -const Slider = imports.ui.slider; +const Signals = imports.signals; // Add this line const UUID = 'quick-settings@celiopy'; const APPLET_DIR = imports.ui.appletManager.appletMeta[UUID].path; @@ -23,12 +18,303 @@ const INHIBIT_IDLE_FLAG = 8; const INHIBIT_SLEEP_FLAG = 4; // l10n/translation support -Gettext.bindtextdomain(UUID, GLib.get_home_dir() + "/.local/share/locale"); +Gettext.bindtextdomain(UUID, APPLET_DIR + "/locale"); function _(str) { return Gettext.dgettext(UUID, str); } +class ThemeManager { + constructor(schemas, settings) { + this._schemas = schemas; + this._settings = settings; + + this._themeDirs = [ + '/usr/share/themes', + GLib.get_home_dir() + '/.themes', + GLib.get_home_dir() + '/.local/share/themes', + ]; + this._monitors = []; + + // Initialize themes + this._lightTheme = settings.getValue("light-theme"); + this._darkTheme = settings.getValue("dark-theme"); + + // Use portal color-scheme as the single source of truth + this._darkMode = this._getDarkModeFromPortal(); + + // Bind to settings changes + this._bindings = []; + this._bindSettings(); + + // Monitor theme directory changes + this._themeDirs.forEach(dir => this._monitorDir(dir)); + + this._populateThemeOptions(); + } + + _getDarkModeFromPortal() { + const colorScheme = this._schemas.portal.get_string("color-scheme"); + return colorScheme === "prefer-dark"; + } + + _bindSettings() { + // Listen to portal color-scheme changes (our single source of truth) + this._bindings.push({ + obj: this._schemas.portal, + id: this._schemas.portal.connect("changed::color-scheme", () => { + this._darkMode = this._getDarkModeFromPortal(); + this._applyCurrentTheme(); + + // Emit a signal so the applet can update its UI + this.emit('dark-mode-changed', this._darkMode); + }) + }); + + // Bind light-theme setting + this._bindings.push({ + obj: this._settings, + id: this._settings.connect("changed::light-theme", () => { + this._lightTheme = this._settings.getValue("light-theme"); + this._applyCurrentTheme(); + }) + }); + + // Bind dark-theme setting + this._bindings.push({ + obj: this._settings, + id: this._settings.connect("changed::dark-theme", () => { + this._darkTheme = this._settings.getValue("dark-theme"); + this._applyCurrentTheme(); + }) + }); + } + + _applyCurrentTheme() { + let theme = this._darkMode ? this._darkTheme : this._lightTheme; + + // Apply the theme to all schemas + this._schemas.gtk.set_string("gtk-theme", theme); + this._schemas.cinnamon.set_string("gtk-theme", theme); + } + + _monitorDir(dirPath) { + try { + let file = Gio.file_new_for_path(dirPath); + let monitor = file.monitor_directory(Gio.FileMonitorFlags.NONE, null); + monitor.connect('changed', () => { + this._populateThemeOptions(); + }); + this._monitors.push(monitor); + } catch(e) { + global.logError(`Failed to monitor directory ${dirPath}: ${e}`); + } + } + + isDarkMode() { + return this._darkMode; + } + + setDarkMode(dark) { + // Update the portal setting (our single source of truth) + let colorScheme = dark ? "prefer-dark" : "default"; + this._schemas.portal.set_string("color-scheme", colorScheme); + + // Note: The actual _darkMode update will happen in the + // changed::color-scheme signal handler + } + + _populateThemeOptions() { + let themes = {}; + + const readThemesFromDir = (dir) => { + try { + let file = Gio.file_new_for_path(dir); + let enumerator = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null); + + let info; + while ((info = enumerator.next_file(null))) { + let themeName = info.get_name(); + if (info.get_file_type() === Gio.FileType.DIRECTORY) { + themes[themeName] = themeName; + } + } + } catch (e) { + global.log(`Error reading themes from ${dir}: ${e.message}`); + } + }; + + this._themeDirs.forEach(dir => readThemesFromDir(dir)); + + this._settings.setOptions("light-theme", themes); + this._settings.setOptions("dark-theme", themes); + } + + // Clean up method to disconnect all signals + destroy() { + // Disconnect all setting bindings + this._bindings.forEach(binding => { + try { + binding.obj.disconnect(binding.id); + } catch (e) { + global.logError(`Error disconnecting binding: ${e}`); + } + }); + this._bindings = []; + + // Stop all directory monitors + this._monitors.forEach(monitor => { + try { + monitor.cancel(); + } catch (e) { + global.logError(`Error canceling monitor: ${e}`); + } + }); + this._monitors = []; + } +} + +// Add signal support to ThemeManager +Signals.addSignalMethods(ThemeManager.prototype); + +const BASE_BUTTON_DEFAULT_PARAMS = Object.freeze({ + name: '', + description: '', + styleClass: 'settings-toggle-box', + reactive: true, + activatable: true, + withMenu: false, +}); + +class BaseButton { + constructor(applet, params) { + this.applet = applet; + params = Object.assign({}, BASE_BUTTON_DEFAULT_PARAMS, params); + + this._active = false; + + this.actor = new St.BoxLayout({ + style_class: params.styleClass, + reactive: params.reactive, + vertical: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + x_expand: false, + y_expand: false + }); + + this._container = new St.BoxLayout({ + style_class: 'settings-toggle-button', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + x_expand: false, + y_expand: false + }); + + this.actor._delegate = this; + this.name = params.name; + this.description = params.description; + + this.label = null; + this.icon = null; + + if (params.reactive) { + if (params.activatable || params.withMenu) { + this.actor.connect('button-press-event', (actor, event) => this._onButtonPressEvent(actor, event)); + this.actor.connect('key-press-event', (actor, event) => this._onKeyPressEvent(actor, event)); + } + } + } + + set active(value) { + this._active = value; + // Adiciona ou remove a classe CSS + if (value) this._container.add_style_class_name('active'); + else this._container.remove_style_class_name('active'); + } + + get active() { + return this._active; + } + + _onButtonPressEvent(actor, event) { + const button = event.get_button(); + + if (button === Clutter.BUTTON_PRIMARY) { + if (this.onLeftClick) this.onLeftClick(this._active); + return Clutter.EVENT_STOP; + } + else if (button === Clutter.BUTTON_MIDDLE) { + if (this.onMiddleClick) this.onMiddleClick(this._active); + return Clutter.EVENT_STOP; + } + else if (button === Clutter.BUTTON_SECONDARY) { + if (this.populateMenu && this.applet) this.applet.toggleContextMenu(this); + return Clutter.EVENT_STOP; + } + + return Clutter.EVENT_PROPAGATE; + } + + _onKeyPressEvent(actor, event) { + const symbol = event.get_key_symbol(); + if ((symbol === Clutter.KEY_space || symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) && this.onLeftClick) { + this.onLeftClick(this._active); + return Clutter.EVENT_STOP; + } + return Clutter.EVENT_PROPAGATE; + } + + addIcon(iconSize, iconName='', gicon=null, symbolic=false) { + if (this.icon) return; + + const params = { icon_size: iconSize }; + if (iconName) params.icon_name = iconName; + else if (gicon) params.gicon = gicon; + params.icon_type = symbolic ? St.IconType.SYMBOLIC : St.IconType.FULLCOLOR; + + this.icon = new St.Icon(params); + + this._container.add_actor(this.icon); + this.actor.add_actor(this._container); + + this.buttonActor = this.icon; + } + + addLabel(label='', styleClass=null) { + if (this.label) return; + this.label = new St.Label({ + text: label, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + if (styleClass) this.label.set_style_class_name(styleClass); + this.actor.add_actor(this.label); + } + + destroy(actorDestroySignal=false) { + if (this.label) this.label.destroy(); + if (this.icon) this.icon.destroy(); + if (!actorDestroySignal) this.actor.destroy(); + + delete this.actor._delegate; + delete this.actor; + delete this.label; + delete this.icon; + } +} + +class ToggleButton extends BaseButton { + constructor(applet, iconName, labelText) { + super(applet, { name: labelText, styleClass: 'settings-toggle-box' }); + this.addIcon(24, iconName, null, true); + this.addLabel(labelText, 'settings-toggle-label'); + this.onLeftClick = null; + this.onMiddleClick = null; + this.active = false; // estado inicial + } +} + class CinnamonUserApplet extends Applet.TextIconApplet { constructor(orientation, panel_height, instance_id) { super(orientation, panel_height, instance_id); @@ -48,20 +334,20 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // Inicializa schemas, bindings, UI e toggles this._initSchemas(); + this.themeManager = new ThemeManager(this._schemas, this.settings); this._initUI(orientation); this._initPowerProfiles(); // Métodos iniciais this._onUserChanged(); this._setKeybinding(); + } // === Inicializa schemas e bindings === _initSchemas() { // Schemas do applet this.settings = new Settings.AppletSettings(this, UUID, this.instance_id); - this.settings.bind("light-theme", "_lightTheme"); - this.settings.bind("dark-theme", "_darkTheme"); this.settings.bind("keyOpen", "keyOpen", () => this._setKeybinding()); this.settings.bind("display-name", "display_name", () => this._updateLabels()); @@ -103,14 +389,14 @@ class CinnamonUserApplet extends Applet.TextIconApplet { _initUI(orientation) { // Sessão this.sessionProxy = null; - this._session = new GnomeSession.SessionManager(Lang.bind(this, function(proxy, error) { + this._session = new GnomeSession.SessionManager((proxy, error) => { if (error) { global.logError("Error initializing session proxy: " + error.message); return; } this.sessionProxy = proxy; global.log("Session proxy initialized successfully"); - })); + }); this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); // Menu @@ -176,6 +462,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // Coloca o avatar dentro do botão this._userIcon = new UserWidget.Avatar(this._user, { iconSize: DIALOG_ICON_SIZE }); + this._userIcon.style = `icon-size: ${DIALOG_ICON_SIZE}px;`; this._userButton.set_child(this._userIcon); // Adiciona o botão no userBox @@ -245,128 +532,72 @@ class CinnamonUserApplet extends Applet.TextIconApplet { // === Inicializa toggles do applet === _initToggles() { - // Dark Mode (applet setting) - this.darkModeToggle = this._createToggle( - "weather-clear-night-symbolic", - _("Dark mode"), - this.settings, - "dark-mode", - (newValue) => this._setDarkMode(newValue) - ); + // Dark Mode + this.darkModeToggle = new ToggleButton(this, "weather-clear-night-symbolic", _("Dark mode")); + this.darkModeToggle.active = this.themeManager.isDarkMode(); + this.darkModeToggle.onLeftClick = () => { + // This will now update the portal setting, which will trigger the signal + this.themeManager.setDarkMode(!this.themeManager.isDarkMode()); + }; + this.darkModeToggle.onMiddleClick = () => { + GLib.spawn_command_line_async("cinnamon-settings themes"); + this._openMenu(); + } this._addToggleToGrid(this.darkModeToggle.actor); - // Night Light (Gio.Settings) - this.nightLightToggle = this._createToggle( - "night-light-symbolic", - _("Night Light"), - this._schemas.color, - "night-light-enabled", - null, - "nightlight" - ); + this.themeManager.connect('dark-mode-changed', (manager, darkMode) => { + this.darkModeToggle.active = darkMode; + }); + + // Night Light + this.nightLightToggle = new ToggleButton(this, "night-light-symbolic", _("Night Light")); + this.nightLightToggle.active = this._schemas.color.get_boolean("night-light-enabled"); + this.nightLightToggle.onLeftClick = () => { + this.nightLightToggle.active = !this.nightLightToggle.active; + this._schemas.color.set_boolean("night-light-enabled", this.nightLightToggle.active); + }; + this.nightLightToggle.onMiddleClick = () => { + GLib.spawn_command_line_async("cinnamon-settings nightlight"); + this._openMenu(); + }; this._addToggleToGrid(this.nightLightToggle.actor); + // Conecta para sincronizar mudanças externas + this._signals.push({ + obj: this._schemas.color, + id: this._schemas.color.connect("changed::night-light-enabled", (settings, key) => { + this.nightLightToggle.active = settings.get_boolean(key); + }) + }); + // Prevent Sleep toggle - this.preventSleepToggle = this._createToggle( - "preferences-desktop-screensaver-symbolic", - _("Prevent Sleep"), - null, - null, - (active) => this._togglePreventSleep(active), - "power" - ); + this.preventSleepToggle = new ToggleButton(this, "preferences-desktop-screensaver-symbolic", _("Prevent Sleep")); + this.preventSleepToggle.active = false; // estado inicial + this.preventSleepToggle.onLeftClick = () => { + this.preventSleepToggle.active = !this.preventSleepToggle.active; + this._togglePreventSleep(this.preventSleepToggle.active); + } + this.preventSleepToggle.onMiddleClick = () => { + GLib.spawn_command_line_async("cinnamon-settings power"); + this._openMenu() + } this._addToggleToGrid(this.preventSleepToggle.actor); - // Power Mode toggle (no settings key) - this.powerModeToggle = this._createToggle( - "power-profile-balanced", // ícone inicial - _("Power Mode"), - null, - null, - () => this._togglePowerMode(), - "power" - ); + // Power Mode toggle + this.powerModeToggle = new ToggleButton(this, "power-profile-balanced", _("Power Mode")); + this.powerModeToggle.onLeftClick = () => this._togglePowerMode(); + this.powerModeToggle.onMiddleClick = () => GLib.spawn_command_line_async("cinnamon-settings power"); this._addToggleToGrid(this.powerModeToggle.actor); - // Inicializa estado ao iniciar + // Inicializa estado ao iniciar (atualiza ícone e label conforme powerprofilesctl) this._updatePowerModeIcon(); } - // === Cria toggle com container extra para botão === - _createToggle(iconName, labelText, settingsObj = null, settingsKey = null, onChange = null, settingsUri = null) { - let toggleBox = new St.BoxLayout({ vertical: true, style_class: "settings-toggle-box", x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, x_expand: false, y_expand: false }); - let buttonContainer = new St.BoxLayout({ style_class: "settings-toggle-icon-container", x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER }); - - let button = new St.Button({ style_class: "settings-toggle-button", reactive: true, can_focus: true, track_hover: true, toggle_mode: true, x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, x_expand: false, y_expand: false }); - let icon = new St.Icon({ icon_name: iconName, icon_type: St.IconType.SYMBOLIC, style_class: "settings-toggle-icon" }); - - button.set_child(icon); - buttonContainer.add_child(button); - toggleBox.add_child(buttonContainer); - - let label = new St.Label({ text: labelText, style_class: "settings-toggle-label", x_align: Clutter.ActorAlign.CENTER }); - toggleBox.add_child(label); - - if (settingsObj && settingsKey) { - const updateState = () => { - let value = (settingsObj instanceof Gio.Settings) - ? settingsObj.get_boolean(settingsKey) - : settingsObj.getValue(settingsKey); - button.checked = value; - - if (onChange) onChange(value); - }; - - updateState(); - - this._signals.push({ - obj: settingsObj, - id: settingsObj.connect(`changed::${settingsKey}`, updateState) - }); - } - - // MOVE THE CLICKED HANDLER OUTSIDE THE if BLOCK - // So it works for both settings-based and custom toggles - button.connect("clicked", () => { - let newValue; - if (settingsObj && settingsKey) { - // Settings-based toggle - let current = (settingsObj instanceof Gio.Settings) ? settingsObj.get_boolean(settingsKey) : settingsObj.getValue(settingsKey); - newValue = !current; - if (settingsObj instanceof Gio.Settings) settingsObj.set_boolean(settingsKey, newValue); - else settingsObj.setValue(settingsKey, newValue); - } else { - // Custom toggle: manually flip - newValue = !button._active; - button._active = newValue; // store state manually - } - - if (onChange) onChange(newValue); - }); - - if (settingsUri) { - button.connect("button-press-event", (actor, event) => { - if (event.get_button() === 2) { // Middle-click - GLib.spawn_command_line_async(`cinnamon-settings ${settingsUri}`); - this.menu.toggle(); - return Clutter.EVENT_STOP; - } - return Clutter.EVENT_PROPAGATE; - }); - } - - return { actor: toggleBox, button, icon, label }; - } - // === Adiciona toggle ao grid === _addToggleToGrid(toggleActor) { - let gridLayout = this.prefsGrid.layout_manager; - gridLayout.attach(toggleActor, this.currentColumn, this.currentRow, 1, 1); - this.currentColumn++; - if (this.currentColumn >= this.maxTogglesPerRow) { - this.currentColumn = 0; - this.currentRow++; - } + let row = Math.floor(this.prefsGrid.get_children().length / this.maxTogglesPerRow); + let col = this.prefsGrid.get_children().length % this.maxTogglesPerRow; + this.prefsGrid.layout_manager.attach(toggleActor, col, row, 1, 1); } // === Inicializa slider de text scaling === @@ -475,87 +706,6 @@ class CinnamonUserApplet extends Applet.TextIconApplet { updateFakeSlider(idx); } - - _populateThemeOptions() { - let themes = {}; - - const readThemesFromDir = (dir) => { - try { - let file = Gio.file_new_for_path(dir); - let enumerator = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null); - - let info; - while ((info = enumerator.next_file(null))) { - let themeName = info.get_name(); - if (info.get_file_type() === Gio.FileType.DIRECTORY) { - themes[themeName] = themeName; // Use theme name as both key and value - } - } - } catch (e) { - log(`Error reading themes from ${dir}: ${e.message}`); - } - }; - - // Read themes from both system and user directories - readThemesFromDir('/usr/share/themes'); - readThemesFromDir(GLib.get_home_dir() + '/.themes'); - - // Clear existing options in the ComboBox (assuming you have a ComboBox defined) - this._clearComboBoxOptions(); - - // Add new options to the ComboBox - for (let theme in themes) { - this._addComboBoxOption(theme, theme); // Add each theme to the ComboBox - } - - // Log found themes - if (Object.keys(themes).length === 0) { - log("No themes found."); - } else { - log(`Found themes: ${JSON.stringify(themes)}`); - } - - // Set the options for light and dark themes - this.settings.setOptions("light-theme", themes); - this.settings.setOptions("dark-theme", themes); - } - - _clearComboBoxOptions() { - // Clear the ComboBox options - // Assuming you have a reference to your ComboBox, for example: - if (this.lightThemeComboBox) { - this.lightThemeComboBox.remove_all(); - } - if (this.darkThemeComboBox) { - this.darkThemeComboBox.remove_all(); - } - } - - _addComboBoxOption(value, label) { - // Add an option to the ComboBox - if (this.lightThemeComboBox) { - this.lightThemeComboBox.add_option(label, value); - } - if (this.darkThemeComboBox) { - this.darkThemeComboBox.add_option(label, value); - } - } - - // === Dark mode === - _setDarkMode(dark) { - let theme = dark ? this._darkTheme : this._lightTheme; - let colorScheme = dark ? "prefer-dark" : "default"; - - this._schemas.gtk.set_string("gtk-theme", theme); - this._schemas.cinnamon.set_string("gtk-theme", theme); - this._schemas.portal.set_string("color-scheme", colorScheme); - - this._darkMode = dark; - global.log(dark); - global.log(theme); - - this._populateThemeOptions(); - } // === Toggle Prevent Sleep === _togglePreventSleep(active) { @@ -566,24 +716,21 @@ class CinnamonUserApplet extends Applet.TextIconApplet { 0, "prevent system sleep and suspension", INHIBIT_SLEEP_FLAG, - Lang.bind(this, function(cookie) { + (cookie) => { this.sessionCookie = cookie; global.log("Prevent sleep activated, cookie: " + cookie); - // Ensure UI reflects the active state this.preventSleepToggle.button.checked = true; - }) + } ); } else if (this.sessionCookie) { // Deactivate prevent sleep - this.sessionProxy.UninhibitRemote( - this.sessionCookie, - Lang.bind(this, function() { - global.log("Prevent sleep deactivated"); - this.sessionCookie = null; - // Ensure UI reflects the inactive state + this.sessionProxy.UninhibitRemote(this.sessionCookie, () => { + global.log("Prevent sleep deactivated"); + this.sessionCookie = null; + if (this.preventSleepToggle) { this.preventSleepToggle.button.checked = false; - }) - ); + } + }); } else { // No cookie to uninhibit, just update UI this.preventSleepToggle.button.checked = false; @@ -648,10 +795,14 @@ class CinnamonUserApplet extends Applet.TextIconApplet { _updatePowerModeIcon() { this._getCurrentPowerMode((current) => { let mode = this._powerModes[current] || this._powerModes["balanced"]; + + this.powerModeToggle.active = (current === "performance"); + + // Atualiza ícone e label this.powerModeToggle.icon.icon_name = mode?.icon; this.powerModeToggle.label.set_text(mode?.label); - let isPerformance = (current === "performance"); - this.powerModeToggle.button.checked = isPerformance; + + // Atualiza estado ativo do toggle conforme performance }); } @@ -686,6 +837,12 @@ class CinnamonUserApplet extends Applet.TextIconApplet { on_applet_clicked() { this._openMenu(); } on_applet_removed_from_panel() { + // Clean up theme manager + if (this.themeManager) { + this.themeManager.destroy(); + this.themeManager = null; + } + // Clean up inhibit cookie if (this.sessionCookie !== null && this.sessionProxy) { try { diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po index cb9c64655e6..1b694bd0fb6 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po @@ -50,23 +50,4 @@ msgid "Balanced" msgstr "Equilibrado" msgid "Performance" -msgstr "Desempenho" - -# prefs -msgid "Theme" -msgstr "Tema" - -msgid "Enable dark mode" -msgstr "Ativar modo escuro" - -msgid "Select a light theme" -msgstr "Selecione um tema claro" - -msgid "Select a dark theme" -msgstr "Selecione um tema escuro" - -msgid "Show menu" -msgstr "Mostrar o menu" - -msgid "Set keybinding(s) to show menu." -msgstr "Defina atalho(s) para mostrar o menu." +msgstr "Desempenho" \ No newline at end of file diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot b/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot similarity index 81% rename from quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot rename to quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot index 8c799f5cb6e..9f49c01f5cc 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/po/user@celiopy.pot +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot @@ -56,23 +56,4 @@ msgid "Balanced" msgstr "" msgid "Performance" -msgstr "" - -# prefs -msgid "Theme" -msgstr "" - -msgid "Enable dark mode" -msgstr "" - -msgid "Select a light theme" -msgstr "" - -msgid "Select a dark theme" -msgstr "" - -msgid "Show menu" -msgstr "" - -msgid "Set keybinding(s) to show menu." -msgstr "" +msgstr "" \ No newline at end of file diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json index b8fca0fa6b3..bd13c9411d2 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json +++ b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json @@ -6,22 +6,12 @@ "display-name": { "type": "switch", "default": false, - "description": "Display user name on panel" - }, - "display-image": { - "type": "switch", - "default": false, - "description": "Display the user image on the panel" + "description": "Display user name and host" }, "section2": { "type": "section", "description": "Theme" }, - "dark-mode": { - "type": "switch", - "default": false, - "description": "Enable dark mode" - }, "light-theme": { "type": "combobox", "default": "Adwaita", diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css index cffbce10736..57523f54dd4 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css +++ b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css @@ -72,7 +72,6 @@ padding: 12px; } -.settings-toggle-button:checked, .settings-toggle-button.active { background-color: #1c71d8; } @@ -84,8 +83,6 @@ .settings-toggle-label { font-size: 9pt; max-width: 80px; - text-align: center; - margin: 0 auto; } /* Scaling slider */ From 3811bb1f87f6c744f262d156718347ec0f5980ed Mon Sep 17 00:00:00 2001 From: Celio Date: Tue, 16 Sep 2025 08:25:11 -0300 Subject: [PATCH 7/8] Adjustments. --- .../files/quick-settings@celiopy/applet.js | 5 +- .../files/quick-settings@celiopy/po/pt_BR.po | 86 ++++++++++++++----- .../po/quick-settings@celiopy.pot | 86 ++++++++++++++----- 3 files changed, 129 insertions(+), 48 deletions(-) diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js index d5fa44c4e41..40ac0045c28 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -11,14 +11,13 @@ const Gettext = imports.gettext; const Signals = imports.signals; // Add this line const UUID = 'quick-settings@celiopy'; -const APPLET_DIR = imports.ui.appletManager.appletMeta[UUID].path; -const DIALOG_ICON_SIZE = 32; +const DIALOG_ICON_SIZE = 32; const INHIBIT_IDLE_FLAG = 8; const INHIBIT_SLEEP_FLAG = 4; // l10n/translation support -Gettext.bindtextdomain(UUID, APPLET_DIR + "/locale"); +Gettext.bindtextdomain(UUID, GLib.get_home_dir() + "/.local/share/locale"); function _(str) { return Gettext.dgettext(UUID, str); diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po index 1b694bd0fb6..123be4b2590 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/pt_BR.po @@ -1,53 +1,95 @@ +# QUICK SETTINGS +# This file is put in the public domain. +# celiopy, 2025 +# +#, fuzzy msgid "" msgstr "" -"Project-Id-Version: user@celiopy 1.0.0\n" -"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-applets/issues\n" -"POT-Creation-Date: 2025-09-10 23:22-0300\n" -"PO-Revision-Date: 2025-09-10 23:50-0300\n" -"Last-Translator: Você \n" -"Language-Team: pt_BR <>\n" -"Language: pt_BR\n" +"Project-Id-Version: quick-settings@celiopy 1.0\n" +"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-applets/" +"issues\n" +"POT-Creation-Date: 2025-09-15 11:15-0300\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: pt_BR \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: applet.js:187 +#. applet.js:330 +msgid "Power Saver" +msgstr "Economia de energia" + +#. applet.js:331 +msgid "Balanced" +msgstr "Equilibrado" + +#. applet.js:332 +msgid "Performance" +msgstr "Desempenho" + +#. settings-schema.json->section1->description +#. applet.js:511 msgid "Settings" msgstr "Configurações" -#: applet.js:192 +#. applet.js:516 msgid "Lock Screen" msgstr "Bloquear tela" -#: applet.js:203 +#. applet.js:527 msgid "Log Out" msgstr "Encerrar sessão" -#: applet.js:206 +#. applet.js:530 msgid "Shut Down" msgstr "Desligar" -#: applet.js:214 +#. applet.js:536 msgid "Dark mode" msgstr "Modo escuro" -#: applet.js:224 +#. applet.js:553 msgid "Night Light" msgstr "Luz noturna" -#: applet.js:233 +#. applet.js:574 msgid "Prevent Sleep" msgstr "Impedir suspensão" -#: applet.js:250 +#. applet.js:587 msgid "Power Mode" -msgstr "Modo de Energia" +msgstr "Modo de energia" -msgid "Power Saver" -msgstr "Economia de Energia" +#. metadata.json->name +msgid "Quick Settings" +msgstr "Configurações Rápidas" -msgid "Balanced" -msgstr "Equilibrado" +#. settings-schema.json->display-name->description +msgid "Display user name and host" +msgstr "Mostrar nome de usuário e host" -msgid "Performance" -msgstr "Desempenho" \ No newline at end of file +#. settings-schema.json->section2->description +msgid "Theme" +msgstr "Tema" + +#. settings-schema.json->light-theme->description +msgid "Select a light theme" +msgstr "Selecione o tema claro" + +#. settings-schema.json->dark-theme->description +msgid "Select a dark theme" +msgstr "Selecione o tema escuro" + +#. settings-schema.json->section3->description +msgid "Keyboard shortcuts" +msgstr "Atalhos do teclado" + +#. settings-schema.json->keyOpen->description +msgid "Show menu" +msgstr "Mostrar o menu" + +#. settings-schema.json->keyOpen->tooltip +msgid "Set keybinding(s) to show menu." +msgstr "Defina o(s) atalho(s) para mostrar o menu." diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot b/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot index 9f49c01f5cc..2534390583e 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot +++ b/quick-settings@celiopy/files/quick-settings@celiopy/po/quick-settings@celiopy.pot @@ -1,59 +1,99 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# QUICK SETTINGS +# This file is put in the public domain. +# celiopy, 2025 # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-09-11 02:20-0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" +"Project-Id-Version: quick-settings@celiopy 1.0\n" +"Report-Msgid-Bugs-To: https://github.com/linuxmint/cinnamon-spices-applets/" +"issues\n" +"POT-Creation-Date: 2025-09-15 11:15-0300\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: applet.js:194 +#. applet.js:330 +msgid "Power Saver" +msgstr "" + +#. applet.js:331 +msgid "Balanced" +msgstr "" + +#. applet.js:332 +msgid "Performance" +msgstr "" + +#. settings-schema.json->section1->description +#. applet.js:511 msgid "Settings" msgstr "" -#: applet.js:199 +#. applet.js:516 msgid "Lock Screen" msgstr "" -#: applet.js:210 +#. applet.js:527 msgid "Log Out" msgstr "" -#: applet.js:213 +#. applet.js:530 msgid "Shut Down" msgstr "" -#: applet.js:221 +#. applet.js:536 msgid "Dark mode" msgstr "" -#: applet.js:231 +#. applet.js:553 msgid "Night Light" msgstr "" -#: applet.js:240 +#. applet.js:574 msgid "Prevent Sleep" msgstr "" -#: applet.js:250 +#. applet.js:587 msgid "Power Mode" msgstr "" -msgid "Power Saver" +#. metadata.json->name +msgid "Quick Settings" msgstr "" -msgid "Balanced" +#. settings-schema.json->display-name->description +msgid "Display user name and host" msgstr "" -msgid "Performance" -msgstr "" \ No newline at end of file +#. settings-schema.json->section2->description +msgid "Theme" +msgstr "" + +#. settings-schema.json->light-theme->options +msgid "Materia-light-compact" +msgstr "" + +#. settings-schema.json->light-theme->description +msgid "Select a light theme" +msgstr "" + +#. settings-schema.json->dark-theme->description +msgid "Select a dark theme" +msgstr "" + +#. settings-schema.json->section3->description +msgid "Keyboard shortcuts" +msgstr "" + +#. settings-schema.json->keyOpen->description +msgid "Show menu" +msgstr "" + +#. settings-schema.json->keyOpen->tooltip +msgid "Set keybinding(s) to show menu." +msgstr "" From 1de129c3ca93be9896f262302d4431ef05818fba Mon Sep 17 00:00:00 2001 From: Celio Date: Wed, 17 Sep 2025 20:16:39 -0300 Subject: [PATCH 8/8] Stylesheet updates... --- .../files/quick-settings@celiopy/applet.js | 6 +- .../settings-schema.json | 2 +- .../quick-settings@celiopy/stylesheet.css | 69 ++++++++----------- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js index 40ac0045c28..1cbdec3eeb2 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/applet.js +++ b/quick-settings@celiopy/files/quick-settings@celiopy/applet.js @@ -402,7 +402,7 @@ class CinnamonUserApplet extends Applet.TextIconApplet { this.menuManager = new PopupMenu.PopupMenuManager(this); this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); - + // Seções do menu this.prefsSection = new PopupMenu.PopupMenuSection(); this.interfaceSection = new PopupMenu.PopupMenuSection(); @@ -421,8 +421,8 @@ class CinnamonUserApplet extends Applet.TextIconApplet { x_align: Clutter.ActorAlign.FILL }); let gridLayout = this.prefsGrid.layout_manager; - gridLayout.set_column_spacing(12); - gridLayout.set_row_spacing(16); + gridLayout.set_column_spacing(16); + gridLayout.set_row_spacing(24); gridLayout.set_column_homogeneous(true); this.prefsSection.actor.add_child(this.prefsGrid); diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json index bd13c9411d2..95fa3719e6a 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json +++ b/quick-settings@celiopy/files/quick-settings@celiopy/settings-schema.json @@ -42,7 +42,7 @@ "keyOpen": { "type": "keybinding", "description": "Show menu", - "default": "s", + "default": "a", "tooltip": "Set keybinding(s) to show menu." } } diff --git a/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css index 57523f54dd4..b044c20f8cb 100644 --- a/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css +++ b/quick-settings@celiopy/files/quick-settings@celiopy/stylesheet.css @@ -1,7 +1,7 @@ /* Session section layout */ .session-container { - padding: 6px 12px; - spacing: 12px; + padding: 0.45em; /* 6pt para em */ + spacing: 0.45em; /* 2pt para em */ } .session-spacer { @@ -10,33 +10,33 @@ } .session-buttons-box { - spacing: 6px; + spacing: 0.3em; /* 4pt para em */ } /* User box styles */ .user-box { - spacing: 6px; + spacing: 0.45em; /* 4pt para em */ } .user-icon { - border-radius: 999px; - border: 0; + border-radius: 50%; /* Mais semântico do que 999px */ + border: 0; } .user-label { - font-size: 9pt; - font-weight: bold; + font-size: 1em; /* 9pt para em */ + font-weight: bold; } .host-label { - font-size: 8pt; + font-size: 0.8em; /* 8pt para em */ color: rgb(150, 150, 150); } /* Session button styles */ .system-button { - border-radius: 9999px; - padding: 10px; + border-radius: 7.5em; /* 100pt para em */ + padding: 0.75em; /* 6pt para em */ background-color: rgba(160, 160, 160, 0.08); transition: background-color 150ms ease; } @@ -50,26 +50,22 @@ } .system-button .system-status-icon { - icon-size: 14px; + icon-size: 0.9em; /* 10pt para em */ } /* Grid Layout */ .prefs-grid-container { - padding: 12px 6px; + padding: 1.1em 0.45em; /* 10pt e 0.375pt para em */ } .settings-toggle-box { - spacing: 8px; - width: 100%; - min-width: 80px; - max-width: 100px; - margin: 0 auto; + spacing: 0.5em; /* 5pt para em */ } .settings-toggle-button { - border-radius: 10px; background-color: rgba(160, 160, 160, 0.1); - padding: 12px; + padding: 1em; /* 10pt para em */ + border-radius: 1em; /* 10pt para em */ } .settings-toggle-button.active { @@ -77,42 +73,35 @@ } .settings-toggle-icon { - icon-size: 20px; + icon-size: 1.5em; /* 20px já em px */ } .settings-toggle-label { - font-size: 9pt; - max-width: 80px; + font-size: 0.85em; /* 9pt para em */ + max-width: 80px; /* 80px para em */ } /* Scaling slider */ -/* Container da seção de scaling */ .scaling-container { - spacing: 8px; /* Espaço entre os elementos */ - margin: 6px; + spacing: 0.35em; /* 5pt para em */ + margin: 0.15em 0.45em; /* 2pt e 6pt para em */ } /* CSS */ .fake-slider-track { - background-color: rgba(100,100,100,0.3); - border-radius: 4px; - height: 8px; + background-color: rgba(100, 100, 100, 0.3); + border-radius: 0.45em; /* 5pt para em */ + height: 0.45em; /* 5pt para em */ } .fake-slider-fill { background-color: #5a9bd5; - border-radius: 4px; - height: 8px; + border-radius: 0.45em; /* 5pt para em */ + height: 0.45em; /* 5pt para em */ } .fake-slider-marker { - background-color: #ddffff; /* cor do marcador */ - width: 2px !important; - border-radius: 1px; -} - -.panel-icon-bin{ - padding: 0 !important; - margin: 0 !important; - spacing: 0 !important; + background-color: #ddffff; + width: 0.075em; /* 1pt para em */ + border-radius: 0.075em; /* 1pt para em */ } \ No newline at end of file