[gnome] Move to Grimble tiling manager

This commit is contained in:
2026-01-14 00:55:43 -05:00
parent de41369f5f
commit 941c727d38
90 changed files with 3999 additions and 9602 deletions

View File

@ -0,0 +1,201 @@
/**
* © gnome-shell-extension-tiling-assistant
* Source: https://git.launchpad.net/ubuntu/+source/gnome-shell-extension-tiling-assistant/tree/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js
*
* This code is copy from Ubuntu tiling assistant extension (GPL-2.0).
*/
// @ts-nocheck
import Adw from 'gi://Adw';
import Gtk from 'gi://Gtk';
import Gdk from 'gi://Gdk';
import GObject from 'gi://GObject';
import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
import {getKeybindingController} from '../keybindingController.js';
/**
* A Widget to implement the shortcuts in the preference window.
* It's an AdwActionRow, which contains a label showing the keybinding(s)
* and a shortcut-clear-button.
*
* Some parts are from https://extensions.gnome.org/extension/2236/night-theme-switcher/.
* _isBindingValid & _isKeyvalForbidden are straight up copied from its util.js
* https://gitlab.com/rmnvgr/nightthemeswitcher-gnome-shell-extension/-/blob/main/src/utils.js
*/
export const ShortcutListener = GObject.registerClass({
GTypeName: 'ShortcutListener',
Template: import.meta.url.replace(/prefs\/(.*)\.js$/, 'ui/$1.ui'),
InternalChildren: ['keybindingLabel', 'clearButton', 'eventKeyController'],
Properties: {
keybinding: GObject.ParamSpec.string('keybinding', 'Keybinding', 'Key sequence', GObject.ParamFlags.READWRITE, null),
},
}, class ShortcutListener extends Adw.ActionRow {
/**
* Only allow 1 active ShortcutListener at a time
*/
static isListening = false;
static isAppendingShortcut = false;
static listener = null;
static listeningText = 'Press a shortcut...';
static appendingText = 'Append a new shortcut...';
/**
* Starts listening for a keyboard shortcut.
*
* @param {ShortcutListener} shortcutListener the new active ShortcutListener
*/
static listen(shortcutListener) {
if (shortcutListener === ShortcutListener.listener)
return;
ShortcutListener.stopListening();
shortcutListener.isActive = true;
shortcutListener.setKeybindingLabel(ShortcutListener.listeningText);
ShortcutListener.listener = shortcutListener;
ShortcutListener.isListening = true;
}
/**
* Stops listening for a keyboard shortcut.
*/
static stopListening() {
if (!ShortcutListener.isListening)
return;
ShortcutListener.isListening = false;
ShortcutListener.isAppendingShortcut = false;
ShortcutListener.listener.isActive = false;
ShortcutListener.listener.setKeybindingLabel(ShortcutListener.listener.getKeybindingLabel());
ShortcutListener.listener = null;
}
initialize(key, setting) {
this._key = key;
this._setting = setting;
this.isActive = false;
this.connect('realize', () => this.get_root().add_controller(this._eventKeyController));
this.keybinding = this._setting.get_strv(key) ?? [];
this._settingChangedId = this._setting.connect(`changed::${key}`, () => {
this.keybinding = this._setting.get_strv(key) ?? [];
this.setKeybindingLabel(this.getKeybindingLabel());
this._clearButton.set_sensitive(this.keybinding.length);
});
}
/*
* Sets the label of the keybinding.
*/
setKeybindingLabel(label) {
this._keybindingLabel.set_label(label);
}
/**
* Gets the keybinding in a more pleasant to read format.
* For example: [<Control><Super>e,<Super>a] will become
* 'Ctrl+Super+E / Super+A' or 'Disabled'
*
* @returns {string}
*/
getKeybindingLabel() {
const kbLabel = this.keybinding.reduce((label, kb) => {
const [, keyval, mask] = Gtk.accelerator_parse(kb);
const l = Gtk.accelerator_get_label(keyval, mask);
if (!label)
return l;
return l ? `${label} / ${l}` : label;
}, '');
return kbLabel || _('Disabled');
}
_onActivated() {
this.isActive ? ShortcutListener.stopListening() : ShortcutListener.listen(this);
}
_onKeybindingChanged() {
this._setting.set_strv(this._key, this.keybinding);
this._clearButton.set_sensitive(this.keybinding.length);
this.setKeybindingLabel(this.getKeybindingLabel());
}
_onClearButtonClicked() {
this.keybinding = [];
this._setting.set_string('keybinding-config', 'Custom');
getKeybindingController().update('');
ShortcutListener.stopListening();
}
_onKeyPressed(eventControllerKey, keyval, keycode, state) {
if (this !== ShortcutListener.listener)
return Gdk.EVENT_PROPAGATE;
let mask = state & Gtk.accelerator_get_default_mod_mask();
mask &= ~Gdk.ModifierType.LOCK_MASK;
if (mask === 0) {
switch (keyval) {
case Gdk.KEY_BackSpace:
this.keybinding = [];
// falls through
case Gdk.KEY_Escape:
ShortcutListener.stopListening();
return Gdk.EVENT_STOP;
case Gdk.KEY_KP_Enter:
case Gdk.KEY_Return:
case Gdk.KEY_space:
ShortcutListener.isAppendingShortcut = !ShortcutListener.isAppendingShortcut;
this.setKeybindingLabel(ShortcutListener.isAppendingShortcut
? ShortcutListener.appendingText
: ShortcutListener.listeningText);
return Gdk.EVENT_STOP;
}
}
if (!this._isBindingValid({mask, keycode, keyval}) ||
!Gtk.accelerator_valid(keyval, mask))
return Gdk.EVENT_STOP;
const sc = Gtk.accelerator_name_with_keycode(null, keyval, keycode, mask);
this.keybinding = ShortcutListener.isAppendingShortcut ? [...this.keybinding, sc] : [sc];
this._setting.set_string('keybinding-config', 'Custom');
getKeybindingController().update('');
ShortcutListener.stopListening();
return Gdk.EVENT_STOP;
}
/**
* Checks, 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.
*/
_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) ||
this._isKeyvalForbidden(keyval))
return false;
}
return true;
}
/**
* Checks, if the given keyval is forbidden.
*
* @param {number} keyval The keyval number.
* @returns {boolean} `true` if the keyval is forbidden.
*/
_isKeyvalForbidden(keyval) {
const forbiddenKeyvals = [
Gdk.KEY_Home,
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);
}
});