Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions data/ui/preferences-shortcut-editor.ui
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ SPDX-License-Identifier: GPL-2.0-or-later
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Note that you have to first disable any conflicting shortucts in GNOME Settings.</property>
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
Expand Down Expand Up @@ -111,17 +125,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="conflict-label">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">confirm</property>
Expand All @@ -132,11 +135,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

17 changes: 8 additions & 9 deletions po/org.gnome.Shell.Extensions.GSConnect.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: org.gnome.Shell.Extensions.GSConnect\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-24 16:26-0400\n"
"POT-Creation-Date: 2025-07-27 10:35+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -469,8 +469,14 @@ msgstr ""
msgid "Set"
msgstr ""

#: data/ui/preferences-shortcut-editor.ui:59
msgid ""
"Note that you have to first disable any conflicting shortucts in GNOME "
"Settings."
msgstr ""

#. Keys for cancelling (␛) or resetting (␈) a shortcut
#: data/ui/preferences-shortcut-editor.ui:80
#: data/ui/preferences-shortcut-editor.ui:94
msgid "Press Esc to cancel or Backspace to reset the keyboard shortcut."
msgstr ""

Expand Down Expand Up @@ -642,13 +648,6 @@ msgstr ""
msgid "Enter a new shortcut to change <b>%s</b>"
msgstr ""

#. TRANSLATORS: When a keyboard shortcut is unavailable
#. Example: [Ctrl]+[S] is already being used
#: src/preferences/keybindings.js:132
#, javascript-format
msgid "%s is already being used"
msgstr ""

#: src/preferences/service.js:361
msgid "A complete KDE Connect implementation for GNOME"
msgstr ""
Expand Down
126 changes: 6 additions & 120 deletions src/preferences/keybindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

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';

Expand Down Expand Up @@ -46,7 +45,7 @@ export const ShortcutChooserDialog = GObject.registerClass({
Children: [
'cancel-button', 'set-button',
'stack', 'summary-label',
'shortcut-label', 'conflict-label',
'shortcut-label',
],
}, class ShortcutChooserDialog extends Gtk.Dialog {

Expand Down Expand Up @@ -122,18 +121,10 @@ export const ShortcutChooserDialog = GObject.registerClass({
realMask &= ~Gdk.ModifierType.LOCK_MASK;

if (keyvalLower !== 0 && realMask !== 0) {
this._ungrab();

// Set the accelerator property/label
this.accelerator = Gtk.accelerator_name(keyvalLower, realMask);

// TRANSLATORS: When a keyboard shortcut is unavailable
// Example: [Ctrl]+[S] is already being used
this.conflict_label.label = _('%s is already being used').format(
Gtk.accelerator_get_label(keyvalLower, realMask)
);

// Show Cancel button and switch to confirm/conflict page
// Show Cancel button and switch to confirm page
this.cancel_button.visible = true;
this.stack.visible_child_name = 'confirm';

Expand All @@ -143,130 +134,25 @@ export const ShortcutChooserDialog = GObject.registerClass({
return true;
}

async _check() {
_check() {
try {
const available = await checkAccelerator(this.accelerator);
// No known way to check availability, so don't. Don't grab input,
// so we don't accidentally overload accelerators as easily
const available = true;
this.set_button.visible = available;
this.conflict_label.visible = !available;
} catch (e) {
logError(e);
this.response(ResponseType.CANCEL);
}
}

_grab() {
const success = this._seat.grab(
this.get_window(),
Gdk.SeatCapabilities.KEYBOARD,
true, // owner_events
null, // cursor
null, // event
null
);

if (success !== Gdk.GrabStatus.SUCCESS)
return this.response(ResponseType.CANCEL);

if (!this._seat.get_keyboard() && !this._seat.get_pointer())
return this.response(ResponseType.CANCEL);

this.grab_add();
}

_ungrab() {
this._seat.ungrab();
this.grab_remove();
}

// Override to use our own ungrab process
response(response_id) {
this.hide();
this._ungrab();

return super.response(response_id);
}

// Override with a non-blocking version of Gtk.Dialog.run()
run() {
this.show();

// Wait a bit before attempting grab
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
this._grab();
return GLib.SOURCE_REMOVE;
});
}
});


/**
* Check the availability of an accelerator using GNOME Shell's DBus interface.
*
* @param {string} accelerator - An accelerator
* @param {number} [modeFlags] - Mode Flags
* @param {number} [grabFlags] - Grab Flags
* @returns {boolean} %true if available, %false on error or unavailable
*/
export async function checkAccelerator(accelerator, modeFlags = 0, grabFlags = 0) {
try {
let result = false;

// Try to grab the accelerator
const action = await new Promise((resolve, reject) => {
Gio.DBus.session.call(
'org.gnome.Shell',
'/org/gnome/Shell',
'org.gnome.Shell',
'GrabAccelerator',
new GLib.Variant('(suu)', [accelerator, modeFlags, grabFlags]),
null,
Gio.DBusCallFlags.NONE,
-1,
null,
(connection, res) => {
try {
res = connection.call_finish(res);
resolve(res.deepUnpack()[0]);
} catch (e) {
reject(e);
}
}
);
});

// If successful, use the result of ungrabbing as our return
if (action !== 0) {
result = await new Promise((resolve, reject) => {
Gio.DBus.session.call(
'org.gnome.Shell',
'/org/gnome/Shell',
'org.gnome.Shell',
'UngrabAccelerator',
new GLib.Variant('(u)', [action]),
null,
Gio.DBusCallFlags.NONE,
-1,
null,
(connection, res) => {
try {
res = connection.call_finish(res);
resolve(res.deepUnpack()[0]);
} catch (e) {
reject(e);
}
}
);
});
}

return result;
} catch (e) {
logError(e);
return false;
}
}


/**
* Show a dialog to get a keyboard shortcut from a user.
*
Expand Down