Project: Switch to Prettier for code formatting
This commit is contained in:
+3
-10
@@ -9,10 +9,7 @@ gnome.compile_resources(
|
||||
install_dir: INSTALL_DIR / 'resources',
|
||||
)
|
||||
|
||||
install_data(
|
||||
DNS + '.gschema.xml',
|
||||
install_dir: INSTALL_DIR / 'schemas'
|
||||
)
|
||||
install_data(DNS + '.gschema.xml', install_dir: INSTALL_DIR / 'schemas')
|
||||
|
||||
if not get_option('pack')
|
||||
glib_compile_schemas_exe = find_program('glib-compile-schemas')
|
||||
@@ -21,12 +18,8 @@ if not get_option('pack')
|
||||
build_by_default: true,
|
||||
build_always_stale: true,
|
||||
output: 'gschemas.compiled',
|
||||
command: [
|
||||
glib_compile_schemas_exe,
|
||||
'--targetdir=@OUTDIR@',
|
||||
'@CURRENT_SOURCE_DIR@'
|
||||
],
|
||||
command: [glib_compile_schemas_exe, '--targetdir=@OUTDIR@', '@CURRENT_SOURCE_DIR@'],
|
||||
install: true,
|
||||
install_dir: INSTALL_DIR / 'schemas'
|
||||
install_dir: INSTALL_DIR / 'schemas',
|
||||
)
|
||||
endif
|
||||
|
||||
+1
-3
@@ -3,10 +3,8 @@
|
||||
|
||||
/**
|
||||
* Print a message in debug builds.
|
||||
*
|
||||
* @param {string} msg Message to print.
|
||||
*/
|
||||
export function message(msg) {
|
||||
if (NTS.metadata['build-type'] === 'debug')
|
||||
console.log(`[${NTS.metadata.name}] ${msg}`);
|
||||
if (NTS.metadata["build-type"] === "debug") console.log(`[${NTS.metadata.name}] ${msg}`);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
/**
|
||||
* Color Schemes.
|
||||
*
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const ColorScheme = {
|
||||
DEFAULT: 'default',
|
||||
PREFER_DARK: 'prefer-dark',
|
||||
PREFER_LIGHT: 'prefer-night',
|
||||
DEFAULT: "default",
|
||||
PREFER_DARK: "prefer-dark",
|
||||
PREFER_LIGHT: "prefer-night",
|
||||
};
|
||||
|
||||
+3
-4
@@ -3,12 +3,11 @@
|
||||
|
||||
/**
|
||||
* Times.
|
||||
*
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
export const Time = {
|
||||
UNKNOWN: 'unknown',
|
||||
DAY: 'day',
|
||||
NIGHT: 'night',
|
||||
UNKNOWN: "unknown",
|
||||
DAY: "day",
|
||||
NIGHT: "night",
|
||||
};
|
||||
|
||||
+27
-30
@@ -1,49 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
|
||||
|
||||
import * as debug from './debug.js';
|
||||
|
||||
import { ColorSchemeSwitcher } from './modules/ColorSchemeSwitcher.js';
|
||||
import { CommandsSwitcher } from './modules/CommandsSwitcher.js';
|
||||
import { Timer } from './modules/Timer.js';
|
||||
import * as debug from "./debug.js";
|
||||
|
||||
import { ColorSchemeSwitcher } from "./modules/ColorSchemeSwitcher.js";
|
||||
import { CommandsSwitcher } from "./modules/CommandsSwitcher.js";
|
||||
import { Timer } from "./modules/Timer.js";
|
||||
|
||||
export default class NightThemeSwitcher extends Extension {
|
||||
#modules = [];
|
||||
#modules = [];
|
||||
|
||||
enable() {
|
||||
globalThis.NTS = this;
|
||||
enable() {
|
||||
globalThis.NTS = this;
|
||||
|
||||
debug.message('Enabling extension...');
|
||||
debug.message("Enabling extension...");
|
||||
|
||||
const timer = new Timer();
|
||||
const timer = new Timer();
|
||||
|
||||
[
|
||||
timer,
|
||||
new ColorSchemeSwitcher({ timer }),
|
||||
new CommandsSwitcher({ timer }),
|
||||
].forEach(module => this.#modules.push(module));
|
||||
[timer, new ColorSchemeSwitcher({ timer }), new CommandsSwitcher({ timer })].forEach((module) =>
|
||||
this.#modules.push(module),
|
||||
);
|
||||
|
||||
this.#modules.forEach(module => module.enable());
|
||||
this.#modules.forEach((module) => module.enable());
|
||||
|
||||
debug.message('Extension enabled.');
|
||||
}
|
||||
debug.message("Extension enabled.");
|
||||
}
|
||||
|
||||
disable() {
|
||||
// Extension won't be disabled in `unlock-dialog` session mode. This is
|
||||
// to enable the color scheme switch while the lock screen is displayed,
|
||||
// as the background image and the shell theme are visible in this mode.
|
||||
debug.message('Disabling extension...');
|
||||
disable() {
|
||||
// Extension won't be disabled in `unlock-dialog` session mode. This is
|
||||
// to enable the color scheme switch while the lock screen is displayed,
|
||||
// as the background image and the shell theme are visible in this mode.
|
||||
debug.message("Disabling extension...");
|
||||
|
||||
this.#modules.forEach(module => module.disable());
|
||||
this.#modules = [];
|
||||
this.#modules.forEach((module) => module.disable());
|
||||
this.#modules = [];
|
||||
|
||||
debug.message('Extension disabled.');
|
||||
debug.message("Extension disabled.");
|
||||
|
||||
delete globalThis.NTS;
|
||||
}
|
||||
delete globalThis.NTS;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -8,9 +8,9 @@ configure_file(
|
||||
'DNS': DNS,
|
||||
'UUID': UUID,
|
||||
'VERSION': meson.project_version(),
|
||||
'BUILD_TYPE': get_option('buildtype')
|
||||
'BUILD_TYPE': get_option('buildtype'),
|
||||
},
|
||||
install_dir: INSTALL_DIR
|
||||
install_dir: INSTALL_DIR,
|
||||
)
|
||||
|
||||
main = [
|
||||
@@ -56,6 +56,6 @@ if (get_option('pack'))
|
||||
meson.project_source_root() / 'build-aux' / 'pack.sh',
|
||||
zip_exe.full_path(),
|
||||
UUID,
|
||||
meson.project_source_root() / get_option('packdir')
|
||||
meson.project_source_root() / get_option('packdir'),
|
||||
)
|
||||
endif
|
||||
|
||||
+10
-10
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Night Theme Switcher",
|
||||
"description": "Automatically toggle your desktop’s color scheme between light and dark, switch backgrounds and run custom commands at sunset and sunrise.",
|
||||
"uuid": "@UUID@",
|
||||
"gettext-domain": "@UUID@",
|
||||
"settings-schema": "@DNS@",
|
||||
"url": "https://nightthemeswitcher.romainvigier.fr",
|
||||
"session-modes": ["unlock-dialog", "user"],
|
||||
"shell-version": ["50"],
|
||||
"version": @VERSION@,
|
||||
"build-type": "@BUILD_TYPE@"
|
||||
"name": "Night Theme Switcher",
|
||||
"description": "Automatically toggle your desktop’s color scheme between light and dark, switch backgrounds and run custom commands at sunset and sunrise.",
|
||||
"uuid": "@UUID@",
|
||||
"gettext-domain": "@UUID@",
|
||||
"settings-schema": "@DNS@",
|
||||
"url": "https://nightthemeswitcher.romainvigier.fr",
|
||||
"session-modes": ["unlock-dialog", "user"],
|
||||
"shell-version": ["50"],
|
||||
"version": @VERSION@,
|
||||
"build-type": "@BUILD_TYPE@"
|
||||
}
|
||||
|
||||
@@ -1,88 +1,87 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Gio from 'gi://Gio';
|
||||
import Gio from "gi://Gio";
|
||||
|
||||
import * as debug from '../debug.js';
|
||||
import * as debug from "../debug.js";
|
||||
|
||||
import { Time } from '../enums/Time.js';
|
||||
import { Time } from "../enums/Time.js";
|
||||
|
||||
import { Switcher } from './Switcher.js';
|
||||
import { Switcher } from "./Switcher.js";
|
||||
|
||||
/**
|
||||
* The Color Scheme Switcher changes the system color scheme according to the time.
|
||||
*/
|
||||
export class ColorSchemeSwitcher extends Switcher {
|
||||
#settings;
|
||||
#interfaceSettings;
|
||||
#timer;
|
||||
#settings;
|
||||
#interfaceSettings;
|
||||
#timer;
|
||||
|
||||
#settingsConnections = [];
|
||||
#settingsConnections = [];
|
||||
|
||||
/**
|
||||
* @param {object} params Params object.
|
||||
* @param {Timer} params.timer Timer to listen to.
|
||||
*/
|
||||
constructor({ timer }) {
|
||||
const settings = NTS.getSettings(`${NTS.metadata['settings-schema']}.color-scheme`);
|
||||
super({
|
||||
name: 'Color Scheme',
|
||||
timer,
|
||||
settings,
|
||||
callback: time => this.#onTimeChanged(time),
|
||||
});
|
||||
this.#timer = timer;
|
||||
this.#settings = settings;
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
}
|
||||
/**
|
||||
* @param {object} params Params object.
|
||||
* @param {import("./Timer.js").Timer} params.timer Timer to listen to.
|
||||
*/
|
||||
constructor({ timer }) {
|
||||
const settings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.color-scheme`);
|
||||
super({
|
||||
name: "Color Scheme",
|
||||
timer,
|
||||
settings,
|
||||
callback: (time) => this.#onTimeChanged(time),
|
||||
});
|
||||
this.#timer = timer;
|
||||
this.#settings = settings;
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" });
|
||||
}
|
||||
|
||||
enable() {
|
||||
super.enable();
|
||||
this.#connectSettings();
|
||||
}
|
||||
enable() {
|
||||
super.enable();
|
||||
this.#connectSettings();
|
||||
}
|
||||
|
||||
disable() {
|
||||
super.disable();
|
||||
this.#disconnectSettings();
|
||||
}
|
||||
disable() {
|
||||
super.disable();
|
||||
this.#disconnectSettings();
|
||||
}
|
||||
|
||||
#connectSettings() {
|
||||
debug.message('Connecting Color Scheme Switcher to settings...');
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect('changed::day', this.#onColorSchemeChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect('changed::night', this.#onColorSchemeChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#interfaceSettings,
|
||||
id: this.#interfaceSettings.connect('changed::color-scheme', this.#onSystemColorSchemeChanged.bind(this)),
|
||||
});
|
||||
}
|
||||
#connectSettings() {
|
||||
debug.message("Connecting Color Scheme Switcher to settings...");
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect("changed::day", this.#onColorSchemeChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect("changed::night", this.#onColorSchemeChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#interfaceSettings,
|
||||
id: this.#interfaceSettings.connect("changed::color-scheme", this.#onSystemColorSchemeChanged.bind(this)),
|
||||
});
|
||||
}
|
||||
|
||||
#disconnectSettings() {
|
||||
this.#settingsConnections.forEach(({ settings, id }) => settings.disconnect(id));
|
||||
this.#settingsConnections = [];
|
||||
debug.message('Disconnected Color Scheme Switcher from settings.');
|
||||
}
|
||||
#disconnectSettings() {
|
||||
this.#settingsConnections.forEach(({ settings, id }) => settings.disconnect(id));
|
||||
this.#settingsConnections = [];
|
||||
debug.message("Disconnected Color Scheme Switcher from settings.");
|
||||
}
|
||||
|
||||
#onTimeChanged(time) {
|
||||
const colorScheme = time === Time.NIGHT ? this.#settings.get_string('night') : this.#settings.get_string('day');
|
||||
this.#interfaceSettings.set_string('color-scheme', colorScheme);
|
||||
}
|
||||
#onTimeChanged(time) {
|
||||
const colorScheme = time === Time.NIGHT ? this.#settings.get_string("night") : this.#settings.get_string("day");
|
||||
this.#interfaceSettings.set_string("color-scheme", colorScheme);
|
||||
}
|
||||
|
||||
#onColorSchemeChanged(_settings, time) {
|
||||
const colorScheme = this.#settings.get_string(time);
|
||||
debug.message(`${time} color scheme changed to ${colorScheme}.`);
|
||||
if (time === this.#timer.time)
|
||||
this.#interfaceSettings.set_string('color-scheme', colorScheme);
|
||||
}
|
||||
#onColorSchemeChanged(_settings, time) {
|
||||
const colorScheme = this.#settings.get_string(time);
|
||||
debug.message(`${time} color scheme changed to ${colorScheme}.`);
|
||||
if (time === this.#timer.time) this.#interfaceSettings.set_string("color-scheme", colorScheme);
|
||||
}
|
||||
|
||||
#onSystemColorSchemeChanged() {
|
||||
const colorScheme = this.#interfaceSettings.get_string('color-scheme');
|
||||
debug.message(`System color scheme changed to ${colorScheme}.`);
|
||||
this.#timer.syncTimeToColorScheme(colorScheme);
|
||||
}
|
||||
#onSystemColorSchemeChanged() {
|
||||
const colorScheme = this.#interfaceSettings.get_string("color-scheme");
|
||||
debug.message(`System color scheme changed to ${colorScheme}.`);
|
||||
this.#timer.syncTimeToColorScheme(colorScheme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import GLib from 'gi://GLib';
|
||||
import GLib from "gi://GLib";
|
||||
|
||||
import * as debug from '../debug.js';
|
||||
import * as debug from "../debug.js";
|
||||
|
||||
import { Time } from '../enums/Time.js';
|
||||
|
||||
import { Switcher } from './Switcher.js';
|
||||
import { Time } from "../enums/Time.js";
|
||||
|
||||
import { Switcher } from "./Switcher.js";
|
||||
|
||||
/**
|
||||
* The Commands Switcher spawns commands according to the time.
|
||||
*/
|
||||
export class CommandsSwitcher extends Switcher {
|
||||
#settings;
|
||||
#settings;
|
||||
|
||||
/**
|
||||
* @param {object} params Params object.
|
||||
* @param {Timer} params.timer Timer to listen to.
|
||||
*/
|
||||
constructor({ timer }) {
|
||||
const settings = NTS.getSettings(`${NTS.metadata['settings-schema']}.commands`);
|
||||
super({
|
||||
name: 'Command',
|
||||
timer,
|
||||
settings,
|
||||
callback: time => this.#onTimeChanged(time),
|
||||
disableable: true,
|
||||
});
|
||||
this.#settings = settings;
|
||||
}
|
||||
/**
|
||||
* @param {object} params Params object.
|
||||
* @param {import("./Timer.js").Timer} params.timer Timer to listen to.
|
||||
*/
|
||||
constructor({ timer }) {
|
||||
const settings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.commands`);
|
||||
super({
|
||||
name: "Command",
|
||||
timer,
|
||||
settings,
|
||||
callback: (time) => this.#onTimeChanged(time),
|
||||
disableable: true,
|
||||
});
|
||||
this.#settings = settings;
|
||||
}
|
||||
|
||||
#onTimeChanged(time) {
|
||||
if (time === Time.UNKNOWN)
|
||||
return;
|
||||
const command = this.#settings.get_string(time === Time.DAY ? 'sunrise' : 'sunset');
|
||||
if (!command)
|
||||
return;
|
||||
GLib.spawn_async(null, ['sh', '-c', command], null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
debug.message(`Spawned ${time} command.`);
|
||||
}
|
||||
#onTimeChanged(time) {
|
||||
if (time === Time.UNKNOWN) return;
|
||||
const command = this.#settings.get_string(time === Time.DAY ? "sunrise" : "sunset");
|
||||
if (!command) return;
|
||||
GLib.spawn_async(null, ["sh", "-c", command], null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
debug.message(`Spawned ${time} command.`);
|
||||
}
|
||||
}
|
||||
|
||||
+72
-77
@@ -1,101 +1,96 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import * as debug from '../debug.js';
|
||||
|
||||
import * as debug from "../debug.js";
|
||||
|
||||
/**
|
||||
* Function called when the time changes.
|
||||
*
|
||||
* @callback TimeChangedCallback
|
||||
* @param {Time} time New time.
|
||||
* @param {import("../enums/Time.js").Time} time New time.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The Switcher runs a callback function when the time changes.
|
||||
*
|
||||
*/
|
||||
export class Switcher {
|
||||
#name;
|
||||
#timer;
|
||||
#settings;
|
||||
#callback;
|
||||
#disableable;
|
||||
#name;
|
||||
#timer;
|
||||
#settings;
|
||||
#callback;
|
||||
#disableable;
|
||||
|
||||
#statusConnection = null;
|
||||
#timerConnection = null;
|
||||
#statusConnection = 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.
|
||||
* @param {TimeChangedCallback} params.callback Callback function.
|
||||
* @param {boolean} params.disableable If the switcher can be disabled using an `enabled` key in the settings.
|
||||
*/
|
||||
constructor({ name, timer, settings, callback, disableable = false }) {
|
||||
this.#name = name;
|
||||
this.#timer = timer;
|
||||
this.#settings = settings;
|
||||
this.#callback = callback;
|
||||
this.#disableable = disableable;
|
||||
/**
|
||||
* @param {object} params Params object.
|
||||
* @param {string} params.name Name of the switcher.
|
||||
* @param {import("./Timer.js").Timer} params.timer Timer to listen to.
|
||||
* @param {import("gi://Gio").default.Settings} params.settings Settings.
|
||||
* @param {TimeChangedCallback} params.callback Callback function.
|
||||
* @param {boolean} params.disableable If the switcher can be disabled using an `enabled` key in the settings.
|
||||
*/
|
||||
constructor({ name, timer, settings, callback, disableable = false }) {
|
||||
this.#name = name;
|
||||
this.#timer = timer;
|
||||
this.#settings = settings;
|
||||
this.#callback = callback;
|
||||
this.#disableable = disableable;
|
||||
}
|
||||
|
||||
enable() {
|
||||
debug.message(`Enabling ${this.#name} switcher...`);
|
||||
if (this.#disableable) this.#watchStatus();
|
||||
if (!this.#disableable || this.#settings.get_boolean("enabled")) {
|
||||
this.#connectTimer();
|
||||
this.#onTimeChanged();
|
||||
}
|
||||
debug.message(`${this.#name} switcher enabled.`);
|
||||
}
|
||||
|
||||
enable() {
|
||||
debug.message(`Enabling ${this.#name} switcher...`);
|
||||
if (this.#disableable)
|
||||
this.#watchStatus();
|
||||
if (!this.#disableable || this.#settings.get_boolean('enabled')) {
|
||||
this.#connectTimer();
|
||||
this.#onTimeChanged();
|
||||
}
|
||||
debug.message(`${this.#name} switcher enabled.`);
|
||||
disable() {
|
||||
debug.message(`Disabling ${this.#name} switcher...`);
|
||||
this.#disconnectTimer();
|
||||
if (this.#disableable) this.#unwatchStatus();
|
||||
debug.message(`${this.#name} switcher disabled.`);
|
||||
}
|
||||
|
||||
#watchStatus() {
|
||||
debug.message(`Watching ${this.#name} switching status...`);
|
||||
this.#statusConnection = this.#settings.connect("changed::enabled", this.#onStatusChanged.bind(this));
|
||||
}
|
||||
|
||||
#unwatchStatus() {
|
||||
if (this.#statusConnection) {
|
||||
this.#settings.disconnect(this.#statusConnection);
|
||||
this.#statusConnection = null;
|
||||
}
|
||||
debug.message(`Stopped watching ${this.#name} switching status.`);
|
||||
}
|
||||
|
||||
disable() {
|
||||
debug.message(`Disabling ${this.#name} switcher...`);
|
||||
this.#disconnectTimer();
|
||||
if (this.#disableable)
|
||||
this.#unwatchStatus();
|
||||
debug.message(`${this.#name} switcher disabled.`);
|
||||
#connectTimer() {
|
||||
debug.message(`Connecting ${this.#name} switcher to Timer...`);
|
||||
this.#timerConnection = this.#timer.connect("notify::time", this.#onTimeChanged.bind(this));
|
||||
}
|
||||
|
||||
#disconnectTimer() {
|
||||
if (this.#timerConnection) {
|
||||
this.#timer.disconnect(this.#timerConnection);
|
||||
this.#timerConnection = null;
|
||||
}
|
||||
debug.message(`Disconnected ${this.#name} switcher from Timer.`);
|
||||
}
|
||||
|
||||
#onStatusChanged() {
|
||||
debug.message(
|
||||
`${this.#name} switching has been ${this.#settings.get_boolean("enabled") ? "enabled" : "disabled"}.`,
|
||||
);
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
#watchStatus() {
|
||||
debug.message(`Watching ${this.#name} switching status...`);
|
||||
this.#statusConnection = this.#settings.connect('changed::enabled', this.#onStatusChanged.bind(this));
|
||||
}
|
||||
|
||||
#unwatchStatus() {
|
||||
if (this.#statusConnection) {
|
||||
this.#settings.disconnect(this.#statusConnection);
|
||||
this.#statusConnection = null;
|
||||
}
|
||||
debug.message(`Stopped watching ${this.#name} switching status.`);
|
||||
}
|
||||
|
||||
#connectTimer() {
|
||||
debug.message(`Connecting ${this.#name} switcher to Timer...`);
|
||||
this.#timerConnection = this.#timer.connect('notify::time', this.#onTimeChanged.bind(this));
|
||||
}
|
||||
|
||||
#disconnectTimer() {
|
||||
if (this.#timerConnection) {
|
||||
this.#timer.disconnect(this.#timerConnection);
|
||||
this.#timerConnection = null;
|
||||
}
|
||||
debug.message(`Disconnected ${this.#name} switcher from Timer.`);
|
||||
}
|
||||
|
||||
|
||||
#onStatusChanged() {
|
||||
debug.message(`${this.#name} switching has been ${this.#settings.get_boolean('enabled') ? 'enabled' : 'disabled'}.`);
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
#onTimeChanged() {
|
||||
this.#callback(this.#timer.time);
|
||||
}
|
||||
#onTimeChanged() {
|
||||
this.#callback(this.#timer.time);
|
||||
}
|
||||
}
|
||||
|
||||
+330
-319
@@ -1,22 +1,21 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Geoclue from 'gi://Geoclue';
|
||||
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';
|
||||
import Geoclue from "gi://Geoclue";
|
||||
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";
|
||||
|
||||
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';
|
||||
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";
|
||||
|
||||
import * as debug from '../debug.js';
|
||||
|
||||
import { ColorScheme } from '../enums/ColorScheme.js'; // eslint-disable-line no-unused-vars
|
||||
import { Time } from '../enums/Time.js';
|
||||
import * as debug from "../debug.js";
|
||||
|
||||
import { ColorScheme } from "../enums/ColorScheme.js";
|
||||
import { Time } from "../enums/Time.js";
|
||||
|
||||
/**
|
||||
* The Timer is responsible for signaling any time change to the other modules.
|
||||
@@ -28,353 +27,365 @@ import { Time } from '../enums/Time.js';
|
||||
* forced the manual schedule in the preferences.
|
||||
*/
|
||||
export class Timer extends GObject.Object {
|
||||
#settings;
|
||||
#colorSchemeSettings;
|
||||
#interfaceSettings;
|
||||
#locationSettings;
|
||||
#time;
|
||||
#settings;
|
||||
#colorSchemeSettings;
|
||||
#interfaceSettings;
|
||||
#locationSettings;
|
||||
/** @type {Time | undefined} */
|
||||
#time;
|
||||
|
||||
#cancellable = null;
|
||||
#previousKeybinding = null;
|
||||
#timeTimeoutId = null;
|
||||
#geoclue = null;
|
||||
#geoclueLocationConnectionId = null;
|
||||
#suntimesTimeoutId = null;
|
||||
#manuallySetTime = false;
|
||||
#cancellable = null;
|
||||
#previousKeybinding = null;
|
||||
#timeTimeoutId = null;
|
||||
#geoclue = null;
|
||||
#geoclueLocationConnectionId = null;
|
||||
#suntimesTimeoutId = null;
|
||||
#manuallySetTime = false;
|
||||
|
||||
#settingsConnections = [];
|
||||
#settingsConnections = [];
|
||||
|
||||
static {
|
||||
GObject.registerClass({
|
||||
Properties: {
|
||||
time: GObject.ParamSpec.string('time', 'Time', 'Time', GObject.ParamFlags.READWRITE, Time.UNKNOWN),
|
||||
},
|
||||
}, this);
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
Properties: {
|
||||
time: GObject.ParamSpec.string("time", "Time", "Time", GObject.ParamFlags.READWRITE, Time.UNKNOWN),
|
||||
},
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#settings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.time`);
|
||||
this.#colorSchemeSettings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.color-scheme`);
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" });
|
||||
this.#locationSettings = new Gio.Settings({ schema: "org.gnome.system.location" });
|
||||
}
|
||||
|
||||
enable() {
|
||||
debug.message("Enabling Timer...");
|
||||
this.#cancellable = new Gio.Cancellable();
|
||||
this.#connectSettings();
|
||||
this.#trackTime();
|
||||
if (this.#settings.get_boolean("manual-schedule")) {
|
||||
debug.message("Using the manual schedule.");
|
||||
} else {
|
||||
debug.message("Using location.");
|
||||
this.#trackLocation();
|
||||
this.#trackSuntimes();
|
||||
}
|
||||
this.#addKeybinding();
|
||||
this.#changeTime(this.#computeTime());
|
||||
debug.message("Timer enabled.");
|
||||
}
|
||||
|
||||
disable() {
|
||||
debug.message("Disabling Timer...");
|
||||
this.#removeKeybinding();
|
||||
this.#untrackSuntimes();
|
||||
this.#untrackLocation();
|
||||
this.#untrackTime();
|
||||
this.#disconnectSettings();
|
||||
this.#cancellable.cancel();
|
||||
debug.message("Timer disabled.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ColorScheme} colorScheme Color scheme to sync the time to.
|
||||
*/
|
||||
syncTimeToColorScheme(colorScheme) {
|
||||
this.#changeTime(this.#colorSchemeToTime(colorScheme), true);
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this.#time || Time.UNKNOWN;
|
||||
}
|
||||
|
||||
#changeTime(time, manual = false) {
|
||||
if (time === this.#time) {
|
||||
if (!manual && this.#manuallySetTime) this.#manuallySetTime = false;
|
||||
return;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#settings = NTS.getSettings(`${NTS.metadata['settings-schema']}.time`);
|
||||
this.#colorSchemeSettings = NTS.getSettings(`${NTS.metadata['settings-schema']}.color-scheme`);
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this.#locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
|
||||
if (!manual && time !== this.#time && this.#manuallySetTime) return;
|
||||
|
||||
this.#time = time;
|
||||
this.#manuallySetTime = manual;
|
||||
|
||||
debug.message(manual ? `Time manually set to ${time}.` : `Time changed to ${time}.`);
|
||||
|
||||
const isMonitorFullscreen = layoutManager.monitors.some((monitor) => monitor.inFullscreen);
|
||||
if (this.#settings.get_boolean("fullscreen-transition") || !isMonitorFullscreen) {
|
||||
try {
|
||||
layoutManager.screenTransition.run();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
enable() {
|
||||
debug.message('Enabling Timer...');
|
||||
this.#cancellable = new Gio.Cancellable();
|
||||
this.#connectSettings();
|
||||
this.#trackTime();
|
||||
if (this.#settings.get_boolean('manual-schedule')) {
|
||||
debug.message('Using the manual schedule.');
|
||||
} else {
|
||||
debug.message('Using location.');
|
||||
this.#trackLocation();
|
||||
this.#trackSuntimes();
|
||||
}
|
||||
this.#addKeybinding();
|
||||
this.#changeTime(this.#computeTime());
|
||||
debug.message('Timer enabled.');
|
||||
this.notify("time");
|
||||
}
|
||||
|
||||
#connectSettings() {
|
||||
debug.message("Connecting Timer to settings...");
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#locationSettings,
|
||||
id: this.#locationSettings.connect("changed::enabled", this.#onLocationStateChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect("changed::manual-schedule", this.#onManualScheduleStateChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect(
|
||||
"changed::nightthemeswitcher-ondemand-keybinding",
|
||||
this.#onOndemandKeybindingChanged.bind(this),
|
||||
),
|
||||
});
|
||||
// Only listen to the offset setting when not using a manual schedule
|
||||
if (!this.#settings.get_boolean("manual-schedule")) {
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect("changed::offset", this.#onOffsetChanged.bind(this)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
debug.message('Disabling Timer...');
|
||||
this.#removeKeybinding();
|
||||
this.#untrackSuntimes();
|
||||
this.#untrackLocation();
|
||||
this.#untrackTime();
|
||||
this.#disconnectSettings();
|
||||
this.#cancellable.cancel();
|
||||
debug.message('Timer disabled.');
|
||||
#disconnectSettings() {
|
||||
this.#settingsConnections.forEach(({ settings, id }) => settings.disconnect(id));
|
||||
this.#settingsConnections = [];
|
||||
debug.message("Disconnected Timer from settings.");
|
||||
}
|
||||
|
||||
#trackTime() {
|
||||
debug.message("Watching for time change...");
|
||||
this.#timeTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
this.#changeTime(this.#computeTime());
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
#untrackTime() {
|
||||
if (this.#timeTimeoutId) {
|
||||
GLib.Source.remove(this.#timeTimeoutId);
|
||||
this.#timeTimeoutId = null;
|
||||
}
|
||||
debug.message("Stopped watching for time change.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ColorScheme} colorScheme Color scheme to sync the time to.
|
||||
*/
|
||||
syncTimeToColorScheme(colorScheme) {
|
||||
this.#changeTime(this.#colorSchemeToTime(colorScheme), true);
|
||||
#trackLocation() {
|
||||
debug.message("Connecting to GeoClue...");
|
||||
Geoclue.Simple.new(
|
||||
"org.gnome.Shell",
|
||||
Geoclue.AccuracyLevel.CITY,
|
||||
this.#cancellable,
|
||||
this.#onGeoclueReady.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
#untrackLocation() {
|
||||
debug.message("Disconnecting from GeoClue...");
|
||||
if (this.#geoclue && this.#geoclueLocationConnectionId) {
|
||||
this.#geoclue.disconnect(this.#geoclueLocationConnectionId);
|
||||
this.#geoclueLocationConnectionId = null;
|
||||
this.#geoclue = null;
|
||||
}
|
||||
debug.message("Disconnected from GeoClue.");
|
||||
}
|
||||
|
||||
#trackSuntimes() {
|
||||
debug.message("Regularly updating sun times...");
|
||||
this.#suntimesTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
|
||||
this.#updateSuntimes();
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this.#time || Time.UNKNOWN;
|
||||
#untrackSuntimes() {
|
||||
if (this.#suntimesTimeoutId) {
|
||||
GLib.Source.remove(this.#suntimesTimeoutId);
|
||||
this.#suntimesTimeoutId = null;
|
||||
}
|
||||
debug.message("Stopped regularly updating sun times.");
|
||||
}
|
||||
|
||||
#changeTime(time, manual = false) {
|
||||
if (time === this.#time) {
|
||||
if (!manual && this.#manuallySetTime)
|
||||
this.#manuallySetTime = false;
|
||||
return;
|
||||
}
|
||||
#addKeybinding() {
|
||||
this.#previousKeybinding = this.#settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
|
||||
if (!this.#settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0]) return;
|
||||
debug.message("Adding keybinding...");
|
||||
wm.addKeybinding(
|
||||
"nightthemeswitcher-ondemand-keybinding",
|
||||
this.#settings,
|
||||
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
||||
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
||||
() => {
|
||||
const time = this.time === Time.NIGHT ? Time.DAY : Time.NIGHT;
|
||||
this.#changeTime(time, true);
|
||||
},
|
||||
);
|
||||
debug.message("Added keybinding.");
|
||||
}
|
||||
|
||||
if (!manual && time !== this.#time && this.#manuallySetTime)
|
||||
return;
|
||||
|
||||
this.#time = time;
|
||||
this.#manuallySetTime = manual;
|
||||
|
||||
debug.message(manual ? `Time manually set to ${time}.` : `Time changed to ${time}.`);
|
||||
|
||||
const isMonitorFullscreen = layoutManager.monitors.some(monitor => monitor.inFullscreen);
|
||||
if (this.#settings.get_boolean('fullscreen-transition') || !isMonitorFullscreen) {
|
||||
try {
|
||||
layoutManager.screenTransition.run();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
this.notify('time');
|
||||
#removeKeybinding() {
|
||||
if (this.#previousKeybinding) {
|
||||
debug.message("Removing keybinding...");
|
||||
wm.removeKeybinding("nightthemeswitcher-ondemand-keybinding");
|
||||
debug.message("Removed keybinding.");
|
||||
}
|
||||
}
|
||||
|
||||
#colorSchemeToTime(colorScheme) {
|
||||
return colorScheme === this.#colorSchemeSettings.get_string("night") ? Time.NIGHT : Time.DAY;
|
||||
}
|
||||
|
||||
#connectSettings() {
|
||||
debug.message('Connecting Timer to settings...');
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#locationSettings,
|
||||
id: this.#locationSettings.connect('changed::enabled', this.#onLocationStateChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect('changed::manual-schedule', this.#onManualScheduleStateChanged.bind(this)),
|
||||
});
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect('changed::nightthemeswitcher-ondemand-keybinding', this.#onOndemandKeybindingChanged.bind(this)),
|
||||
});
|
||||
// Only listen to the offset setting when not using a manual schedule
|
||||
if (!this.#settings.get_boolean('manual-schedule')) {
|
||||
this.#settingsConnections.push({
|
||||
settings: this.#settings,
|
||||
id: this.#settings.connect('changed::offset', this.#onOffsetChanged.bind(this)),
|
||||
});
|
||||
}
|
||||
}
|
||||
#onLocationStateChanged() {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
#disconnectSettings() {
|
||||
this.#settingsConnections.forEach(({ settings, id }) => settings.disconnect(id));
|
||||
this.#settingsConnections = [];
|
||||
debug.message('Disconnected Timer from settings.');
|
||||
}
|
||||
#onManualScheduleStateChanged() {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
#onOffsetChanged() {
|
||||
this.#updateSuntimes();
|
||||
}
|
||||
|
||||
#trackTime() {
|
||||
debug.message('Watching for time change...');
|
||||
this.#timeTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
this.#changeTime(this.#computeTime());
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
});
|
||||
}
|
||||
#onOndemandKeybindingChanged() {
|
||||
this.#removeKeybinding();
|
||||
this.#addKeybinding();
|
||||
}
|
||||
|
||||
#untrackTime() {
|
||||
if (this.#timeTimeoutId) {
|
||||
GLib.Source.remove(this.#timeTimeoutId);
|
||||
this.#timeTimeoutId = null;
|
||||
}
|
||||
debug.message('Stopped watching for time change.');
|
||||
}
|
||||
|
||||
|
||||
#trackLocation() {
|
||||
debug.message('Connecting to GeoClue...');
|
||||
Geoclue.Simple.new(
|
||||
'org.gnome.Shell',
|
||||
Geoclue.AccuracyLevel.CITY,
|
||||
this.#cancellable,
|
||||
this.#onGeoclueReady.bind(this)
|
||||
#onGeoclueReady(_geoclue, result) {
|
||||
try {
|
||||
this.#geoclue = Geoclue.Simple.new_finish(result);
|
||||
this.#geoclueLocationConnectionId = this.#geoclue.connect("notify::location", this.#onLocationChanged.bind(this));
|
||||
debug.message("Connected to GeoClue.");
|
||||
this.#onLocationChanged();
|
||||
} catch (e) {
|
||||
const [latitude, longitude] = this.#settings.get_value("location").deepUnpack();
|
||||
if (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) {
|
||||
console.error(
|
||||
`[${NTS.metadata.name}] Unable to retrieve the location, using the last known location instead.\n${e}`,
|
||||
);
|
||||
}
|
||||
|
||||
#untrackLocation() {
|
||||
debug.message('Disconnecting from GeoClue...');
|
||||
if (this.#geoclue && this.#geoclueLocationConnectionId) {
|
||||
this.#geoclue.disconnect(this.#geoclueLocationConnectionId);
|
||||
this.#geoclueLocationConnectionId = null;
|
||||
this.#geoclue = null;
|
||||
}
|
||||
debug.message('Disconnected from GeoClue.');
|
||||
}
|
||||
|
||||
|
||||
#trackSuntimes() {
|
||||
debug.message('Regularly updating sun times...');
|
||||
this.#suntimesTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
|
||||
this.#updateSuntimes();
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
#untrackSuntimes() {
|
||||
if (this.#suntimesTimeoutId) {
|
||||
GLib.Source.remove(this.#suntimesTimeoutId);
|
||||
this.#suntimesTimeoutId = null;
|
||||
}
|
||||
debug.message('Stopped regularly updating sun times.');
|
||||
}
|
||||
|
||||
|
||||
#addKeybinding() {
|
||||
this.#previousKeybinding = this.#settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0];
|
||||
if (!this.#settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0])
|
||||
return;
|
||||
debug.message('Adding keybinding...');
|
||||
wm.addKeybinding(
|
||||
'nightthemeswitcher-ondemand-keybinding',
|
||||
this.#settings,
|
||||
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
|
||||
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
||||
() => {
|
||||
const time = this.time === Time.NIGHT ? Time.DAY : Time.NIGHT;
|
||||
this.#changeTime(time, true);
|
||||
}
|
||||
this.#updateSuntimes();
|
||||
} else {
|
||||
console.error(
|
||||
`[${NTS.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.\n${e}`,
|
||||
);
|
||||
debug.message('Added keybinding.');
|
||||
|
||||
const source = new MessageTray.Source({
|
||||
title: NTS.metadata.name,
|
||||
icon: Gio.icon_new_for_string(
|
||||
GLib.build_filenamev([NTS.metadata.path, "icons", "nightthemeswitcher-symbolic.svg"]),
|
||||
),
|
||||
});
|
||||
messageTray.add(source);
|
||||
|
||||
const notification = new MessageTray.Notification({
|
||||
source,
|
||||
title: _("Unknown Location"),
|
||||
body: _("A manual schedule will be used to switch the dark mode."),
|
||||
"icon-name": "location-services-disabled-symbolic",
|
||||
});
|
||||
notification.addAction(_("Edit Manual Schedule"), () => NTS.openPreferences());
|
||||
|
||||
notification.connect("activated", () => NTS.openPreferences());
|
||||
|
||||
source.addNotification(notification);
|
||||
|
||||
this.#settings.set_boolean("manual-schedule", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#removeKeybinding() {
|
||||
if (this.#previousKeybinding) {
|
||||
debug.message('Removing keybinding...');
|
||||
wm.removeKeybinding('nightthemeswitcher-ondemand-keybinding');
|
||||
debug.message('Removed keybinding.');
|
||||
}
|
||||
}
|
||||
#onLocationChanged(_geoclue, _location) {
|
||||
debug.message("Location has changed.");
|
||||
const { latitude, longitude } = this.#geoclue.get_location();
|
||||
this.#settings.set_value("location", new GLib.Variant("(dd)", [latitude, longitude]));
|
||||
debug.message(`Current location: (${latitude};${longitude})`);
|
||||
this.#updateSuntimes();
|
||||
}
|
||||
|
||||
#computeTime() {
|
||||
const sunrise = this.#settings.get_double("sunrise");
|
||||
const sunset = this.#settings.get_double("sunset");
|
||||
const datetime = GLib.DateTime.new_now_local();
|
||||
const hour = datetime.get_hour() + datetime.get_minute() / 60 + datetime.get_second() / 3600;
|
||||
|
||||
#colorSchemeToTime(colorScheme) {
|
||||
return colorScheme === this.#colorSchemeSettings.get_string('night') ? Time.NIGHT : Time.DAY;
|
||||
}
|
||||
// Regular schedule
|
||||
if (sunrise < sunset) return hour >= sunrise && hour < sunset ? Time.DAY : Time.NIGHT;
|
||||
// Sunset happens on the day after
|
||||
else if (sunrise > sunset) return hour >= sunrise || hour < sunset ? Time.DAY : Time.NIGHT;
|
||||
// Sunset and Sunrise times are identical; preserve current theme
|
||||
else return this.#time || this.#colorSchemeToTime(this.#interfaceSettings.get_string("color-scheme"));
|
||||
}
|
||||
|
||||
#updateSuntimes() {
|
||||
const [latitude, longitude] = this.#settings.get_value("location").deepUnpack();
|
||||
|
||||
#onLocationStateChanged() {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
if (latitude < -90 && latitude > 90 && longitude < -180 && longitude > 180) return;
|
||||
|
||||
#onManualScheduleStateChanged() {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
debug.message("Updating sun times...");
|
||||
|
||||
#onOffsetChanged() {
|
||||
this.#updateSuntimes();
|
||||
}
|
||||
const rad = (degrees) => (degrees * Math.PI) / 180;
|
||||
const deg = (radians) => (radians * 180) / Math.PI;
|
||||
|
||||
#onOndemandKeybindingChanged() {
|
||||
this.#removeKeybinding();
|
||||
this.#addKeybinding();
|
||||
}
|
||||
// Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
|
||||
const dtNow = GLib.DateTime.new_now_local();
|
||||
const dtZero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
|
||||
|
||||
#onGeoclueReady(_geoclue, result) {
|
||||
try {
|
||||
this.#geoclue = Geoclue.Simple.new_finish(result);
|
||||
this.#geoclueLocationConnectionId = this.#geoclue.connect('notify::location', this.#onLocationChanged.bind(this));
|
||||
debug.message('Connected to GeoClue.');
|
||||
this.#onLocationChanged();
|
||||
} catch (e) {
|
||||
const [latitude, longitude] = this.#settings.get_value('location').deepUnpack();
|
||||
if (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) {
|
||||
console.error(`[${NTS.metadata.name}] Unable to retrieve the location, using the last known location instead.\n${e}`);
|
||||
this.#updateSuntimes();
|
||||
} else {
|
||||
console.error(`[${NTS.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.\n${e}`);
|
||||
const timeSpan = dtNow.difference(dtZero);
|
||||
|
||||
const source = new MessageTray.Source({
|
||||
title: NTS.metadata.name,
|
||||
icon: Gio.icon_new_for_string(GLib.build_filenamev([NTS.metadata.path, 'icons', 'nightthemeswitcher-symbolic.svg'])),
|
||||
});
|
||||
messageTray.add(source);
|
||||
const date = timeSpan / 1000 / 1000 / 60 / 60 / 24 + 2;
|
||||
const tzOffset = dtNow.get_utc_offset() / 1000 / 1000 / 60 / 60;
|
||||
|
||||
const notification = new MessageTray.Notification(
|
||||
{
|
||||
source,
|
||||
title: _('Unknown Location'),
|
||||
body: _('A manual schedule will be used to switch the dark mode.'),
|
||||
'icon-name': 'location-services-disabled-symbolic',
|
||||
}
|
||||
);
|
||||
notification.addAction(_('Edit Manual Schedule'), () => NTS.openPreferences());
|
||||
const julianDay = date + 2415018.5 - tzOffset / 24;
|
||||
const julianCentury = (julianDay - 2451545) / 36525;
|
||||
const geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
|
||||
const geomMeanAnomSun = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
|
||||
const eccentEarthOrbit = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
|
||||
const sunEqOfCtr =
|
||||
Math.sin(rad(geomMeanAnomSun)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury)) +
|
||||
Math.sin(rad(2 * geomMeanAnomSun)) * (0.019993 - 0.000101 * julianCentury) +
|
||||
Math.sin(rad(3 * geomMeanAnomSun)) * 0.000289;
|
||||
const sunTrueLong = geomMeanLongSun + sunEqOfCtr;
|
||||
const sunAppLong = sunTrueLong - 0.00569 - 0.00478 * Math.sin(rad(125.04 - 1934.136 * julianCentury));
|
||||
const meanObliqEcliptic =
|
||||
23 + (26 + (21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813))) / 60) / 60;
|
||||
const obliqCorr = meanObliqEcliptic + 0.00256 * Math.cos(rad(125.04 - 1934.136 * julianCentury));
|
||||
const sunDeclin = deg(Math.asin(Math.sin(rad(obliqCorr)) * Math.sin(rad(sunAppLong))));
|
||||
const varY = Math.tan(rad(obliqCorr / 2)) * Math.tan(rad(obliqCorr / 2));
|
||||
const eqOfTime =
|
||||
4 *
|
||||
deg(
|
||||
varY * Math.sin(2 * rad(geomMeanLongSun)) -
|
||||
2 * eccentEarthOrbit * Math.sin(rad(geomMeanAnomSun)) +
|
||||
4 * eccentEarthOrbit * varY * Math.sin(rad(geomMeanAnomSun)) * Math.cos(2 * rad(geomMeanLongSun)) -
|
||||
0.5 * varY * varY * Math.sin(4 * rad(geomMeanLongSun)) -
|
||||
1.25 * eccentEarthOrbit * eccentEarthOrbit * Math.sin(2 * rad(geomMeanAnomSun)),
|
||||
);
|
||||
const haSunrise = deg(
|
||||
Math.acos(
|
||||
Math.cos(rad(90.833)) / (Math.cos(rad(latitude)) * Math.cos(rad(sunDeclin))) -
|
||||
Math.tan(rad(latitude)) * Math.tan(rad(sunDeclin)),
|
||||
),
|
||||
);
|
||||
const solarNoon = (720 - 4 * longitude - eqOfTime + tzOffset * 60) / 1440;
|
||||
|
||||
notification.connect('activated', () => NTS.openPreferences());
|
||||
const timeSunrise = solarNoon - (haSunrise * 4) / 1440;
|
||||
const timeSunset = solarNoon + (haSunrise * 4) / 1440;
|
||||
|
||||
source.addNotification(notification);
|
||||
const modulo = (n, m) => ((n % m) + m) % m;
|
||||
|
||||
this.#settings.set_boolean('manual-schedule', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
const offset = this.#settings.get_double("offset");
|
||||
const sunrise = modulo(timeSunrise * 24 + offset, 24);
|
||||
const sunset = modulo(timeSunset * 24 - offset, 24);
|
||||
|
||||
#onLocationChanged(_geoclue, _location) {
|
||||
debug.message('Location has changed.');
|
||||
const { latitude, longitude } = this.#geoclue.get_location();
|
||||
this.#settings.set_value('location', new GLib.Variant('(dd)', [latitude, longitude]));
|
||||
debug.message(`Current location: (${latitude};${longitude})`);
|
||||
this.#updateSuntimes();
|
||||
}
|
||||
this.#settings.set_double("sunrise", sunrise);
|
||||
this.#settings.set_double("sunset", sunset);
|
||||
|
||||
|
||||
#computeTime() {
|
||||
const sunrise = this.#settings.get_double('sunrise');
|
||||
const sunset = this.#settings.get_double('sunset');
|
||||
const datetime = GLib.DateTime.new_now_local();
|
||||
const hour = datetime.get_hour() + datetime.get_minute() / 60 + datetime.get_second() / 3600;
|
||||
|
||||
// Regular schedule
|
||||
if (sunrise < sunset)
|
||||
return hour >= sunrise && hour < sunset ? Time.DAY : Time.NIGHT;
|
||||
// Sunset happens on the day after
|
||||
else if (sunrise > sunset)
|
||||
return hour >= sunrise || hour < sunset ? Time.DAY : Time.NIGHT;
|
||||
// Sunset and Sunrise times are identical; preserve current theme
|
||||
else
|
||||
return this.#time || this.#colorSchemeToTime(this.#interfaceSettings.get_string('color-scheme'));
|
||||
}
|
||||
|
||||
#updateSuntimes() {
|
||||
const [latitude, longitude] = this.#settings.get_value('location').deepUnpack();
|
||||
|
||||
if (latitude < -90 && latitude > 90 && longitude < -180 && longitude > 180)
|
||||
return;
|
||||
|
||||
debug.message('Updating sun times...');
|
||||
|
||||
const rad = degrees => degrees * Math.PI / 180;
|
||||
const deg = radians => radians * 180 / Math.PI;
|
||||
|
||||
// Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
|
||||
const dtNow = GLib.DateTime.new_now_local();
|
||||
const dtZero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
|
||||
|
||||
const timeSpan = dtNow.difference(dtZero);
|
||||
|
||||
const date = timeSpan / 1000 / 1000 / 60 / 60 / 24 + 2;
|
||||
const tzOffset = dtNow.get_utc_offset() / 1000 / 1000 / 60 / 60;
|
||||
|
||||
const julianDay = date + 2415018.5 - tzOffset / 24;
|
||||
const julianCentury = (julianDay - 2451545) / 36525;
|
||||
const geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
|
||||
const geomMeanAnomSun = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
|
||||
const eccentEarthOrbit = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
|
||||
const sunEqOfCtr = Math.sin(rad(geomMeanAnomSun)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury)) + Math.sin(rad(2 * geomMeanAnomSun)) * (0.019993 - 0.000101 * julianCentury) + Math.sin(rad(3 * geomMeanAnomSun)) * 0.000289;
|
||||
const sunTrueLong = geomMeanLongSun + sunEqOfCtr;
|
||||
const sunAppLong = sunTrueLong - 0.00569 - 0.00478 * Math.sin(rad(125.04 - 1934.136 * julianCentury));
|
||||
const meanObliqEcliptic = 23 + (26 + ((21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813)))) / 60) / 60;
|
||||
const obliqCorr = meanObliqEcliptic + 0.00256 * Math.cos(rad(125.04 - 1934.136 * julianCentury));
|
||||
const sunDeclin = deg(Math.asin(Math.sin(rad(obliqCorr)) * Math.sin(rad(sunAppLong))));
|
||||
const varY = Math.tan(rad(obliqCorr / 2)) * Math.tan(rad(obliqCorr / 2));
|
||||
const eqOfTime = 4 * deg(varY * Math.sin(2 * rad(geomMeanLongSun)) - 2 * eccentEarthOrbit * Math.sin(rad(geomMeanAnomSun)) + 4 * eccentEarthOrbit * varY * Math.sin(rad(geomMeanAnomSun)) * Math.cos(2 * rad(geomMeanLongSun)) - 0.5 * varY * varY * Math.sin(4 * rad(geomMeanLongSun)) - 1.25 * eccentEarthOrbit * eccentEarthOrbit * Math.sin(2 * rad(geomMeanAnomSun)));
|
||||
const haSunrise = deg(Math.acos(Math.cos(rad(90.833)) / (Math.cos(rad(latitude)) * Math.cos(rad(sunDeclin))) - Math.tan(rad(latitude)) * Math.tan(rad(sunDeclin))));
|
||||
const solarNoon = (720 - 4 * longitude - eqOfTime + tzOffset * 60) / 1440;
|
||||
|
||||
const timeSunrise = solarNoon - haSunrise * 4 / 1440;
|
||||
const timeSunset = solarNoon + haSunrise * 4 / 1440;
|
||||
|
||||
const modulo = (n, m) => ((n % m) + m) % m;
|
||||
|
||||
const offset = this.#settings.get_double('offset');
|
||||
const sunrise = modulo(timeSunrise * 24 + offset, 24);
|
||||
const sunset = modulo(timeSunset * 24 - offset, 24);
|
||||
|
||||
this.#settings.set_double('sunrise', sunrise);
|
||||
this.#settings.set_double('sunset', sunset);
|
||||
|
||||
debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
|
||||
}
|
||||
debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
|
||||
}
|
||||
}
|
||||
|
||||
+171
-165
@@ -1,184 +1,190 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
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';
|
||||
import Adw from "gi://Adw";
|
||||
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";
|
||||
|
||||
import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
|
||||
|
||||
Gio._promisify(Gtk.FileDialog.prototype, 'open', 'open_finish');
|
||||
import { gettext as _ } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
|
||||
|
||||
Gio._promisify(Gtk.FileDialog.prototype, "open", "open_finish");
|
||||
|
||||
export class BackgroundButton extends Gtk.Button {
|
||||
#uri;
|
||||
#uri;
|
||||
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'BackgroundButton',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui',
|
||||
InternalChildren: ['file_dialog', '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);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "BackgroundButton",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundButton.ui",
|
||||
InternalChildren: ["file_dialog", "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.#setupDropTarget();
|
||||
this.#setupFileDialog();
|
||||
}
|
||||
constructor({ ...params } = {}) {
|
||||
super(params);
|
||||
this.#setupSize();
|
||||
this.#setupDropTarget();
|
||||
this.#setupFileDialog();
|
||||
}
|
||||
|
||||
get uri() {
|
||||
return this.#uri || null;
|
||||
}
|
||||
get uri() {
|
||||
return this.#uri || null;
|
||||
}
|
||||
|
||||
set uri(uri) {
|
||||
if (uri === this.#uri)
|
||||
return;
|
||||
this.#uri = uri;
|
||||
this.notify('uri');
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
this.#updateThumbnail();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
set uri(uri) {
|
||||
if (uri === this.#uri) return;
|
||||
this.#uri = uri;
|
||||
this.notify("uri");
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
this.#updateThumbnail();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
#setupSize() {
|
||||
const display = Gdk.Display.get_default();
|
||||
const monitor = display.get_monitors().get_item(0);
|
||||
if (monitor.width_mm === 0 || monitor.height_mm === 0)
|
||||
return;
|
||||
if (monitor.width_mm > monitor.height_mm)
|
||||
this.thumbHeight *= monitor.height_mm / monitor.width_mm;
|
||||
else
|
||||
this.thumbWidth *= monitor.width_mm / monitor.height_mm;
|
||||
}
|
||||
#setupSize() {
|
||||
const display = Gdk.Display.get_default();
|
||||
const monitor = display.get_monitors().get_item(0);
|
||||
if (monitor.width_mm === 0 || monitor.height_mm === 0) return;
|
||||
if (monitor.width_mm > monitor.height_mm) this.thumbHeight *= monitor.height_mm / monitor.width_mm;
|
||||
else this.thumbWidth *= monitor.width_mm / monitor.height_mm;
|
||||
}
|
||||
|
||||
#setupDropTarget() {
|
||||
const dropTarget = Gtk.DropTarget.new(Gio.File.$gtype, Gdk.DragAction.COPY);
|
||||
dropTarget.connect('drop', (_target, file, _x, _y) => {
|
||||
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
||||
if (this.#isContentTypeSupported(contentType)) {
|
||||
this.uri = file.get_uri();
|
||||
return true;
|
||||
} else {
|
||||
if (this.root instanceof Adw.PreferencesWindow) {
|
||||
this.root.add_toast(new Adw.Toast({
|
||||
title: _('This image format is not supported.'),
|
||||
timeout: 10,
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.add_controller(dropTarget);
|
||||
}
|
||||
|
||||
#setupFileDialog() {
|
||||
const filter = new Gtk.FileFilter();
|
||||
filter.set_name(_('Image files'));
|
||||
this.#getSupportedContentTypes().forEach(type => filter.add_mime_type(type));
|
||||
this._file_dialog.filters = Gio.ListStore.new(Gtk.FileFilter);
|
||||
this._file_dialog.filters.append(filter);
|
||||
}
|
||||
|
||||
#getSupportedContentTypes() {
|
||||
return GdkPixbuf.Pixbuf.get_formats().flatMap(format => format.get_mime_types()).concat('application/xml');
|
||||
}
|
||||
|
||||
#isContentTypeSupported(contentType) {
|
||||
for (const supportedContentType of this.#getSupportedContentTypes()) {
|
||||
if (Gio.content_type_equals(contentType, supportedContentType))
|
||||
return true;
|
||||
#setupDropTarget() {
|
||||
const dropTarget = Gtk.DropTarget.new(Gio.File.$gtype, Gdk.DragAction.COPY);
|
||||
dropTarget.connect("drop", (_target, file, _x, _y) => {
|
||||
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
||||
if (this.#isContentTypeSupported(contentType)) {
|
||||
this.uri = file.get_uri();
|
||||
return true;
|
||||
} else {
|
||||
if (this.root instanceof Adw.PreferencesWindow) {
|
||||
this.root.add_toast(
|
||||
new Adw.Toast({
|
||||
title: _("This image format is not supported."),
|
||||
timeout: 10,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.add_controller(dropTarget);
|
||||
}
|
||||
|
||||
#setupFileDialog() {
|
||||
const filter = new Gtk.FileFilter();
|
||||
filter.set_name(_("Image files"));
|
||||
this.#getSupportedContentTypes().forEach((type) => filter.add_mime_type(type));
|
||||
this._file_dialog.filters = Gio.ListStore.new(Gtk.FileFilter);
|
||||
this._file_dialog.filters.append(filter);
|
||||
}
|
||||
|
||||
#getSupportedContentTypes() {
|
||||
return GdkPixbuf.Pixbuf.get_formats()
|
||||
.flatMap((format) => format.get_mime_types())
|
||||
.concat("application/xml");
|
||||
}
|
||||
|
||||
#isContentTypeSupported(contentType) {
|
||||
for (const supportedContentType of this.#getSupportedContentTypes()) {
|
||||
if (Gio.content_type_equals(contentType, supportedContentType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vfunc_mnemonic_activate() {
|
||||
this.#setURIFromFileDialog();
|
||||
}
|
||||
|
||||
onClicked(_button) {
|
||||
this.#setURIFromFileDialog();
|
||||
}
|
||||
|
||||
async #setURIFromFileDialog() {
|
||||
try {
|
||||
const file = await this._file_dialog.open(this.get_root(), null);
|
||||
this.uri = file.get_uri();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
#updateThumbnail() {
|
||||
this._thumbnail.paintable = null;
|
||||
|
||||
if (!this.uri) return;
|
||||
|
||||
const file = Gio.File.new_for_uri(this.uri);
|
||||
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
||||
|
||||
if (!this.#isContentTypeSupported(contentType)) return;
|
||||
|
||||
let path;
|
||||
if (Gio.content_type_equals(contentType, "application/xml")) {
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
const contents = decoder.decode(file.load_contents(null)[1]);
|
||||
try {
|
||||
path = contents.match(/<file>(.+)<\/file>/m)[1];
|
||||
if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0])) throw new Error();
|
||||
} catch (e) {
|
||||
console.error(`No suitable background file found in ${file.get_path()}.\n${e}`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
path = file.get_path();
|
||||
}
|
||||
|
||||
vfunc_mnemonic_activate() {
|
||||
this.#setURIFromFileDialog();
|
||||
}
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path);
|
||||
const scale =
|
||||
pixbuf.width / pixbuf.height > this.thumbWidth / this.thumbHeight
|
||||
? this.thumbHeight / pixbuf.height
|
||||
: this.thumbWidth / pixbuf.width;
|
||||
const thumbPixbuf = GdkPixbuf.Pixbuf.new(
|
||||
pixbuf.colorspace,
|
||||
pixbuf.has_alpha,
|
||||
pixbuf.bits_per_sample,
|
||||
this.thumbWidth,
|
||||
this.thumbHeight,
|
||||
);
|
||||
pixbuf.scale(
|
||||
thumbPixbuf,
|
||||
0,
|
||||
0,
|
||||
this.thumbWidth,
|
||||
this.thumbHeight,
|
||||
-(pixbuf.width * scale - this.thumbWidth) / 2,
|
||||
-(pixbuf.height * scale - this.thumbHeight) / 2,
|
||||
scale,
|
||||
scale,
|
||||
GdkPixbuf.InterpType.TILES,
|
||||
);
|
||||
|
||||
onClicked(_button) {
|
||||
this.#setURIFromFileDialog();
|
||||
}
|
||||
|
||||
async #setURIFromFileDialog() {
|
||||
try {
|
||||
const file = await this._file_dialog.open(this.get_root(), null);
|
||||
this.uri = file.get_uri();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
#updateThumbnail() {
|
||||
this._thumbnail.paintable = null;
|
||||
|
||||
if (!this.uri)
|
||||
return;
|
||||
|
||||
const file = Gio.File.new_for_uri(this.uri);
|
||||
const contentType = Gio.content_type_guess(file.get_basename(), null)[0];
|
||||
|
||||
if (!this.#isContentTypeSupported(contentType))
|
||||
return;
|
||||
|
||||
let path;
|
||||
if (Gio.content_type_equals(contentType, 'application/xml')) {
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const contents = decoder.decode(file.load_contents(null)[1]);
|
||||
try {
|
||||
path = contents.match(/<file>(.+)<\/file>/m)[1];
|
||||
if (!this.#isContentTypeSupported(Gio.content_type_guess(path, null)[0]))
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
console.error(`No suitable background file found in ${file.get_path()}.\n${e}`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
path = file.get_path();
|
||||
}
|
||||
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path);
|
||||
const scale = pixbuf.width / pixbuf.height > this.thumbWidth / this.thumbHeight ? this.thumbHeight / pixbuf.height : this.thumbWidth / pixbuf.width;
|
||||
const thumbPixbuf = GdkPixbuf.Pixbuf.new(pixbuf.colorspace, pixbuf.has_alpha, pixbuf.bits_per_sample, this.thumbWidth, this.thumbHeight);
|
||||
pixbuf.scale(
|
||||
thumbPixbuf,
|
||||
0, 0,
|
||||
this.thumbWidth, this.thumbHeight,
|
||||
-(pixbuf.width * scale - this.thumbWidth) / 2, -(pixbuf.height * scale - this.thumbHeight) / 2,
|
||||
scale, scale,
|
||||
GdkPixbuf.InterpType.TILES
|
||||
);
|
||||
|
||||
this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf);
|
||||
}
|
||||
this._thumbnail.paintable = Gdk.Texture.new_for_pixbuf(thumbPixbuf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import Adw from "gi://Adw";
|
||||
import Gio from "gi://Gio";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
export class BackgroundsPage extends Adw.PreferencesPage {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'BackgroundsPage',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundsPage.ui',
|
||||
InternalChildren: [
|
||||
'day_button',
|
||||
'night_button',
|
||||
],
|
||||
}, this);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "BackgroundsPage",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/BackgroundsPage.ui",
|
||||
InternalChildren: ["day_button", "night_button"],
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
constructor({ ...params } = {}) {
|
||||
super(params);
|
||||
const settings = new Gio.Settings({ schema: 'org.gnome.desktop.background' });
|
||||
constructor({ ...params } = {}) {
|
||||
super(params);
|
||||
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-dark', this._night_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import Adw from "gi://Adw";
|
||||
import Gio from "gi://Gio";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
export class CommandsPage extends Adw.PreferencesPage {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'CommandsPage',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/CommandsPage.ui',
|
||||
InternalChildren: [
|
||||
'enabled_switch',
|
||||
'sunrise_entry',
|
||||
'sunset_entry',
|
||||
],
|
||||
}, this);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "CommandsPage",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/CommandsPage.ui",
|
||||
InternalChildren: ["enabled_switch", "sunrise_entry", "sunset_entry"],
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
constructor({ settings, ...params } = {}) {
|
||||
super(params);
|
||||
constructor({ settings, ...params } = {}) {
|
||||
super(params);
|
||||
|
||||
settings.bind('enabled', this._enabled_switch, 'active', 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("enabled", this._enabled_switch, "active", Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("sunrise", this._sunrise_entry, "text", Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("sunset", this._sunset_entry, "text", Gio.SettingsBindFlags.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import Adw from "gi://Adw";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
export class ContributePage extends Adw.PreferencesPage {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ContributePage',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ContributePage.ui',
|
||||
}, this);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "ContributePage",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ContributePage.ui",
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import Adw from "gi://Adw";
|
||||
import Gio from "gi://Gio";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
export class SchedulePage extends Adw.PreferencesPage {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'SchedulePage',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/SchedulePage.ui',
|
||||
InternalChildren: [
|
||||
'manual_schedule_switch',
|
||||
'keyboard_shortcut_button',
|
||||
'schedule_sunrise_time_chooser',
|
||||
'schedule_sunset_time_chooser',
|
||||
'schedule_offset_spin_button',
|
||||
'fullscreen_transition_switch',
|
||||
],
|
||||
}, this);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "SchedulePage",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/SchedulePage.ui",
|
||||
InternalChildren: [
|
||||
"manual_schedule_switch",
|
||||
"keyboard_shortcut_button",
|
||||
"schedule_sunrise_time_chooser",
|
||||
"schedule_sunset_time_chooser",
|
||||
"schedule_offset_spin_button",
|
||||
"fullscreen_transition_switch",
|
||||
],
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
constructor({ settings, ...params } = {}) {
|
||||
super(params);
|
||||
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);
|
||||
|
||||
settings.bind('sunrise', this._schedule_sunrise_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind('sunset', this._schedule_sunset_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("sunrise", this._schedule_sunrise_time_chooser, "time", Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("sunset", this._schedule_sunset_time_chooser, "time", Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
settings.bind('offset', this._schedule_offset_spin_button, 'value', Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("offset", this._schedule_offset_spin_button, "value", Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
settings.bind('fullscreen-transition', this._fullscreen_transition_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||
settings.bind("fullscreen-transition", this._fullscreen_transition_switch, "active", Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
settings.connect('changed::nightthemeswitcher-ondemand-keybinding', () => {
|
||||
this._keyboard_shortcut_button.keybinding = settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0];
|
||||
});
|
||||
this._keyboard_shortcut_button.connect('notify::keybinding', () => {
|
||||
settings.set_strv('nightthemeswitcher-ondemand-keybinding', [this._keyboard_shortcut_button.keybinding]);
|
||||
});
|
||||
this._keyboard_shortcut_button.keybinding = settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0];
|
||||
}
|
||||
settings.connect("changed::nightthemeswitcher-ondemand-keybinding", () => {
|
||||
this._keyboard_shortcut_button.keybinding = settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
|
||||
});
|
||||
this._keyboard_shortcut_button.connect("notify::keybinding", () => {
|
||||
settings.set_strv("nightthemeswitcher-ondemand-keybinding", [this._keyboard_shortcut_button.keybinding]);
|
||||
});
|
||||
this._keyboard_shortcut_button.keybinding = settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
|
||||
}
|
||||
}
|
||||
|
||||
+101
-114
@@ -1,147 +1,134 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Gdk from 'gi://Gdk';
|
||||
import GObject from 'gi://GObject';
|
||||
import Gtk from 'gi://Gtk';
|
||||
|
||||
import Gdk from "gi://Gdk";
|
||||
import GObject from "gi://GObject";
|
||||
import Gtk from "gi://Gtk";
|
||||
|
||||
export class ShortcutButton extends Gtk.Stack {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ShortcutButton',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ShortcutButton.ui',
|
||||
InternalChildren: ['choose_button', 'change_button', 'clear_button', 'dialog'],
|
||||
Properties: {
|
||||
keybinding: GObject.ParamSpec.string(
|
||||
'keybinding',
|
||||
'Keybinding',
|
||||
'Key sequence',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
null
|
||||
),
|
||||
},
|
||||
}, this);
|
||||
}
|
||||
|
||||
vfunc_mnemonic_activate() {
|
||||
this.activate();
|
||||
}
|
||||
|
||||
activate() {
|
||||
if (this.keybinding)
|
||||
return this._change_button.activate();
|
||||
else
|
||||
return this._choose_button.activate();
|
||||
}
|
||||
|
||||
openDialog() {
|
||||
this._dialog.transient_for = this.get_root();
|
||||
this._dialog.present();
|
||||
}
|
||||
|
||||
onKeybindingChanged(button) {
|
||||
button.visible_child_name = button.keybinding ? 'edit' : 'choose';
|
||||
}
|
||||
|
||||
onChooseButtonClicked(_button) {
|
||||
this.openDialog();
|
||||
}
|
||||
|
||||
onChangeButtonClicked(_button) {
|
||||
this.openDialog();
|
||||
}
|
||||
|
||||
onClearButtonClicked(_button) {
|
||||
this.keybinding = '';
|
||||
}
|
||||
|
||||
onKeyPressed(_widget, keyval, keycode, state) {
|
||||
let mask = state & Gtk.accelerator_get_default_mod_mask();
|
||||
mask &= ~Gdk.ModifierType.LOCK_MASK;
|
||||
|
||||
if (mask === 0 && keyval === Gdk.KEY_Escape) {
|
||||
this._dialog.close();
|
||||
return Gdk.EVENT_STOP;
|
||||
}
|
||||
|
||||
if (
|
||||
!isBindingValid({ mask, keycode, keyval }) ||
|
||||
!isAccelValid({ mask, keyval })
|
||||
)
|
||||
return Gdk.EVENT_STOP;
|
||||
|
||||
this.keybinding = Gtk.accelerator_name_with_keycode(
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "ShortcutButton",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/ShortcutButton.ui",
|
||||
InternalChildren: ["choose_button", "change_button", "clear_button", "dialog"],
|
||||
Properties: {
|
||||
keybinding: GObject.ParamSpec.string(
|
||||
"keybinding",
|
||||
"Keybinding",
|
||||
"Key sequence",
|
||||
GObject.ParamFlags.READWRITE,
|
||||
null,
|
||||
keyval,
|
||||
keycode,
|
||||
mask
|
||||
);
|
||||
this._dialog.close();
|
||||
return Gdk.EVENT_STOP;
|
||||
}
|
||||
}
|
||||
),
|
||||
},
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
vfunc_mnemonic_activate() {
|
||||
this.activate();
|
||||
}
|
||||
|
||||
activate() {
|
||||
if (this.keybinding) return this._change_button.activate();
|
||||
else return this._choose_button.activate();
|
||||
}
|
||||
|
||||
openDialog() {
|
||||
this._dialog.transient_for = this.get_root();
|
||||
this._dialog.present();
|
||||
}
|
||||
|
||||
onKeybindingChanged(button) {
|
||||
button.visible_child_name = button.keybinding ? "edit" : "choose";
|
||||
}
|
||||
|
||||
onChooseButtonClicked(_button) {
|
||||
this.openDialog();
|
||||
}
|
||||
|
||||
onChangeButtonClicked(_button) {
|
||||
this.openDialog();
|
||||
}
|
||||
|
||||
onClearButtonClicked(_button) {
|
||||
this.keybinding = "";
|
||||
}
|
||||
|
||||
onKeyPressed(_widget, keyval, keycode, state) {
|
||||
let mask = state & Gtk.accelerator_get_default_mod_mask();
|
||||
mask &= ~Gdk.ModifierType.LOCK_MASK;
|
||||
|
||||
if (mask === 0 && keyval === Gdk.KEY_Escape) {
|
||||
this._dialog.close();
|
||||
return Gdk.EVENT_STOP;
|
||||
}
|
||||
|
||||
if (!isBindingValid({ mask, keycode, keyval }) || !isAccelValid({ mask, keyval })) return Gdk.EVENT_STOP;
|
||||
|
||||
this.keybinding = Gtk.accelerator_name_with_keycode(null, keyval, keycode, mask);
|
||||
this._dialog.close();
|
||||
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);
|
||||
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.ModifierType.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;
|
||||
if ((mask === 0 || mask === Gdk.ModifierType.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);
|
||||
return Gtk.accelerator_valid(keyval, mask) || (keyval === Gdk.KEY_Tab && mask !== 0);
|
||||
}
|
||||
|
||||
@@ -1,97 +1,113 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import GDesktopEnums from 'gi://GDesktopEnums';
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
import Gtk from 'gi://Gtk';
|
||||
|
||||
import GDesktopEnums from "gi://GDesktopEnums";
|
||||
import Gio from "gi://Gio";
|
||||
import GObject from "gi://GObject";
|
||||
import Gtk from "gi://Gtk";
|
||||
|
||||
export class TimeChooser extends Gtk.Widget {
|
||||
#clockFormat;
|
||||
#interfaceSettings;
|
||||
#clockFormat;
|
||||
#interfaceSettings;
|
||||
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'TimeChooser',
|
||||
Template: 'resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/TimeChooser.ui',
|
||||
InternalChildren: ['clock_format_stack', 'hours_12', 'minutes_12', 'hours_24', 'minutes_24', 'am_toggle_button', 'pm_toggle_button'],
|
||||
Properties: {
|
||||
time: GObject.ParamSpec.double(
|
||||
'time',
|
||||
'Time',
|
||||
'The time of the chooser',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0,
|
||||
24,
|
||||
0
|
||||
),
|
||||
},
|
||||
}, this);
|
||||
}
|
||||
static {
|
||||
GObject.registerClass(
|
||||
{
|
||||
GTypeName: "TimeChooser",
|
||||
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/TimeChooser.ui",
|
||||
InternalChildren: [
|
||||
"clock_format_stack",
|
||||
"hours_12",
|
||||
"minutes_12",
|
||||
"hours_24",
|
||||
"minutes_24",
|
||||
"am_toggle_button",
|
||||
"pm_toggle_button",
|
||||
],
|
||||
Properties: {
|
||||
time: GObject.ParamSpec.double(
|
||||
"time",
|
||||
"Time",
|
||||
"The time of the chooser",
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0,
|
||||
24,
|
||||
0,
|
||||
),
|
||||
},
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
constructor({ ...params } = {}) {
|
||||
super(params);
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this.#interfaceSettings.connect('changed::clock-format', this.#onClockFormatChanged.bind(this));
|
||||
this.#onClockFormatChanged();
|
||||
}
|
||||
constructor({ ...params } = {}) {
|
||||
super(params);
|
||||
this.#interfaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" });
|
||||
this.#interfaceSettings.connect("changed::clock-format", this.#onClockFormatChanged.bind(this));
|
||||
this.#onClockFormatChanged();
|
||||
}
|
||||
|
||||
#onClockFormatChanged(_settings) {
|
||||
this.#clockFormat = this.#interfaceSettings.get_enum('clock-format');
|
||||
this.#syncClockFormatStack();
|
||||
}
|
||||
#onClockFormatChanged(_settings) {
|
||||
this.#clockFormat = this.#interfaceSettings.get_enum("clock-format");
|
||||
this.#syncClockFormatStack();
|
||||
}
|
||||
|
||||
#syncClockFormatStack() {
|
||||
this._clock_format_stack.set_visible_child_name(this.#clockFormat === GDesktopEnums.ClockFormat['12H'] ? '12h' : '24h');
|
||||
}
|
||||
#syncClockFormatStack() {
|
||||
this._clock_format_stack.set_visible_child_name(
|
||||
this.#clockFormat === GDesktopEnums.ClockFormat["12H"] ? "12h" : "24h",
|
||||
);
|
||||
}
|
||||
|
||||
#convertTimeTo24hFormat(time) {
|
||||
const hours = Math.trunc(time);
|
||||
const minutes = Math.round((time - hours) * 60);
|
||||
return { hours, minutes };
|
||||
}
|
||||
#convertTimeTo24hFormat(time) {
|
||||
const hours = Math.trunc(time);
|
||||
const minutes = Math.round((time - hours) * 60);
|
||||
return { hours, minutes };
|
||||
}
|
||||
|
||||
#convertTimeTo12hFormat(time) {
|
||||
const { hours: hours24, minutes } = this.#convertTimeTo24hFormat(time);
|
||||
const hours = hours24 === 0 ? 12 : hours24 % 12;
|
||||
const isPm = hours24 >= 12;
|
||||
return { hours, minutes, isPm };
|
||||
}
|
||||
#convertTimeTo12hFormat(time) {
|
||||
const { hours: hours24, minutes } = this.#convertTimeTo24hFormat(time);
|
||||
const hours = hours24 === 0 ? 12 : hours24 % 12;
|
||||
const isPm = hours24 >= 12;
|
||||
return { hours, minutes, isPm };
|
||||
}
|
||||
|
||||
#convert24hFormatToTime({ hours, minutes }) {
|
||||
return hours + minutes / 60;
|
||||
}
|
||||
#convert24hFormatToTime({ hours, minutes }) {
|
||||
return hours + minutes / 60;
|
||||
}
|
||||
|
||||
#convert12hFormatToTime({ hours, minutes, isPm }) {
|
||||
let hours24 = hours + Number(isPm) * 12;
|
||||
if (hours === 12)
|
||||
hours24 = isPm ? 12 : 0;
|
||||
return this.#convert24hFormatToTime({ hours: hours24, minutes });
|
||||
}
|
||||
#convert12hFormatToTime({ hours, minutes, isPm }) {
|
||||
let hours24 = hours + Number(isPm) * 12;
|
||||
if (hours === 12) hours24 = isPm ? 12 : 0;
|
||||
return this.#convert24hFormatToTime({ hours: hours24, minutes });
|
||||
}
|
||||
|
||||
onTimeChanged(_chooser) {
|
||||
const time = this.time;
|
||||
onTimeChanged(_chooser) {
|
||||
const time = this.time;
|
||||
|
||||
const clock12 = this.#convertTimeTo12hFormat(time);
|
||||
this._hours_12.value = clock12.hours;
|
||||
this._minutes_12.value = clock12.minutes;
|
||||
this._am_toggle_button.active = !clock12.isPm;
|
||||
this._pm_toggle_button.active = clock12.isPm;
|
||||
const clock12 = this.#convertTimeTo12hFormat(time);
|
||||
this._hours_12.value = clock12.hours;
|
||||
this._minutes_12.value = clock12.minutes;
|
||||
this._am_toggle_button.active = !clock12.isPm;
|
||||
this._pm_toggle_button.active = clock12.isPm;
|
||||
|
||||
const clock24 = this.#convertTimeTo24hFormat(time);
|
||||
this._hours_24.value = clock24.hours;
|
||||
this._minutes_24.value = clock24.minutes;
|
||||
}
|
||||
const clock24 = this.#convertTimeTo24hFormat(time);
|
||||
this._hours_24.value = clock24.hours;
|
||||
this._minutes_24.value = clock24.minutes;
|
||||
}
|
||||
|
||||
onValueChanged(_widget) {
|
||||
this.time = this.#clockFormat === GDesktopEnums.ClockFormat['12H']
|
||||
? this.#convert12hFormatToTime({ hours: this._hours_12.value, minutes: this._minutes_12.value, isPm: this._pm_toggle_button.active })
|
||||
: this.#convert24hFormatToTime({ hours: this._hours_24.value, minutes: this._minutes_24.value });
|
||||
}
|
||||
onValueChanged(_widget) {
|
||||
this.time =
|
||||
this.#clockFormat === GDesktopEnums.ClockFormat["12H"]
|
||||
? this.#convert12hFormatToTime({
|
||||
hours: this._hours_12.value,
|
||||
minutes: this._minutes_12.value,
|
||||
isPm: this._pm_toggle_button.active,
|
||||
})
|
||||
: this.#convert24hFormatToTime({ hours: this._hours_24.value, minutes: this._minutes_24.value });
|
||||
}
|
||||
|
||||
onOutputChanged(spin) {
|
||||
spin.text = spin.value.toString().padStart(2, '0');
|
||||
return true;
|
||||
}
|
||||
onOutputChanged(spin) {
|
||||
spin.text = spin.value.toString().padStart(2, "0");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+45
-47
@@ -1,60 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Night Theme Switcher Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
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';
|
||||
|
||||
import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
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";
|
||||
|
||||
import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
|
||||
|
||||
export default class NightThemeSwitcherPreferences extends ExtensionPreferences {
|
||||
/**
|
||||
* Fill the PreferencesWindow.
|
||||
*
|
||||
* @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);
|
||||
/**
|
||||
* Fill the PreferencesWindow.
|
||||
* @param {import("gi://Adw").default.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);
|
||||
|
||||
// Load icons
|
||||
const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
|
||||
iconTheme.add_resource_path('/org/gnome/Shell/Extensions/nightthemeswitcher/preferences/icons');
|
||||
// Load icons
|
||||
const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
|
||||
iconTheme.add_resource_path("/org/gnome/Shell/Extensions/nightthemeswitcher/preferences/icons");
|
||||
|
||||
// Set window properties
|
||||
window.search_enabled = true;
|
||||
window.set_default_size(500, 630);
|
||||
// Set window properties
|
||||
window.search_enabled = true;
|
||||
window.set_default_size(500, 630);
|
||||
|
||||
// Dynamically import all classes
|
||||
const { BackgroundButton } = await import('./preferences/BackgroundButton.js');
|
||||
const { BackgroundsPage } = await import('./preferences/BackgroundsPage.js');
|
||||
const { CommandsPage } = await import('./preferences/CommandsPage.js');
|
||||
const { ContributePage } = await import('./preferences/ContributePage.js');
|
||||
const { SchedulePage } = await import('./preferences/SchedulePage.js');
|
||||
const { ShortcutButton } = await import('./preferences/ShortcutButton.js');
|
||||
const { TimeChooser } = await import('./preferences/TimeChooser.js');
|
||||
// Dynamically import all classes
|
||||
const { BackgroundButton } = await import("./preferences/BackgroundButton.js");
|
||||
const { BackgroundsPage } = await import("./preferences/BackgroundsPage.js");
|
||||
const { CommandsPage } = await import("./preferences/CommandsPage.js");
|
||||
const { ContributePage } = await import("./preferences/ContributePage.js");
|
||||
const { SchedulePage } = await import("./preferences/SchedulePage.js");
|
||||
const { ShortcutButton } = await import("./preferences/ShortcutButton.js");
|
||||
const { TimeChooser } = await import("./preferences/TimeChooser.js");
|
||||
|
||||
// Make sure all GObjects are registered
|
||||
GObject.type_ensure(BackgroundButton);
|
||||
GObject.type_ensure(BackgroundsPage);
|
||||
GObject.type_ensure(CommandsPage);
|
||||
GObject.type_ensure(ContributePage);
|
||||
GObject.type_ensure(SchedulePage);
|
||||
GObject.type_ensure(ShortcutButton);
|
||||
GObject.type_ensure(TimeChooser);
|
||||
// Make sure all GObjects are registered
|
||||
GObject.type_ensure(BackgroundButton);
|
||||
GObject.type_ensure(BackgroundsPage);
|
||||
GObject.type_ensure(CommandsPage);
|
||||
GObject.type_ensure(ContributePage);
|
||||
GObject.type_ensure(SchedulePage);
|
||||
GObject.type_ensure(ShortcutButton);
|
||||
GObject.type_ensure(TimeChooser);
|
||||
|
||||
// Add all pages
|
||||
[
|
||||
new SchedulePage({ settings: this.getSettings(`${this.metadata['settings-schema']}.time`) }),
|
||||
new BackgroundsPage(),
|
||||
new CommandsPage({ settings: this.getSettings(`${this.metadata['settings-schema']}.commands`) }),
|
||||
new ContributePage(),
|
||||
].forEach(page => window.add(page));
|
||||
}
|
||||
// Add all pages
|
||||
[
|
||||
new SchedulePage({ settings: this.getSettings(`${this.metadata["settings-schema"]}.time`) }),
|
||||
new BackgroundsPage(),
|
||||
new CommandsPage({ settings: this.getSettings(`${this.metadata["settings-schema"]}.commands`) }),
|
||||
new ContributePage(),
|
||||
].forEach((page) => window.add(page));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user