Extension: Allow using a manual location

This commit is contained in:
Romain Vigier
2026-08-02 00:25:06 +02:00
parent 0939a53411
commit dc9c0240fb
5 changed files with 172 additions and 77 deletions
@@ -63,6 +63,19 @@ SPDX-License-Identifier: GPL-3.0-or-later
<default>0</default>
</key>
</schema>
<schema id="org.gnome.shell.extensions.nightthemeswitcher.location" path="/org/gnome/shell/extensions/nightthemeswitcher/location/">
<key name="manual-location" type="b">
<default>false</default>
</key>
<key name="latitude" type="d">
<range min="-90" max="90"/>
<default>0</default>
</key>
<key name="longitude" type="d">
<range min="-180" max="180"/>
<default>0</default>
</key>
</schema>
<schema id="org.gnome.shell.extensions.nightthemeswitcher.color-scheme" path="/org/gnome/shell/extensions/nightthemeswitcher/color-scheme/">
<key name="day" enum="org.gnome.shell.extensions.nightthemeswitcher.color-scheme-enum">
<default>"default"</default>
+53 -2
View File
@@ -8,6 +8,53 @@ SPDX-License-Identifier: GPL-3.0-or-later
<property name="name">tweaks</property>
<property name="title" translatable="yes">Tweaks</property>
<property name="icon-name">nightthemeswitcher-tweaks-symbolic</property>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwExpanderRow">
<property name="title" translatable="yes">Manual location</property>
<property name="subtitle" translatable="yes">Use a manual location instead of system the location service.</property>
<child type="suffix">
<object class="GtkSwitch" id="manual_location_switch">
<property name="valign">center</property>
</object>
</child>
<child>
<object class="AdwSpinRow" id="latitude_spin">
<property name="title" translatable="yes">Latitude</property>
<binding name="sensitive">
<lookup name="active">manual_location_switch</lookup>
</binding>
<property name="digits">2</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">-90</property>
<property name="upper">90</property>
<property name="step-increment">0.01</property>
</object>
</property>
</object>
</child>
<child>
<object class="AdwSpinRow" id="longitude_spin">
<property name="title" translatable="yes">Longitude</property>
<binding name="sensitive">
<lookup name="active">manual_location_switch</lookup>
</binding>
<property name="digits">2</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">-180</property>
<property name="upper">180</property>
<property name="step-increment">0.01</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
@@ -22,7 +69,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Sunrise</property>
<property name="sensitive" bind-source="manual_schedule_switch" bind-property="active" bind-flags="sync-create"/>
<binding name="sensitive">
<lookup name="active">manual_schedule_switch</lookup>
</binding>
<child>
<object class="TimeChooser" id="schedule_sunrise_time_chooser">
<property name="margin-top">12</property>
@@ -35,7 +84,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Sunset</property>
<property name="sensitive" bind-source="manual_schedule_switch" bind-property="active" bind-flags="sync-create"/>
<binding name="sensitive">
<lookup name="active">manual_schedule_switch</lookup>
</binding>
<child>
<object class="TimeChooser" id="schedule_sunset_time_chooser">
<property name="margin-top">12</property>
+63 -48
View File
@@ -27,10 +27,11 @@ import { Time } from "../enums/Time.js";
* forced the manual schedule in the preferences.
*/
export class Timer extends GObject.Object {
#settings;
#colorSchemeSettings;
#interfaceSettings;
#locationSettings;
#timeSettings;
#colorSchemeSettings;
#systemInterfaceSettings;
#systemLocationSettings;
/** @type {Time | undefined} */
#time;
@@ -57,10 +58,11 @@ export class Timer extends GObject.Object {
constructor() {
super();
this.#settings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.time`);
this.#locationSettings = NTS.getSettings(`${NTS.metadata["settings-schema"]}.location`);
this.#timeSettings = 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" });
this.#systemInterfaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" });
this.#systemLocationSettings = new Gio.Settings({ schema: "org.gnome.system.location" });
}
enable() {
@@ -68,10 +70,13 @@ export class Timer extends GObject.Object {
this.#cancellable = new Gio.Cancellable();
this.#connectSettings();
this.#trackTime();
if (this.#settings.get_boolean("manual-schedule")) {
if (this.#timeSettings.get_boolean("manual-schedule")) {
debug.message("Using the manual schedule.");
} else if (this.#locationSettings.get_boolean("manual-location")) {
debug.message("Using the manual location.");
this.#trackSuntimes();
} else {
debug.message("Using location.");
debug.message("Using system location.");
this.#trackLocation();
this.#trackSuntimes();
}
@@ -116,7 +121,7 @@ export class Timer extends GObject.Object {
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) {
if (this.#timeSettings.get_boolean("fullscreen-transition") || !isMonitorFullscreen) {
try {
layoutManager.screenTransition.run();
} catch {}
@@ -127,30 +132,38 @@ export class Timer extends GObject.Object {
#connectSettings() {
debug.message("Connecting Timer to settings...");
this.#settingsConnections.push({
settings: this.#systemLocationSettings,
id: this.#systemLocationSettings.connect("changed::enabled", this.#onSystemLocationStateChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#locationSettings,
id: this.#locationSettings.connect("changed::enabled", this.#onLocationStateChanged.bind(this)),
id: this.#locationSettings.connect("changed::manual-location", this.#onManualLocationStateChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#settings,
id: this.#settings.connect("changed::location", this.#onLocationSettingsChanged.bind(this)),
settings: this.#locationSettings,
id: this.#locationSettings.connect("changed::latitude", this.#onLocationChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#settings,
id: this.#settings.connect("changed::manual-schedule", this.#onManualScheduleStateChanged.bind(this)),
settings: this.#locationSettings,
id: this.#locationSettings.connect("changed::longitude", this.#onLocationChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#settings,
id: this.#settings.connect(
settings: this.#timeSettings,
id: this.#timeSettings.connect("changed::manual-schedule", this.#onManualScheduleStateChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#timeSettings,
id: this.#timeSettings.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")) {
if (!this.#timeSettings.get_boolean("manual-schedule")) {
this.#settingsConnections.push({
settings: this.#settings,
id: this.#settings.connect("changed::offset", this.#onOffsetChanged.bind(this)),
settings: this.#timeSettings,
id: this.#timeSettings.connect("changed::offset", this.#onOffsetChanged.bind(this)),
});
}
}
@@ -214,12 +227,12 @@ export class Timer extends GObject.Object {
}
#addKeybinding() {
this.#previousKeybinding = this.#settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
if (!this.#settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0]) return;
this.#previousKeybinding = this.#timeSettings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
if (!this.#timeSettings.get_strv("nightthemeswitcher-ondemand-keybinding")[0]) return;
debug.message("Adding keybinding...");
wm.addKeybinding(
"nightthemeswitcher-ondemand-keybinding",
this.#settings,
this.#timeSettings,
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => {
@@ -242,12 +255,17 @@ export class Timer extends GObject.Object {
return colorScheme === this.#colorSchemeSettings.get_string("night") ? Time.NIGHT : Time.DAY;
}
#onLocationStateChanged() {
#onSystemLocationStateChanged() {
this.disable();
this.enable();
}
#onLocationSettingsChanged() {
#onManualLocationStateChanged() {
this.disable();
this.enable();
}
#onLocationChanged() {
this.#updateSuntimes();
}
@@ -268,17 +286,13 @@ export class Timer extends GObject.Object {
#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.#geoclueLocationConnectionId = this.#geoclue.connect(
"notify::location",
this.#onGeoclueLocationChanged.bind(this),
);
this.#updateSuntimes();
} else {
debug.message("Connected to GeoClue.");
this.#onGeoclueLocationChanged();
} catch (e) {
console.error(
`[${NTS.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.\n${e}`,
);
@@ -293,31 +307,31 @@ export class Timer extends GObject.Object {
const notification = new MessageTray.Notification({
source,
title: _("Unknown Location"),
body: _("A manual schedule will be used to switch the dark mode."),
title: _("Error while getting your location"),
body: _("A manual schedule will be used. You can edit it or manually set your location in the preferences."),
"icon-name": "location-services-disabled-symbolic",
});
notification.addAction(_("Edit Manual Schedule"), () => NTS.openPreferences());
notification.addAction(_("Open Preferences"), () => NTS.openPreferences());
notification.connect("activated", () => NTS.openPreferences());
source.addNotification(notification);
this.#settings.set_boolean("manual-schedule", true);
}
this.#timeSettings.set_boolean("manual-schedule", true);
}
}
#onLocationChanged(_geoclue, _location) {
#onGeoclueLocationChanged(_geoclue, _location) {
debug.message("Location has changed.");
const { latitude, longitude } = this.#geoclue.get_location();
debug.message(`Current location: (${latitude};${longitude})`);
this.#settings.set_value("location", new GLib.Variant("(dd)", [latitude, longitude]));
this.#locationSettings.set_double("latitude", latitude);
this.#locationSettings.set_double("longitude", longitude);
}
#computeTime() {
const sunrise = this.#settings.get_double("sunrise");
const sunset = this.#settings.get_double("sunset");
const sunrise = this.#timeSettings.get_double("sunrise");
const sunset = this.#timeSettings.get_double("sunset");
const datetime = GLib.DateTime.new_now_local();
const hour = datetime.get_hour() + datetime.get_minute() / 60 + datetime.get_second() / 3600;
@@ -326,11 +340,12 @@ export class Timer extends GObject.Object {
// 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"));
else return this.#time || this.#colorSchemeToTime(this.#systemInterfaceSettings.get_string("color-scheme"));
}
#updateSuntimes() {
const [latitude, longitude] = this.#settings.get_value("location").deepUnpack();
const latitude = this.#locationSettings.get_double("latitude");
const longitude = this.#locationSettings.get_double("longitude");
if (latitude < -90 && latitude > 90 && longitude < -180 && longitude > 180) return;
@@ -386,12 +401,12 @@ export class Timer extends GObject.Object {
const modulo = (n, m) => ((n % m) + m) % m;
const offset = this.#settings.get_double("offset");
const offset = this.#timeSettings.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);
this.#timeSettings.set_double("sunrise", sunrise);
this.#timeSettings.set_double("sunset", sunset);
debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
}
+24 -11
View File
@@ -12,6 +12,9 @@ export class TweaksPage extends Adw.PreferencesPage {
GTypeName: "TweaksPage",
Template: "resource:///org/gnome/Shell/Extensions/nightthemeswitcher/preferences/ui/TweaksPage.ui",
InternalChildren: [
"manual_location_switch",
"latitude_spin",
"longitude_spin",
"manual_schedule_switch",
"keyboard_shortcut_button",
"schedule_sunrise_time_chooser",
@@ -24,24 +27,34 @@ export class TweaksPage extends Adw.PreferencesPage {
);
}
constructor({ settings, ...params } = {}) {
constructor({ locationSettings, timeSettings, ...params } = {}) {
super(params);
settings.bind("manual-schedule", this._manual_schedule_switch, "active", Gio.SettingsBindFlags.DEFAULT);
locationSettings.bind("manual-location", this._manual_location_switch, "active", Gio.SettingsBindFlags.DEFAULT);
locationSettings.bind("latitude", this._latitude_spin, "value", Gio.SettingsBindFlags.DEFAULT);
locationSettings.bind("longitude", this._longitude_spin, "value", 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);
// We have to manually force this for AdwSpinRow after binding the value property
this._latitude_spin.sensitive = this._manual_location_switch.active;
this._longitude_spin.sensitive = this._manual_location_switch.active;
settings.bind("offset", this._schedule_offset_spin_button, "value", Gio.SettingsBindFlags.DEFAULT);
timeSettings.bind("manual-schedule", this._manual_schedule_switch, "active", Gio.SettingsBindFlags.DEFAULT);
timeSettings.bind("sunrise", this._schedule_sunrise_time_chooser, "time", Gio.SettingsBindFlags.DEFAULT);
timeSettings.bind("sunset", this._schedule_sunset_time_chooser, "time", Gio.SettingsBindFlags.DEFAULT);
timeSettings.bind("offset", this._schedule_offset_spin_button, "value", Gio.SettingsBindFlags.DEFAULT);
timeSettings.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];
timeSettings.connect("changed::nightthemeswitcher-ondemand-keybinding", () => {
this._keyboard_shortcut_button.keybinding = timeSettings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
});
this._keyboard_shortcut_button.connect("notify::keybinding", () => {
settings.set_strv("nightthemeswitcher-ondemand-keybinding", [this._keyboard_shortcut_button.keybinding]);
timeSettings.set_strv("nightthemeswitcher-ondemand-keybinding", [this._keyboard_shortcut_button.keybinding]);
});
this._keyboard_shortcut_button.keybinding = settings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
this._keyboard_shortcut_button.keybinding = timeSettings.get_strv("nightthemeswitcher-ondemand-keybinding")[0];
}
}
+4 -1
View File
@@ -70,7 +70,10 @@ export default class NightThemeSwitcherPreferences extends ExtensionPreferences
colorSchemeSettings: this.getSettings(`${this.metadata["settings-schema"]}.color-scheme`),
}),
new CommandsPage({ settings: this.getSettings(`${this.metadata["settings-schema"]}.commands`) }),
new TweaksPage({ settings: this.getSettings(`${this.metadata["settings-schema"]}.time`) }),
new TweaksPage({
locationSettings: this.getSettings(`${this.metadata["settings-schema"]}.location`),
timeSettings: this.getSettings(`${this.metadata["settings-schema"]}.time`),
}),
new ContributePage(),
].forEach((page) => window.add(page));
}