388 lines
13 KiB
JavaScript
388 lines
13 KiB
JavaScript
/*
|
|
This file is part of Caffeine (gnome-shell-extension-caffeine).
|
|
|
|
Caffeine is free software: you can redistribute it and/or modify it under the terms of
|
|
the GNU General Public License as published by the Free Software Foundation, either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
Caffeine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with Caffeine.
|
|
If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
Copyright 2022 Pakaoraki
|
|
|
|
// From https://gitlab.com/skrewball/openweather/-/blob/master/src/prefs.js
|
|
*/
|
|
/* exported GeneralPage */
|
|
'use strict';
|
|
|
|
import Adw from 'gi://Adw';
|
|
import Gtk from 'gi://Gtk';
|
|
import GObject from 'gi://GObject';
|
|
import Gdk from 'gi://Gdk';
|
|
|
|
import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
|
|
|
const genParam = (type, name, ...dflt) => GObject.ParamSpec[type](name, name, name, GObject.ParamFlags.READWRITE, ...dflt);
|
|
|
|
const TIMERS_DURATION = [
|
|
'05, 10, 30',
|
|
'10, 20, 45',
|
|
'15, 30, 60',
|
|
'20, 40, 75',
|
|
'30, 50, 80'
|
|
];
|
|
|
|
export var GeneralPage = GObject.registerClass(
|
|
class CaffeineGeneralPage extends Adw.PreferencesPage {
|
|
_init(settings, settingsKey) {
|
|
super._init({
|
|
title: _('General'),
|
|
icon_name: 'general-symbolic',
|
|
name: 'GeneralPage'
|
|
});
|
|
this._settings = settings;
|
|
this._settingsKey = settingsKey;
|
|
|
|
// Behavior group
|
|
// --------------
|
|
let behaviorGroup = new Adw.PreferencesGroup({
|
|
title: _('Behavior')
|
|
});
|
|
|
|
// Enable / Disable fullscreen apps
|
|
let disableFullscreenSwitch = new Gtk.Switch({
|
|
valign: Gtk.Align.CENTER,
|
|
active: this._settings.get_boolean(this._settingsKey.FULLSCREEN)
|
|
});
|
|
let disableFullscreenRow = new Adw.ActionRow({
|
|
title: _('Enable for fullscreen apps'),
|
|
subtitle: _('Automatically enable when an app enters fullscreen mode'),
|
|
activatable_widget: disableFullscreenSwitch
|
|
});
|
|
disableFullscreenRow.add_suffix(disableFullscreenSwitch);
|
|
|
|
// Remember state
|
|
let rememberStateSwitch = new Gtk.Switch({
|
|
valign: Gtk.Align.CENTER,
|
|
active: this._settings.get_boolean(this._settingsKey.RESTORE)
|
|
});
|
|
let rememberStateRow = new Adw.ActionRow({
|
|
title: _('Remember state'),
|
|
subtitle: _('Remember the last state across sessions and reboots'),
|
|
activatable_widget: rememberStateSwitch
|
|
});
|
|
rememberStateRow.add_suffix(rememberStateSwitch);
|
|
|
|
// Pause and resume Night Light
|
|
let pauseNightLight = new Gtk.StringList();
|
|
pauseNightLight.append(_('Never'));
|
|
pauseNightLight.append(_('Always'));
|
|
pauseNightLight.append(_('For apps on list'));
|
|
let pauseNightLightRow = new Adw.ComboRow({
|
|
title: _('Pause and resume Night Light'),
|
|
subtitle: _('Toggles the night light together with Caffeine\'s state'),
|
|
model: pauseNightLight,
|
|
selected: this._settings.get_enum(this._settingsKey.NIGHT_LIGHT)
|
|
});
|
|
|
|
// Allow blank screen
|
|
let allowBlankScreen = new Gtk.StringList();
|
|
allowBlankScreen.append(_('Never'));
|
|
allowBlankScreen.append(_('Always'));
|
|
allowBlankScreen.append(_('For apps on list'));
|
|
let allowBlankScreenRow = new Adw.ComboRow({
|
|
title: _('Allow screen blank'),
|
|
subtitle: _('Allow turning off screen when Caffeine is enabled'),
|
|
model: allowBlankScreen,
|
|
selected: this._settings.get_enum(this._settingsKey.SCREEN_BLANK)
|
|
});
|
|
|
|
// Add elements
|
|
behaviorGroup.add(disableFullscreenRow);
|
|
behaviorGroup.add(rememberStateRow);
|
|
behaviorGroup.add(pauseNightLightRow);
|
|
behaviorGroup.add(allowBlankScreenRow);
|
|
this.add(behaviorGroup);
|
|
|
|
// Timer group
|
|
// --------------
|
|
let timerGroup = new Adw.PreferencesGroup({
|
|
title: _('Timer')
|
|
});
|
|
|
|
const durationIndex = this._settings.get_int(this._settingsKey.DURATION_TIMER_INDEX);
|
|
this.timerOptionRow = new Adw.ActionRow({
|
|
title: _('Durations'),
|
|
activatable: true
|
|
});
|
|
|
|
let adjustSliderTimer = new Gtk.Adjustment({
|
|
lower: 0,
|
|
upper: 4,
|
|
step_increment: 0.1,
|
|
page_increment: 1,
|
|
value: durationIndex
|
|
});
|
|
this._updateTimerDuration(durationIndex);
|
|
|
|
let sliderTimer = new Gtk.Scale({
|
|
valign: 'center',
|
|
hexpand: true,
|
|
width_request: '200px',
|
|
round_digits: false,
|
|
draw_value: false,
|
|
orientation: 'horizontal',
|
|
digits: 0,
|
|
adjustment: adjustSliderTimer
|
|
});
|
|
sliderTimer.add_mark(0, Gtk.PositionType.BOTTOM, null);
|
|
sliderTimer.add_mark(1, Gtk.PositionType.BOTTOM, null);
|
|
sliderTimer.add_mark(2, Gtk.PositionType.BOTTOM, null);
|
|
sliderTimer.add_mark(3, Gtk.PositionType.BOTTOM, null);
|
|
sliderTimer.add_mark(4, Gtk.PositionType.BOTTOM, null);
|
|
this.timerOptionRow.add_suffix(sliderTimer);
|
|
|
|
// Add elements
|
|
timerGroup.add(this.timerOptionRow);
|
|
this.add(timerGroup);
|
|
|
|
// Shortcut group
|
|
// --------------
|
|
let deleteShortcutButton = new Gtk.Button({
|
|
icon_name: 'edit-delete-symbolic',
|
|
valign: Gtk.Align.CENTER,
|
|
css_classes: ['error'],
|
|
hexpand: false,
|
|
vexpand: false
|
|
});
|
|
let shortcutGroup = new Adw.PreferencesGroup({
|
|
title: _('Shortcut'),
|
|
header_suffix: deleteShortcutButton
|
|
});
|
|
|
|
// Keyboard shortcut
|
|
this.shortcutKeyBoard = new ShortcutSettingWidget(
|
|
this._settings,
|
|
this._settingsKey.TOGGLE_SHORTCUT,
|
|
_('Toggle shortcut'),
|
|
_('Use Backspace to clear')
|
|
);
|
|
|
|
// Hide/Show delete button
|
|
if (!this.shortcutKeyBoard.isAcceleratorSet()) {
|
|
deleteShortcutButton.visible = false;
|
|
}
|
|
|
|
// Add elements
|
|
shortcutGroup.add(this.shortcutKeyBoard);
|
|
this.add(shortcutGroup);
|
|
|
|
|
|
// Bind signals
|
|
// --------------
|
|
disableFullscreenSwitch.connect('notify::active', (widget) => {
|
|
this._settings.set_boolean(this._settingsKey.FULLSCREEN, widget.get_active());
|
|
});
|
|
rememberStateSwitch.connect('notify::active', (widget) => {
|
|
this._settings.set_boolean(this._settingsKey.RESTORE, widget.get_active());
|
|
});
|
|
pauseNightLightRow.connect('notify::selected', (widget) => {
|
|
this._settings.set_enum(this._settingsKey.NIGHT_LIGHT, widget.selected);
|
|
});
|
|
allowBlankScreenRow.connect('notify::selected', (widget) => {
|
|
this._settings.set_enum(this._settingsKey.SCREEN_BLANK, widget.selected);
|
|
});
|
|
sliderTimer.connect('change-value',
|
|
(widget) => this._updateTimerDuration(widget.get_value()));
|
|
deleteShortcutButton.connect('clicked', this._resetShortcut.bind(this));
|
|
this._settings.connect(`changed::${this._settingsKey.TOGGLE_SHORTCUT}`, () => {
|
|
if (this.shortcutKeyBoard.isAcceleratorSet()) {
|
|
deleteShortcutButton.visible = true;
|
|
} else {
|
|
deleteShortcutButton.visible = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
_updateTimerDuration(value) {
|
|
const durationIndex = this._settings.get_int(this._settingsKey.DURATION_TIMER_INDEX);
|
|
this.timerOptionRow.set_subtitle(_('Set to ') + TIMERS_DURATION[value] + _(' minutes'));
|
|
if (durationIndex !== value) {
|
|
this._settings.set_int(this._settingsKey.DURATION_TIMER_INDEX, value);
|
|
}
|
|
}
|
|
|
|
_resetShortcut() {
|
|
this.shortcutKeyBoard.resetAccelerator();
|
|
}
|
|
});
|
|
|
|
/*
|
|
* Shortcut Widget
|
|
*/
|
|
const ShortcutSettingWidget = class extends Adw.ActionRow {
|
|
static {
|
|
GObject.registerClass({
|
|
Properties: {
|
|
shortcut: genParam('string', 'shortcut', '')
|
|
},
|
|
Signals: {
|
|
changed: { param_types: [GObject.TYPE_STRING] }
|
|
}
|
|
}, this);
|
|
}
|
|
|
|
constructor(settings, key, label, sublabel) {
|
|
super({
|
|
title: label,
|
|
subtitle: sublabel,
|
|
activatable: true
|
|
});
|
|
|
|
this.shortcutBox = new Gtk.Box({
|
|
orientation: Gtk.Orientation.HORIZONTAL,
|
|
halign: Gtk.Align.CENTER,
|
|
spacing: 5,
|
|
hexpand: false,
|
|
vexpand: false
|
|
});
|
|
|
|
this._key = key;
|
|
this._settings = settings;
|
|
this._description = sublabel;
|
|
|
|
this.add_suffix(this.shortcutBox);
|
|
this.shortLabel = new Gtk.ShortcutLabel({
|
|
disabled_text: _('New accelerator…'),
|
|
valign: Gtk.Align.CENTER,
|
|
hexpand: false,
|
|
vexpand: false
|
|
});
|
|
|
|
this.shortcutBox.append(this.shortLabel);
|
|
|
|
// Bind signals
|
|
this.connect('activated', this._onActivated.bind(this));
|
|
this.bind_property('shortcut', this.shortLabel, 'accelerator', GObject.BindingFlags.DEFAULT);
|
|
[this.shortcut] = this._settings.get_strv(this._key);
|
|
|
|
this.add_suffix(this.shortcutBox);
|
|
}
|
|
|
|
isAcceleratorSet() {
|
|
if (this.shortLabel.get_accelerator()) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
resetAccelerator() {
|
|
this.saveShortcut(); // Clear shortcut
|
|
}
|
|
|
|
_onActivated(widget) {
|
|
let ctl = new Gtk.EventControllerKey();
|
|
|
|
let content = new Adw.StatusPage({
|
|
title: _('New accelerator…'),
|
|
description: this._description,
|
|
icon_name: 'preferences-desktop-keyboard-shortcuts-symbolic'
|
|
});
|
|
|
|
this._editor = new Adw.Window({
|
|
modal: true,
|
|
hide_on_close: true,
|
|
transient_for: widget.get_root(),
|
|
width_request: 480,
|
|
height_request: 320,
|
|
content
|
|
});
|
|
|
|
this._editor.add_controller(ctl);
|
|
ctl.connect('key-pressed', this._onKeyPressed.bind(this));
|
|
this._editor.present();
|
|
}
|
|
|
|
_onKeyPressed(_widget, keyval, keycode, state) {
|
|
let mask = state & Gtk.accelerator_get_default_mod_mask();
|
|
mask &= ~Gdk.ModifierType.LOCK_MASK;
|
|
|
|
if (!mask && keyval === Gdk.KEY_Escape) {
|
|
this._editor.close();
|
|
return Gdk.EVENT_STOP;
|
|
}
|
|
|
|
if (keyval === Gdk.KEY_BackSpace) {
|
|
this.saveShortcut(); // Clear shortcut
|
|
return Gdk.EVENT_STOP;
|
|
}
|
|
|
|
if (!this.isValidBinding(mask, keycode, keyval) || !this.isValidAccel(mask, keyval)) {
|
|
return Gdk.EVENT_STOP;
|
|
}
|
|
|
|
this.saveShortcut(keyval, keycode, mask);
|
|
return Gdk.EVENT_STOP;
|
|
}
|
|
|
|
saveShortcut(keyval, keycode, mask) {
|
|
if (!keyval && !keycode) {
|
|
this.shortcut = '';
|
|
} else {
|
|
this.shortcut = Gtk.accelerator_name_with_keycode(null, keyval, keycode, mask);
|
|
}
|
|
|
|
this.emit('changed', this.shortcut);
|
|
this._settings.set_strv(this._key, [this.shortcut]);
|
|
this._editor.destroy();
|
|
}
|
|
|
|
// Functions from https://gitlab.gnome.org/GNOME/gnome-control-center/-/blob/main/panels/keyboard/keyboard-shortcuts.c
|
|
|
|
keyvalIsForbidden(keyval) {
|
|
return [
|
|
// Navigation keys
|
|
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,
|
|
|
|
// Return
|
|
Gdk.KEY_KP_Enter,
|
|
Gdk.KEY_Return,
|
|
|
|
Gdk.KEY_Mode_switch
|
|
].includes(keyval);
|
|
}
|
|
|
|
isValidBinding(mask, keycode, keyval) {
|
|
return !(mask === 0 || mask === Gdk.ModifierType.SHIFT_MASK && keycode !== 0 &&
|
|
((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) || this.keyvalIsForbidden(keyval))
|
|
);
|
|
}
|
|
|
|
isValidAccel(mask, keyval) {
|
|
return Gtk.accelerator_valid(keyval, mask) || (keyval === Gdk.KEY_Tab && mask !== 0);
|
|
}
|
|
};
|