Extension: Port to ESM

This commit is contained in:
Romain Vigier
2023-08-19 19:23:10 +02:00
parent b480820b37
commit 8c7724607e
23 changed files with 587 additions and 549 deletions
+5
View File
@@ -7,3 +7,8 @@ rules:
- vars: 'local' - vars: 'local'
varsIgnorePattern: (^unused|_$) varsIgnorePattern: (^unused|_$)
argsIgnorePattern: ^(unused|_) argsIgnorePattern: ^(unused|_)
object-curly-spacing: off
globals:
NTSMetadata: writable
parserOptions:
sourceType: module
+1 -1
View File
@@ -4,7 +4,7 @@ SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
--> -->
<gresources> <gresources>
<gresource prefix="/org/gnome/shell/extensions/nightthemeswitcher/preferences"> <gresource prefix="/org/gnome/Shell/Extensions/nightthemeswitcher/preferences">
<file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/actions/nightthemeswitcher-code-symbolic.svg">icons/code-symbolic.svg</file> <file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/actions/nightthemeswitcher-code-symbolic.svg">icons/code-symbolic.svg</file>
<file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/actions/nightthemeswitcher-translate-symbolic.svg">icons/translate-symbolic.svg</file> <file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/actions/nightthemeswitcher-translate-symbolic.svg">icons/translate-symbolic.svg</file>
<file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/apps/nightthemeswitcher-symbolic.svg">icons/nightthemeswitcher-symbolic.svg</file> <file compressed="true" preprocess="xml-stripblanks" alias="icons/scalable/apps/nightthemeswitcher-symbolic.svg">icons/nightthemeswitcher-symbolic.svg</file>
+6 -19
View File
@@ -23,25 +23,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkLabel">
<property name="orientation">vertical</property> <property name="label">Night Theme Switcher</property>
<property name="spacing">6</property> <property name="wrap">True</property>
<child> <style>
<object class="GtkLabel"> <class name="title-1"/>
<property name="label">Night Theme Switcher</property> </style>
<property name="wrap">True</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<binding name="label">
<closure type="gchararray" function="getVersionString"/>
</binding>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>
+4 -8
View File
@@ -1,16 +1,12 @@
// SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension();
/** /**
* Print a message in debug builds. * Print a message in debug builds.
* *
* @param {string} msg Message to print. * @param {string} msg Message to print.
*/ */
function message(msg) { export function message(msg) {
if (Me.metadata['build-type'] === 'debug') if (NTSMetadata['build-type'] === 'debug')
console.log(`[${Me.metadata.name}] ${msg}`); console.log(`[${NTSMetadata.name}] ${msg}`);
} }
+1 -1
View File
@@ -7,7 +7,7 @@
* @readonly * @readonly
* @enum {string} * @enum {string}
*/ */
var Time = { export const Time = {
UNKNOWN: 'unknown', UNKNOWN: 'unknown',
DAY: 'day', DAY: 'day',
NIGHT: 'night', NIGHT: 'night',
+17 -29
View File
@@ -3,40 +3,33 @@
'use strict'; 'use strict';
const { Gio } = imports.gi; import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension(); import * as debug from './debug.js';
const debug = Me.imports.debug; import { SwitcherCommands } from './modules/SwitcherCommands.js';
import { SwitcherThemeCursor, SwitcherThemeGtk, SwitcherThemeIcon, SwitcherThemeShell } from './modules/SwitcherTheme.js';
const { SwitcherCommands } = Me.imports.modules.SwitcherCommands; import { Timer } from './modules/Timer.js';
const { SwitcherThemeCursor, SwitcherThemeGtk, SwitcherThemeIcon, SwitcherThemeShell } = Me.imports.modules.SwitcherTheme;
const { Timer } = Me.imports.modules.Timer;
class NightThemeSwitcher { export default class NightThemeSwitcher extends Extension {
#modules = []; #modules = [];
constructor() {
debug.message('Initializing extension...');
extensionUtils.initTranslations();
debug.message('Extension initialized.');
}
enable() { enable() {
globalThis.NTSMetadata = this.metadata;
debug.message('Enabling extension...'); debug.message('Enabling extension...');
const timer = new Timer(); const timer = new Timer({ settings: this.getSettings(`${this.metadata['settings-schema']}.time`), openPrefs: this.openPrefs });
this.#modules.push(timer); this.#modules.push(timer);
[ [
SwitcherThemeGtk, new SwitcherThemeGtk({ timer, settings: this.getSettings(`${this.metadata['settings-schema']}.gtk-variants`) }),
SwitcherThemeIcon, new SwitcherThemeIcon({ timer, settings: this.getSettings(`${this.metadata['settings-schema']}.icon-variants`) }),
SwitcherThemeShell, new SwitcherThemeShell({ timer, settings: this.getSettings(`${this.metadata['settings-schema']}.shell-variants`) }),
SwitcherThemeCursor, new SwitcherThemeCursor({ timer, settings: this.getSettings(`${this.metadata['settings-schema']}.cursor-variants`) }),
SwitcherCommands, new SwitcherCommands({ timer, settings: this.getSettings(`${this.metadata['settings-schema']}.commands`) }),
].forEach(SwitcherModule => this.#modules.push(new SwitcherModule({ timer }))); ].forEach(module => this.#modules.push(module));
this.#modules.forEach(module => module.enable()); this.#modules.forEach(module => module.enable());
@@ -53,12 +46,7 @@ class NightThemeSwitcher {
this.#modules = []; this.#modules = [];
debug.message('Extension disabled.'); debug.message('Extension disabled.');
delete globalThis.NTSMetadata;
} }
} }
/**
* Extension initialization.
*/
function init() {
return new NightThemeSwitcher();
}
+1 -1
View File
@@ -6,7 +6,7 @@
"settings-schema": "@DNS@", "settings-schema": "@DNS@",
"url": "https://nightthemeswitcher.romainvigier.fr", "url": "https://nightthemeswitcher.romainvigier.fr",
"session-modes": ["unlock-dialog", "user"], "session-modes": ["unlock-dialog", "user"],
"shell-version": ["44"], "shell-version": ["45"],
"version": @VERSION@, "version": @VERSION@,
"build-type": "@BUILD_TYPE@" "build-type": "@BUILD_TYPE@"
} }
+10 -15
View File
@@ -1,14 +1,7 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Gio } = imports.gi; import * as debug from '../debug.js';
const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension();
const debug = Me.imports.debug;
const { Time } = Me.imports.enums.Time;
/** /**
@@ -22,13 +15,8 @@ const { Time } = Me.imports.enums.Time;
/** /**
* The Switcher runs a callback function when the time changes. * The Switcher runs a callback function when the time changes.
* *
* @param {Object} params Params object.
* @param {string} params.name Name of the switcher.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Settings with the `enabled` key.
* @param {TimeChangedCallback} params.callback Callback function.
*/ */
var Switcher = class { export class Switcher {
#name; #name;
#timer; #timer;
#settings; #settings;
@@ -37,6 +25,13 @@ var Switcher = class {
#statusConnection = null; #statusConnection = null;
#timerConnection = null; #timerConnection = null;
/**
* @param {object} params Params object.
* @param {string} params.name Name of the switcher.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Settings with the `enabled` key.
* @param {TimeChangedCallback} params.callback Callback function.
*/
constructor({ name, timer, settings, callback }) { constructor({ name, timer, settings, callback }) {
this.#name = name; this.#name = name;
this.#timer = timer; this.#timer = timer;
@@ -98,4 +93,4 @@ var Switcher = class {
#onTimeChanged() { #onTimeChanged() {
this.#callback(this.#timer.time); this.#callback(this.#timer.time);
} }
}; }
+12 -14
View File
@@ -1,29 +1,27 @@
// SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { GLib } = imports.gi; import GLib from 'gi://GLib';
const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension(); import * as debug from '../debug.js';
const debug = Me.imports.debug; import { Time } from '../enums/Time.js';
const { Switcher } = Me.imports.modules.Switcher; import { Switcher } from './Switcher.js';
const { Time } = Me.imports.enums.Time;
/** /**
* The Commands Switcher spawns commands according to the time. * The Commands Switcher spawns commands according to the time.
*
* @param {Object} params Params object.
* @param {Timer} params.timer Timer to listen to.
*/ */
var SwitcherCommands = class extends Switcher { export class SwitcherCommands extends Switcher {
#settings; #settings;
constructor({ timer }) { /**
const settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.commands`); * @param {object} params Params object.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Commands settings.
*/
constructor({ timer, settings }) {
super({ super({
name: 'Command', name: 'Command',
timer, timer,
@@ -42,4 +40,4 @@ var SwitcherCommands = class extends Switcher {
GLib.spawn_async(null, ['sh', '-c', command], null, GLib.SpawnFlags.SEARCH_PATH, null); GLib.spawn_async(null, ['sh', '-c', command], null, GLib.SpawnFlags.SEARCH_PATH, null);
debug.message(`Spawned ${time} command.`); debug.message(`Spawned ${time} command.`);
} }
}; }
+126 -38
View File
@@ -1,18 +1,18 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Gio } = imports.gi; import Gio from 'gi://Gio';
const { extensionUtils } = imports.misc; import GLib from 'gi://GLib';
const { extensionManager } = imports.ui.main;
const Me = extensionUtils.getCurrentExtension(); import { ExtensionState } from 'resource:///org/gnome/shell/misc/extensionUtils.js';
import { extensionManager, setThemeStylesheet, loadTheme } from 'resource:///org/gnome/shell/ui/main.js';
const debug = Me.imports.debug; import * as debug from '../debug.js';
const utils = Me.imports.utils; import * as utils from '../utils.js';
const { Switcher } = Me.imports.modules.Switcher; import { Time } from '../enums/Time.js';
const { Time } = Me.imports.enums.Time; import { Switcher } from './Switcher.js';
/** /**
@@ -28,16 +28,8 @@ const { Time } = Me.imports.enums.Time;
* provided settings or by running a callback function. * provided settings or by running a callback function.
* *
* It also listens to system theme changes to update the current variant setting. * It also listens to system theme changes to update the current variant setting.
*
* @param {Object} params Params object.
* @param {string} params.name Name of the switcher.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Settings with the `enabled`, `day` and `night` keys.
* @param {Gio.Settings=} params.systemSettings System settings containing the theme name.
* @param {string} params.themeKey Settings key of the theme name.
* @param {noSettingsUpdateSystemThemeCallback} Callback function.
*/ */
var SwitcherTheme = class extends Switcher { export class SwitcherTheme extends Switcher {
#name; #name;
#timer; #timer;
#settings; #settings;
@@ -47,6 +39,15 @@ var SwitcherTheme = class extends Switcher {
#settingsConnections = []; #settingsConnections = [];
/**
* @param {object} params Params object.
* @param {string} params.name Name of the switcher.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Settings with the `enabled`, `day` and `night` keys.
* @param {Gio.Settings} [params.systemSettings] System settings containing the theme name.
* @param {string} params.themeKey Settings key of the theme name.
* @param {noSettingsUpdateSystemThemeCallback} params.noSettingsUpdateSystemThemeCallback function.
*/
constructor({ name, timer, settings, systemSettings = null, themeKey, noSettingsUpdateSystemThemeCallback = null }) { constructor({ name, timer, settings, systemSettings = null, themeKey, noSettingsUpdateSystemThemeCallback = null }) {
super({ super({
name, name,
@@ -141,59 +142,78 @@ var SwitcherTheme = class extends Switcher {
else if (this.#noSettingsUpdateSystemThemeCallback) else if (this.#noSettingsUpdateSystemThemeCallback)
this.#noSettingsUpdateSystemThemeCallback(this.#timer.time); this.#noSettingsUpdateSystemThemeCallback(this.#timer.time);
} }
}; }
var SwitcherThemeCursor = class extends SwitcherTheme { export class SwitcherThemeCursor extends SwitcherTheme {
constructor({ timer }) { /**
* @param {object} params Params object.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Cursor theme settings.
*/
constructor({ timer, settings }) {
super({ super({
name: 'Cursor theme', name: 'Cursor theme',
timer, timer,
settings: extensionUtils.getSettings(`${Me.metadata['settings-schema']}.cursor-variants`), settings,
systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }), systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }),
themeKey: 'cursor-theme', themeKey: 'cursor-theme',
}); });
} }
}; }
var SwitcherThemeGtk = class extends SwitcherTheme { export class SwitcherThemeGtk extends SwitcherTheme {
constructor({ timer }) { /**
* @param {object} params Params object.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings GTK theme settings.
*/
constructor({ timer, settings }) {
super({ super({
name: 'GTK theme', name: 'GTK theme',
timer, timer,
settings: extensionUtils.getSettings(`${Me.metadata['settings-schema']}.gtk-variants`), settings,
systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }), systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }),
themeKey: 'gtk-theme', themeKey: 'gtk-theme',
}); });
} }
}; }
var SwitcherThemeIcon = class extends SwitcherTheme { export class SwitcherThemeIcon extends SwitcherTheme {
constructor({ timer }) { /**
* @param {object} params Params object.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Icon theme settings.
*/
constructor({ timer, settings }) {
super({ super({
name: 'Icon theme', name: 'Icon theme',
timer, timer,
settings: extensionUtils.getSettings(`${Me.metadata['settings-schema']}.icon-variants`), settings,
systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }), systemSettings: new Gio.Settings({ schema: 'org.gnome.desktop.interface' }),
themeKey: 'icon-theme', themeKey: 'icon-theme',
}); });
} }
}; }
var SwitcherThemeShell = class extends SwitcherTheme { export class SwitcherThemeShell extends SwitcherTheme {
#settings; #settings;
#extensionManagerConnection = null; #extensionManagerConnection = null;
constructor({ timer }) { /**
const settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.shell-variants`); * @param {object} params Params object.
* @param {Timer} params.timer Timer to listen to.
* @param {Gio.Settings} params.settings Shell theme settings.
*/
constructor({ timer, settings }) {
super({ super({
name: 'Shell theme', name: 'Shell theme',
timer, timer,
settings, settings,
systemSettings: utils.getUserthemesSettings(), systemSettings: getUserthemesSettings(),
themeKey: 'name', themeKey: 'name',
noSettingsUpdateSystemThemeCallback: time => this.#noSettingsUpdateSystemThemeCallback(time), noSettingsUpdateSystemThemeCallback: time => this.#noSettingsUpdateSystemThemeCallback(time),
}); });
@@ -215,11 +235,79 @@ var SwitcherThemeShell = class extends SwitcherTheme {
#noSettingsUpdateSystemThemeCallback(time) { #noSettingsUpdateSystemThemeCallback(time) {
const shellTheme = this.#settings.get_string(time); const shellTheme = this.#settings.get_string(time);
const stylesheet = utils.getShellThemeStylesheet(shellTheme); const stylesheet = getShellThemeStylesheet(shellTheme);
utils.applyShellStylesheet(stylesheet); applyShellStylesheet(stylesheet);
} }
#onExtensionStateChanged() { #onExtensionStateChanged() {
this.systemSettings = utils.getUserthemesSettings(); this.systemSettings = getUserthemesSettings();
} }
}; }
/**
* Get the User Themes extension.
*
* @returns {object|undefined} The User Themes extension object or undefined if
* it isn't installed.
*/
function getUserthemesExtension() {
try {
return extensionManager.lookup('user-theme@gnome-shell-extensions.gcampax.github.com');
} catch (_e) {
return undefined;
}
}
/**
* Get the User Themes extension settings.
*
* @returns {Gio.Settings|null} The User Themes extension settings or null if
* the extension isn't installed.
*/
function getUserthemesSettings() {
let extension = getUserthemesExtension();
if (!extension || extension.state !== ExtensionState.ENABLED)
return null;
const schemaDir = extension.dir.get_child('schemas');
const GioSSS = Gio.SettingsSchemaSource;
let schemaSource;
if (schemaDir.query_exists(null))
schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), GioSSS.get_default(), false);
else
schemaSource = GioSSS.get_default();
const schemaObj = schemaSource.lookup('org.gnome.shell.extensions.user-theme', true);
return new Gio.Settings({ settings_schema: schemaObj });
}
/**
* Get the shell theme stylesheet.
*
* @param {string} theme The shell theme name.
* @returns {string|null} Path to the shell theme stylesheet.
*/
function getShellThemeStylesheet(theme) {
const themeName = theme ? `'${theme}'` : 'default';
debug.message(`Getting the ${themeName} theme shell stylesheet...`);
let stylesheet = null;
if (theme) {
const stylesheetPaths = utils.getResourcesDirsPaths('themes').map(path => GLib.build_filenamev([path, theme, 'gnome-shell', 'gnome-shell.css']));
stylesheet = stylesheetPaths.find(path => {
const file = Gio.file_new_for_path(path);
return file.query_exists(null);
});
}
return stylesheet;
}
/**
* Apply a stylesheet to the shell.
*
* @param {string} stylesheet The shell stylesheet to apply.
*/
function applyShellStylesheet(stylesheet) {
debug.message('Applying shell stylesheet...');
setThemeStylesheet(stylesheet);
loadTheme();
debug.message('Shell stylesheet applied.');
}
+33 -24
View File
@@ -1,18 +1,20 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Geoclue, Gio, GLib, GObject, Meta, Shell } = imports.gi; import Geoclue from 'gi://Geoclue';
const { extensionUtils } = imports.misc; import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
const { main, messageTray } = imports.ui; import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';
import { layoutManager, messageTray, wm } from 'resource:///org/gnome/shell/ui/main.js';
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
const Me = extensionUtils.getCurrentExtension(); import * as debug from '../debug.js';
const _ = extensionUtils.gettext; import { Time } from '../enums/Time.js';
const debug = Me.imports.debug;
const { Time } = Me.imports.enums.Time;
/** /**
@@ -24,10 +26,11 @@ const { Time } = Me.imports.enums.Time;
* to a manual schedule if the location services are disabled or if the user * to a manual schedule if the location services are disabled or if the user
* forced the manual schedule in the preferences. * forced the manual schedule in the preferences.
*/ */
var Timer = class extends GObject.Object { export class Timer extends GObject.Object {
#settings; #settings;
#interfaceSettings; #interfaceSettings;
#locationSettings; #locationSettings;
#openPrefs;
#time; #time;
#cancellable = null; #cancellable = null;
@@ -48,11 +51,17 @@ var Timer = class extends GObject.Object {
}, this); }, this);
} }
constructor() { /**
* @param {object} params Params object.
* @param {Gio.Settings} params.settings Timer settings.
* @param {Function} params.openPrefs Function opening the extension preferences.
*/
constructor({ settings, openPrefs }) {
super(); super();
this.#settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.time`); this.#settings = settings;
this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this.#locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' }); this.#locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
this.#openPrefs = openPrefs;
} }
enable() { enable() {
@@ -103,7 +112,7 @@ var Timer = class extends GObject.Object {
debug.message(manual ? `Time manually set to ${time}.` : `Time changed to ${time}.`); debug.message(manual ? `Time manually set to ${time}.` : `Time changed to ${time}.`);
main.layoutManager.screenTransition.run(); layoutManager.screenTransition.run();
this.#interfaceSettings.set_string('color-scheme', time === Time.NIGHT ? 'prefer-dark' : 'default'); this.#interfaceSettings.set_string('color-scheme', time === Time.NIGHT ? 'prefer-dark' : 'default');
this.notify('time'); this.notify('time');
} }
@@ -203,7 +212,7 @@ var Timer = class extends GObject.Object {
if (!this.#settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0]) if (!this.#settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0])
return; return;
debug.message('Adding keybinding...'); debug.message('Adding keybinding...');
main.wm.addKeybinding( wm.addKeybinding(
'nightthemeswitcher-ondemand-keybinding', 'nightthemeswitcher-ondemand-keybinding',
this.#settings, this.#settings,
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
@@ -219,7 +228,7 @@ var Timer = class extends GObject.Object {
#removeKeybinding() { #removeKeybinding() {
if (this.#previousKeybinding) { if (this.#previousKeybinding) {
debug.message('Removing keybinding...'); debug.message('Removing keybinding...');
main.wm.removeKeybinding('nightthemeswitcher-ondemand-keybinding'); wm.removeKeybinding('nightthemeswitcher-ondemand-keybinding');
debug.message('Removed keybinding.'); debug.message('Removed keybinding.');
} }
} }
@@ -258,29 +267,29 @@ var Timer = class extends GObject.Object {
} catch (e) { } catch (e) {
const [latitude, longitude] = this.#settings.get_value('location').deepUnpack(); const [latitude, longitude] = this.#settings.get_value('location').deepUnpack();
if (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) { if (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) {
console.error(`[${Me.metadata.name}] Unable to retrieve the location, using the last known location instead.\n${e}`); console.error(`[${NTSMetadata.name}] Unable to retrieve the location, using the last known location instead.\n${e}`);
this.#updateSuntimes(); this.#updateSuntimes();
} else { } else {
console.error(`[${Me.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.\n${e}`); console.error(`[${NTSMetadata.name}] Unable to retrieve the location, using the manual schedule times instead.\n${e}`);
const source = new messageTray.Source(Me.metadata.name, 'dialog-information-symbolic'); const source = new MessageTray.Source(NTSMetadata.name, 'dialog-information-symbolic');
main.messageTray.add(source); messageTray.add(source);
const notification = new messageTray.Notification( const notification = new MessageTray.Notification(
source, source,
_('Unknown Location'), _('Unknown Location'),
_('A manual schedule will be used to switch the dark mode.'), _('A manual schedule will be used to switch the dark mode.'),
{ {
gicon: Gio.icon_new_for_string(GLib.build_filenamev([Me.path, 'icons', 'nightthemeswitcher-symbolic.svg'])), gicon: Gio.icon_new_for_string(GLib.build_filenamev([NTSMetadata.path, 'icons', 'nightthemeswitcher-symbolic.svg'])),
} }
); );
notification.addAction(_('Edit Manual Schedule'), () => extensionUtils.openPrefs()); notification.addAction(_('Edit Manual Schedule'), () => this.#openPrefs());
const locationSettingsApp = Shell.AppSystem.get_default().lookup_app('gnome-location-panel.desktop'); const locationSettingsApp = Shell.AppSystem.get_default().lookup_app('gnome-location-panel.desktop');
if (locationSettingsApp) if (locationSettingsApp)
notification.addAction(_('Open Location Settings'), () => locationSettingsApp.activate()); notification.addAction(_('Open Location Settings'), () => locationSettingsApp.activate());
notification.connect('activated', () => extensionUtils.openPrefs()); notification.connect('activated', () => this.#openPrefs());
source.showNotification(notification); source.showNotification(notification);
@@ -364,4 +373,4 @@ var Timer = class extends GObject.Object {
debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`); debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
} }
}; }
+46 -38
View File
@@ -1,47 +1,55 @@
// SPDX-FileCopyrightText: 2021, 2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2021, 2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gdk from 'gi://Gdk';
import GdkPixbuf from 'gi://GdkPixbuf';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension(); import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const _ = extensionUtils.gettext;
var BackgroundButton = GObject.registerClass({ export class BackgroundButton extends Gtk.Button {
GTypeName: 'BackgroundButton',
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui',
InternalChildren: ['filechooser', 'thumbnail'],
Properties: {
uri: GObject.ParamSpec.string(
'uri',
'URI',
'URI to the background file',
GObject.ParamFlags.READWRITE,
null
),
thumbWidth: GObject.ParamSpec.int(
'thumb-width',
'Thumbnail width',
'Width of the displayed thumbnail',
GObject.ParamFlags.READWRITE,
0, 600,
180
),
thumbHeight: GObject.ParamSpec.int(
'thumb-height',
'Thumbnail height',
'Height of the displayed thumbnail',
GObject.ParamFlags.READWRITE,
0, 600,
180
),
},
}, class BackgroundButton extends Gtk.Button {
#uri; #uri;
constructor(props = {}) { static {
super(props); GObject.registerClass({
GTypeName: 'BackgroundButton',
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui',
InternalChildren: ['filechooser', 'thumbnail'],
Properties: {
uri: GObject.ParamSpec.string(
'uri',
'URI',
'URI to the background file',
GObject.ParamFlags.READWRITE,
null
),
thumbWidth: GObject.ParamSpec.int(
'thumb-width',
'Thumbnail width',
'Width of the displayed thumbnail',
GObject.ParamFlags.READWRITE,
0, 600,
180
),
thumbHeight: GObject.ParamSpec.int(
'thumb-height',
'Thumbnail height',
'Height of the displayed thumbnail',
GObject.ParamFlags.READWRITE,
0, 600,
180
),
},
}, this);
}
constructor({ ...params } = {}) {
super(params);
this.#setupSize(); this.#setupSize();
this.#setupDropTarget(); this.#setupDropTarget();
this.#setupFileChooserFilter(); this.#setupFileChooserFilter();
@@ -151,7 +159,7 @@ var BackgroundButton = GObject.registerClass({
if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0])) if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0]))
throw new Error(); throw new Error();
} catch (e) { } catch (e) {
console.error(`[${Me.metadata.name}] No suitable background file found in ${file.get_path()}.\n${e}`); console.error(`No suitable background file found in ${file.get_path()}.\n${e}`);
return; return;
} }
} else { } else {
@@ -172,4 +180,4 @@ var BackgroundButton = GObject.registerClass({
this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf); this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf);
} }
}); }
+18 -15
View File
@@ -1,25 +1,28 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, Gio, GLib, GObject } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
const Me = extensionUtils.getCurrentExtension();
var BackgroundsPage = GObject.registerClass({ export class BackgroundsPage extends Adw.PreferencesPage {
GTypeName: 'BackgroundsPage', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/BackgroundsPage.ui', GObject.registerClass({
InternalChildren: [ GTypeName: 'BackgroundsPage',
'day_button', Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundsPage.ui',
'night_button', InternalChildren: [
], 'day_button',
}, class BackgroundsPage extends Adw.PreferencesPage { 'night_button',
constructor(props = {}) { ],
super(props); }, this);
}
constructor({ ...params } = {}) {
super(params);
const settings = new Gio.Settings({ schema: 'org.gnome.desktop.background' }); const settings = new Gio.Settings({ schema: 'org.gnome.desktop.background' });
settings.bind('picture-uri', this._day_button, 'uri', Gio.SettingsBindFlags.DEFAULT); settings.bind('picture-uri', this._day_button, 'uri', Gio.SettingsBindFlags.DEFAULT);
settings.bind('picture-uri-dark', this._night_button, 'uri', Gio.SettingsBindFlags.DEFAULT); settings.bind('picture-uri-dark', this._night_button, 'uri', Gio.SettingsBindFlags.DEFAULT);
} }
}); }
+11 -9
View File
@@ -1,18 +1,20 @@
// SPDX-FileCopyrightText: 2021 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2021 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Gio, GLib, GObject, Gtk } = imports.gi; import GObject from 'gi://GObject';
const { extensionUtils } = imports.misc; import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension();
var ClearableEntry = GObject.registerClass({ export class ClearableEntry extends Gtk.Entry {
GTypeName: 'ClearableEntry', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/ClearableEntry.ui', GObject.registerClass({
}, class ClearableEntry extends Gtk.Entry { GTypeName: 'ClearableEntry',
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ClearableEntry.ui',
}, this);
}
onIconReleased(entry, position) { onIconReleased(entry, position) {
if (position === Gtk.EntryIconPosition.SECONDARY) if (position === Gtk.EntryIconPosition.SECONDARY)
entry.text = ''; entry.text = '';
} }
}); }
+19 -17
View File
@@ -1,27 +1,29 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, Gio, GLib, GObject } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
const Me = extensionUtils.getCurrentExtension();
var CommandsPage = GObject.registerClass({ export class CommandsPage extends Adw.PreferencesPage {
GTypeName: 'CommandsPage', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/CommandsPage.ui', GObject.registerClass({
InternalChildren: [ GTypeName: 'CommandsPage',
'enabled_switch', Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/CommandsPage.ui',
'sunrise_entry', InternalChildren: [
'sunset_entry', 'enabled_switch',
], 'sunrise_entry',
}, class CommandsPage extends Adw.PreferencesPage { 'sunset_entry',
constructor(props = {}) { ],
super(props); }, this);
const settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.commands`); }
constructor({ settings, ...params } = {}) {
super(params);
settings.bind('enabled', this._enabled_switch, 'active', Gio.SettingsBindFlags.DEFAULT); settings.bind('enabled', this._enabled_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
settings.bind('sunrise', this._sunrise_entry, 'text', Gio.SettingsBindFlags.DEFAULT); settings.bind('sunrise', this._sunrise_entry, 'text', Gio.SettingsBindFlags.DEFAULT);
settings.bind('sunset', this._sunset_entry, 'text', Gio.SettingsBindFlags.DEFAULT); settings.bind('sunset', this._sunset_entry, 'text', Gio.SettingsBindFlags.DEFAULT);
} }
}); }
+9 -12
View File
@@ -1,18 +1,15 @@
// SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, GLib, GObject } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import GObject from 'gi://GObject';
const Me = extensionUtils.getCurrentExtension();
const _ = extensionUtils.gettext;
var ContributePage = GObject.registerClass({ export class ContributePage extends Adw.PreferencesPage {
GTypeName: 'ContributePage', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/ContributePage.ui', GObject.registerClass({
}, class ContributePage extends Adw.PreferencesPage { GTypeName: 'ContributePage',
getVersionString(_page) { Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ContributePage.ui',
return _('Version %d').format(Me.metadata.version); }, this);
} }
}); }
+31 -27
View File
@@ -1,32 +1,36 @@
// SPDX-FileCopyrightText: 2020, 2021 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020, 2021 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { GObject } = imports.gi; import GObject from 'gi://GObject';
var DropDownChoice = GObject.registerClass({ export class DropDownChoice extends GObject.Object {
GTypeName: 'DropDownChoice', static {
Properties: { GObject.registerClass({
id: GObject.ParamSpec.string( GTypeName: 'DropDownChoice',
'id', Properties: {
'ID', id: GObject.ParamSpec.string(
'Identifier', 'id',
GObject.ParamFlags.READWRITE, 'ID',
null 'Identifier',
), GObject.ParamFlags.READWRITE,
title: GObject.ParamSpec.string( null
'title', ),
'Title', title: GObject.ParamSpec.string(
'Displayed title', 'title',
GObject.ParamFlags.READWRITE, 'Title',
null 'Displayed title',
), GObject.ParamFlags.READWRITE,
enabled: GObject.ParamSpec.boolean( null
'enabled', ),
'Enabled', enabled: GObject.ParamSpec.boolean(
'If the choice is enabled', 'enabled',
GObject.ParamFlags.READWRITE, 'Enabled',
true 'If the choice is enabled',
), GObject.ParamFlags.READWRITE,
}, true
}, class DropDownChoice extends GObject.Object {}); ),
},
}, this);
}
}
+20 -18
View File
@@ -1,25 +1,27 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, Gio, GLib, GObject, Gtk } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
const Me = extensionUtils.getCurrentExtension();
var SchedulePage = GObject.registerClass({ export class SchedulePage extends Adw.PreferencesPage {
GTypeName: 'SchedulePage', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/SchedulePage.ui', GObject.registerClass({
InternalChildren: [ GTypeName: 'SchedulePage',
'manual_schedule_switch', Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/SchedulePage.ui',
'keyboard_shortcut_button', InternalChildren: [
'schedule_sunrise_time_chooser', 'manual_schedule_switch',
'schedule_sunset_time_chooser', 'keyboard_shortcut_button',
], 'schedule_sunrise_time_chooser',
}, class SchedulePage extends Adw.PreferencesPage { 'schedule_sunset_time_chooser',
constructor(props = {}) { ],
super(props); }, this);
const settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.time`); }
constructor({ settings, ...params } = {}) {
super(params);
settings.bind('manual-schedule', this._manual_schedule_switch, 'active', Gio.SettingsBindFlags.DEFAULT); settings.bind('manual-schedule', this._manual_schedule_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
@@ -34,4 +36,4 @@ var SchedulePage = GObject.registerClass({
}); });
this._keyboard_shortcut_button.keybinding = settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0]; this._keyboard_shortcut_button.keybinding = settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0];
} }
}); }
+88 -23
View File
@@ -1,28 +1,29 @@
// SPDX-FileCopyrightText: 2021, 2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2021, 2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Gdk, GLib, GObject, Gtk } = imports.gi; import Gdk from 'gi://Gdk';
const { extensionUtils } = imports.misc; import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension();
const utils = Me.imports.utils;
var ShortcutButton = GObject.registerClass({ export class ShortcutButton extends Gtk.Stack {
GTypeName: 'ShortcutButton', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/ShortcutButton.ui', GObject.registerClass({
InternalChildren: ['choose_button', 'change_button', 'clear_button', 'dialog'], GTypeName: 'ShortcutButton',
Properties: { Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ShortcutButton.ui',
keybinding: GObject.ParamSpec.string( InternalChildren: ['choose_button', 'change_button', 'clear_button', 'dialog'],
'keybinding', Properties: {
'Keybinding', keybinding: GObject.ParamSpec.string(
'Key sequence', 'keybinding',
GObject.ParamFlags.READWRITE, 'Keybinding',
null 'Key sequence',
), GObject.ParamFlags.READWRITE,
}, null
}, class ShortcutButton extends Gtk.Stack { ),
},
}, this);
}
vfunc_mnemonic_activate() { vfunc_mnemonic_activate() {
this.activate(); this.activate();
} }
@@ -65,8 +66,8 @@ var ShortcutButton = GObject.registerClass({
} }
if ( if (
!utils.isBindingValid({ mask, keycode, keyval }) || !isBindingValid({ mask, keycode, keyval }) ||
!utils.isAccelValid({ mask, keyval }) !isAccelValid({ mask, keyval })
) )
return Gdk.EVENT_STOP; return Gdk.EVENT_STOP;
@@ -79,4 +80,68 @@ var ShortcutButton = GObject.registerClass({
this._dialog.close(); this._dialog.close();
return Gdk.EVENT_STOP; return Gdk.EVENT_STOP;
} }
}); }
/**
* Check if the given keyval is forbidden.
*
* @param {number} keyval The keyval number.
* @returns {boolean} `true` if the keyval is forbidden.
*/
function isKeyvalForbidden(keyval) {
const forbiddenKeyvals = [
Gdk.KEY_Home,
Gdk.KEY_Left,
Gdk.KEY_Up,
Gdk.KEY_Right,
Gdk.KEY_Down,
Gdk.KEY_Page_Up,
Gdk.KEY_Page_Down,
Gdk.KEY_End,
Gdk.KEY_Tab,
Gdk.KEY_KP_Enter,
Gdk.KEY_Return,
Gdk.KEY_Mode_switch,
];
return forbiddenKeyvals.includes(keyval);
}
/**
* Check if the given key combo is a valid binding
*
* @param {{mask: number, keycode: number, keyval:number}} combo An object
* representing the key combo.
* @returns {boolean} `true` if the key combo is a valid binding.
*/
function isBindingValid({ mask, keycode, keyval }) {
if ((mask === 0 || mask === Gdk.SHIFT_MASK) && keycode !== 0) {
if (
(keyval >= Gdk.KEY_a && keyval <= Gdk.KEY_z) ||
(keyval >= Gdk.KEY_A && keyval <= Gdk.KEY_Z) ||
(keyval >= Gdk.KEY_0 && keyval <= Gdk.KEY_9) ||
(keyval >= Gdk.KEY_kana_fullstop && keyval <= Gdk.KEY_semivoicedsound) ||
(keyval >= Gdk.KEY_Arabic_comma && keyval <= Gdk.KEY_Arabic_sukun) ||
(keyval >= Gdk.KEY_Serbian_dje && keyval <= Gdk.KEY_Cyrillic_HARDSIGN) ||
(keyval >= Gdk.KEY_Greek_ALPHAaccent && keyval <= Gdk.KEY_Greek_omega) ||
(keyval >= Gdk.KEY_hebrew_doublelowline && keyval <= Gdk.KEY_hebrew_taf) ||
(keyval >= Gdk.KEY_Thai_kokai && keyval <= Gdk.KEY_Thai_lekkao) ||
(keyval >= Gdk.KEY_Hangul_Kiyeog && keyval <= Gdk.KEY_Hangul_J_YeorinHieuh) ||
(keyval === Gdk.KEY_space && mask === 0) ||
isKeyvalForbidden(keyval)
)
return false;
}
return true;
}
/**
* Check if the given key combo is a valid accelerator.
*
* @param {{mask: number, keyval:number}} combo An object representing the key
* combo.
* @returns {boolean} `true` if the key combo is a valid accelerator.
*/
function isAccelValid({ mask, keyval }) {
return Gtk.accelerator_valid(keyval, mask) || (keyval === Gdk.KEY_Tab && mask !== 0);
}
+33 -31
View File
@@ -1,41 +1,42 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020-2022 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Adw, Gio, GLib, GObject, Gtk } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension(); import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const _ = extensionUtils.gettext;
const utils = Me.imports.utils; import * as utils from '../utils.js';
const { DropDownChoice } = Me.imports.preferences.DropDownChoice; import { DropDownChoice } from './DropDownChoice.js';
var ThemesPage = GObject.registerClass({ export class ThemesPage extends Adw.PreferencesPage {
GTypeName: 'ThemesPage', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/ThemesPage.ui', GObject.registerClass({
InternalChildren: [ GTypeName: 'ThemesPage',
'gtk_row', Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ThemesPage.ui',
'gtk_day_variant_combo_row', InternalChildren: [
'gtk_night_variant_combo_row', 'gtk_row',
'shell_row', 'gtk_day_variant_combo_row',
'shell_day_variant_combo_row', 'gtk_night_variant_combo_row',
'shell_night_variant_combo_row', 'shell_row',
'icon_row', 'shell_day_variant_combo_row',
'icon_day_variant_combo_row', 'shell_night_variant_combo_row',
'icon_night_variant_combo_row', 'icon_row',
'cursor_row', 'icon_day_variant_combo_row',
'cursor_day_variant_combo_row', 'icon_night_variant_combo_row',
'cursor_night_variant_combo_row', 'cursor_row',
], 'cursor_day_variant_combo_row',
}, class ThemesPage extends Adw.PreferencesPage { 'cursor_night_variant_combo_row',
constructor(props = {}) { ],
super(props); }, this);
const gtkSettings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.gtk-variants`); }
const shellSettings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.shell-variants`);
const iconSettings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.icon-variants`); constructor({ gtkSettings, shellSettings, iconSettings, cursorSettings, ...params } = {}) {
const cursorSettings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.cursor-variants`); super(params);
gtkSettings.bind('enabled', this._gtk_row, 'enable-expansion', Gio.SettingsBindFlags.DEFAULT); gtkSettings.bind('enabled', this._gtk_row, 'enable-expansion', Gio.SettingsBindFlags.DEFAULT);
@@ -65,7 +66,8 @@ var ThemesPage = GObject.registerClass({
_setupComboRow(this._cursor_day_variant_combo_row, cursorThemesStore, cursorSettings, 'day'); _setupComboRow(this._cursor_day_variant_combo_row, cursorThemesStore, cursorSettings, 'day');
_setupComboRow(this._cursor_night_variant_combo_row, cursorThemesStore, cursorSettings, 'night'); _setupComboRow(this._cursor_night_variant_combo_row, cursorThemesStore, cursorSettings, 'night');
} }
}); }
/** /**
* Set up the model of a combo row. * Set up the model of a combo row.
+23 -21
View File
@@ -1,28 +1,30 @@
// SPDX-FileCopyrightText: 2021 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2021 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { GLib, GObject, Gtk } = imports.gi; import GObject from 'gi://GObject';
const { extensionUtils } = imports.misc; import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension();
var TimeChooser = GObject.registerClass({ export class TimeChooser extends Gtk.Box {
GTypeName: 'TimeChooser', static {
Template: 'resource:///org/gnome/shell/extensions/nightthemeswitcher/preferences/ui/TimeChooser.ui', GObject.registerClass({
InternalChildren: ['hours', 'minutes'], GTypeName: 'TimeChooser',
Properties: { Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/TimeChooser.ui',
time: GObject.ParamSpec.double( InternalChildren: ['hours', 'minutes'],
'time', Properties: {
'Time', time: GObject.ParamSpec.double(
'The time of the chooser', 'time',
GObject.ParamFlags.READWRITE, 'Time',
0, 'The time of the chooser',
24, GObject.ParamFlags.READWRITE,
0 0,
), 24,
}, 0
}, class TimeChooser extends Gtk.Box { ),
},
}, this);
}
onTimeChanged(chooser) { onTimeChanged(chooser) {
const hours = Math.trunc(chooser.time); const hours = Math.trunc(chooser.time);
const minutes = Math.round((chooser.time - hours) * 60); const minutes = Math.round((chooser.time - hours) * 60);
@@ -40,4 +42,4 @@ var TimeChooser = GObject.registerClass({
spin.text = spin.value.toString().padStart(2, '0'); spin.text = spin.value.toString().padStart(2, '0');
return true; return true;
} }
}); }
+64 -43
View File
@@ -3,57 +3,78 @@
'use strict'; 'use strict';
const { Adw, Gdk, Gio, GLib, GObject, Gtk } = imports.gi; import Adw from 'gi://Adw';
const { extensionUtils } = imports.misc; import Gdk from 'gi://Gdk';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension(); import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const _ = extensionUtils.gettext;
/** export default class NightThemeSwitcherPreferences extends ExtensionPreferences {
* Initialize the preferences. /**
*/ * Fill the PreferencesWindow.
function init() { *
extensionUtils.initTranslations(); * @param {Adw.PreferencesWindow} window The PreferencesWindow to fill.
*/
async fillPreferencesWindow(window) {
// Load resources
const resource = Gio.Resource.load(GLib.build_filenamev([this.path, 'resources', 'preferences.gresource']));
Gio.resources_register(resource);
const resource = Gio.Resource.load(GLib.build_filenamev([Me.path, 'resources', 'preferences.gresource'])); // Load icons
Gio.resources_register(resource); const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
iconTheme.add_resource_path('/org/gnome/Shell/Extensions/nightthemeswitcher/preferences/icons');
GObject.type_ensure(Me.imports.preferences.BackgroundsPage.BackgroundsPage); // Set window properties
GObject.type_ensure(Me.imports.preferences.CommandsPage.CommandsPage); window.search_enabled = true;
GObject.type_ensure(Me.imports.preferences.ContributePage.ContributePage); window.set_default_size(640, 600);
GObject.type_ensure(Me.imports.preferences.SchedulePage.SchedulePage);
GObject.type_ensure(Me.imports.preferences.ThemesPage.ThemesPage);
GObject.type_ensure(Me.imports.preferences.BackgroundButton.BackgroundButton); // Add a dummy page until the dynamics imports are done
GObject.type_ensure(Me.imports.preferences.ClearableEntry.ClearableEntry); const dummyPage = new Adw.PreferencesPage();
GObject.type_ensure(Me.imports.preferences.ShortcutButton.ShortcutButton); window.add(dummyPage);
GObject.type_ensure(Me.imports.preferences.TimeChooser.TimeChooser);
const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()); // Dynamically import all classes
iconTheme.add_resource_path('/org/gnome/shell/extensions/nightthemeswitcher/preferences/icons'); const { BackgroundButton } = await import('./preferences/BackgroundButton.js');
} const { BackgroundsPage } = await import('./preferences/BackgroundsPage.js');
const { ClearableEntry } = await import('./preferences/ClearableEntry.js');
const { CommandsPage } = await import('./preferences/CommandsPage.js');
const { ContributePage } = await import('./preferences/ContributePage.js');
const { DropDownChoice } = await import('./preferences/DropDownChoice.js');
const { SchedulePage } = await import('./preferences/SchedulePage.js');
const { ShortcutButton } = await import('./preferences/ShortcutButton.js');
const { ThemesPage } = await import('./preferences/ThemesPage.js');
const { TimeChooser } = await import('./preferences/TimeChooser.js');
/** // Make sure all GObjects are registered
* Fill the PreferencesWindow. GObject.type_ensure(BackgroundButton);
* GObject.type_ensure(BackgroundsPage);
* @param {Adw.PreferencesWindow} window The PreferencesWindow to fill. GObject.type_ensure(ClearableEntry);
*/ GObject.type_ensure(CommandsPage);
function fillPreferencesWindow(window) { GObject.type_ensure(ContributePage);
const { BackgroundsPage } = Me.imports.preferences.BackgroundsPage; GObject.type_ensure(DropDownChoice);
const { CommandsPage } = Me.imports.preferences.CommandsPage; GObject.type_ensure(SchedulePage);
const { ContributePage } = Me.imports.preferences.ContributePage; GObject.type_ensure(ShortcutButton);
const { SchedulePage } = Me.imports.preferences.SchedulePage; GObject.type_ensure(ThemesPage);
const { ThemesPage } = Me.imports.preferences.ThemesPage; GObject.type_ensure(TimeChooser);
[ // Remove the dummy page
new SchedulePage(), window.remove(dummyPage);
new BackgroundsPage(),
new CommandsPage(),
new ThemesPage(),
new ContributePage(),
].forEach(page => window.add(page));
window.search_enabled = true; // Add all pages
window.set_default_size(720, 490); [
new SchedulePage({ settings: this.getSettings(`${this.metadata['settings-schema']}.time`) }),
new BackgroundsPage(),
new CommandsPage({ settings: this.getSettings(`${this.metadata['settings-schema']}.commands`) }),
new ThemesPage({
gtkSettings: this.getSettings(`${this.metadata['settings-schema']}.gtk-variants`),
shellSettings: this.getSettings(`${this.metadata['settings-schema']}.shell-variants`),
iconSettings: this.getSettings(`${this.metadata['settings-schema']}.icon-variants`),
cursorSettings: this.getSettings(`${this.metadata['settings-schema']}.cursor-variants`),
}),
new ContributePage(),
].forEach(page => window.add(page));
}
} }
+9 -145
View File
@@ -1,15 +1,9 @@
// SPDX-FileCopyrightText: 2020, 2021 Romain Vigier <contact AT romainvigier.fr> // SPDX-FileCopyrightText: 2020, 2021 Romain Vigier <contact AT romainvigier.fr>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
const { Gdk, Gio, GLib, Gtk } = imports.gi; import Gio from 'gi://Gio';
const { extensionUtils } = imports.misc; import GLib from 'gi://GLib';
import Gtk from 'gi://Gtk';
const Me = extensionUtils.getCurrentExtension();
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
const _ = Gettext.gettext;
const { ExtensionState } = extensionUtils;
/** /**
@@ -18,7 +12,7 @@ const { ExtensionState } = extensionUtils;
* @param {string} resource The resource to get the directories. * @param {string} resource The resource to get the directories.
* @returns {string[]} An array of paths. * @returns {string[]} An array of paths.
*/ */
function getResourcesDirsPaths(resource) { export function getResourcesDirsPaths(resource) {
return [ return [
GLib.build_filenamev([GLib.get_home_dir(), `.${resource}`]), GLib.build_filenamev([GLib.get_home_dir(), `.${resource}`]),
GLib.build_filenamev([GLib.get_user_data_dir(), resource]), GLib.build_filenamev([GLib.get_user_data_dir(), resource]),
@@ -62,7 +56,7 @@ function getInstalledResources(type) {
* *
* @returns {Set<string>} A set containing all the installed GTK themes names. * @returns {Set<string>} A set containing all the installed GTK themes names.
*/ */
function getInstalledGtkThemes() { export function getInstalledGtkThemes() {
const themes = new Set(); const themes = new Set();
getInstalledResources('themes').forEach(theme => { getInstalledResources('themes').forEach(theme => {
const version = [0, Gtk.MINOR_VERSION].find(gtkVersion => { const version = [0, Gtk.MINOR_VERSION].find(gtkVersion => {
@@ -82,7 +76,7 @@ function getInstalledGtkThemes() {
* *
* @returns {Set<string>} A set containing all the installed shell themes names. * @returns {Set<string>} A set containing all the installed shell themes names.
*/ */
function getInstalledShellThemes() { export function getInstalledShellThemes() {
const themes = new Set(['']); const themes = new Set(['']);
getInstalledResources('themes').forEach(theme => { getInstalledResources('themes').forEach(theme => {
const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'gnome-shell', 'gnome-shell.css'])); const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'gnome-shell', 'gnome-shell.css']));
@@ -97,7 +91,7 @@ function getInstalledShellThemes() {
* *
* @returns {Set<string>} A set containing all the installed icon themes names. * @returns {Set<string>} A set containing all the installed icon themes names.
*/ */
function getInstalledIconThemes() { export function getInstalledIconThemes() {
const themes = new Set(); const themes = new Set();
getInstalledResources('icons').forEach(theme => { getInstalledResources('icons').forEach(theme => {
const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'index.theme'])); const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'index.theme']));
@@ -113,7 +107,7 @@ function getInstalledIconThemes() {
* *
* @returns {Set<string>} A set containing all the installed cursor themes names. * @returns {Set<string>} A set containing all the installed cursor themes names.
*/ */
function getInstalledCursorThemes() { export function getInstalledCursorThemes() {
const themes = new Set(); const themes = new Set();
getInstalledResources('icons').forEach(theme => { getInstalledResources('icons').forEach(theme => {
const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'cursors'])); const themeFile = Gio.File.new_for_path(GLib.build_filenamev([theme.get('path'), 'cursors']));
@@ -123,136 +117,6 @@ function getInstalledCursorThemes() {
return themes; return themes;
} }
/**
* Get the User Themes extension.
*
* @returns {object|undefined} The User Themes extension object or undefined if
* it isn't installed.
*/
function getUserthemesExtension() {
try {
return imports.ui.main.extensionManager.lookup('user-theme@gnome-shell-extensions.gcampax.github.com');
} catch (_e) {
return undefined;
}
}
/**
* Get the User Themes extension settings.
*
* @returns {Gio.Settings|null} The User Themes extension settings or null if
* the extension isn't installed.
*/
function getUserthemesSettings() {
let extension = getUserthemesExtension();
if (!extension || extension.state !== ExtensionState.ENABLED)
return null;
const schemaDir = extension.dir.get_child('schemas');
const GioSSS = Gio.SettingsSchemaSource;
let schemaSource;
if (schemaDir.query_exists(null))
schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), GioSSS.get_default(), false);
else
schemaSource = GioSSS.get_default();
const schemaObj = schemaSource.lookup('org.gnome.shell.extensions.user-theme', true);
return new Gio.Settings({ settings_schema: schemaObj });
}
/**
* Get the shell theme stylesheet.
*
* @param {string} theme The shell theme name.
* @returns {string|null} Path to the shell theme stylesheet.
*/
function getShellThemeStylesheet(theme) {
const themeName = theme ? `'${theme}'` : 'default';
console.debug(`Getting the ${themeName} theme shell stylesheet...`);
let stylesheet = null;
if (theme) {
const stylesheetPaths = getResourcesDirsPaths('themes').map(path => GLib.build_filenamev([path, theme, 'gnome-shell', 'gnome-shell.css']));
stylesheet = stylesheetPaths.find(path => {
const file = Gio.file_new_for_path(path);
return file.query_exists(null);
});
}
return stylesheet;
}
/**
* Apply a stylesheet to the shell.
*
* @param {string} stylesheet The shell stylesheet to apply.
*/
function applyShellStylesheet(stylesheet) {
console.debug('Applying shell stylesheet...');
imports.ui.main.setThemeStylesheet(stylesheet);
imports.ui.main.loadTheme();
console.debug('Shell stylesheet applied.');
}
/**
* Check if the given keyval is forbidden.
*
* @param {number} keyval The keyval number.
* @returns {boolean} `true` if the keyval is forbidden.
*/
function isKeyvalForbidden(keyval) {
const forbiddenKeyvals = [
Gdk.KEY_Home,
Gdk.KEY_Left,
Gdk.KEY_Up,
Gdk.KEY_Right,
Gdk.KEY_Down,
Gdk.KEY_Page_Up,
Gdk.KEY_Page_Down,
Gdk.KEY_End,
Gdk.KEY_Tab,
Gdk.KEY_KP_Enter,
Gdk.KEY_Return,
Gdk.KEY_Mode_switch,
];
return forbiddenKeyvals.includes(keyval);
}
/**
* Check if the given key combo is a valid binding
*
* @param {{mask: number, keycode: number, keyval:number}} combo An object
* representing the key combo.
* @returns {boolean} `true` if the key combo is a valid binding.
*/
function isBindingValid({ mask, keycode, keyval }) {
if ((mask === 0 || mask === Gdk.SHIFT_MASK) && keycode !== 0) {
if (
(keyval >= Gdk.KEY_a && keyval <= Gdk.KEY_z) ||
(keyval >= Gdk.KEY_A && keyval <= Gdk.KEY_Z) ||
(keyval >= Gdk.KEY_0 && keyval <= Gdk.KEY_9) ||
(keyval >= Gdk.KEY_kana_fullstop && keyval <= Gdk.KEY_semivoicedsound) ||
(keyval >= Gdk.KEY_Arabic_comma && keyval <= Gdk.KEY_Arabic_sukun) ||
(keyval >= Gdk.KEY_Serbian_dje && keyval <= Gdk.KEY_Cyrillic_HARDSIGN) ||
(keyval >= Gdk.KEY_Greek_ALPHAaccent && keyval <= Gdk.KEY_Greek_omega) ||
(keyval >= Gdk.KEY_hebrew_doublelowline && keyval <= Gdk.KEY_hebrew_taf) ||
(keyval >= Gdk.KEY_Thai_kokai && keyval <= Gdk.KEY_Thai_lekkao) ||
(keyval >= Gdk.KEY_Hangul_Kiyeog && keyval <= Gdk.KEY_Hangul_J_YeorinHieuh) ||
(keyval === Gdk.KEY_space && mask === 0) ||
isKeyvalForbidden(keyval)
)
return false;
}
return true;
}
/**
* Check if the given key combo is a valid accelerator.
*
* @param {{mask: number, keyval:number}} combo An object representing the key
* combo.
* @returns {boolean} `true` if the key combo is a valid accelerator.
*/
function isAccelValid({ mask, keyval }) {
return Gtk.accelerator_valid(keyval, mask) || (keyval === Gdk.KEY_Tab && mask !== 0);
}
/** /**
* Find an item in a `Gio.ListModel`. * Find an item in a `Gio.ListModel`.
* *
@@ -260,7 +124,7 @@ function isAccelValid({ mask, keyval }) {
* @param {Function} findFunction The function used to find the item. Gets the item as argument. * @param {Function} findFunction The function used to find the item. Gets the item as argument.
* @returns {(*|undefined)} The found item or `undefined`. * @returns {(*|undefined)} The found item or `undefined`.
*/ */
function findItemPositionInModel(model, findFunction) { export function findItemPositionInModel(model, findFunction) {
const nItems = model.get_n_items(); const nItems = model.get_n_items();
for (let i = 0; i < nItems; i++) { for (let i = 0; i < nItems; i++) {
if (findFunction(model.get_item(i))) if (findFunction(model.get_item(i)))