Skip to content
Merged
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
9 changes: 6 additions & 3 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ src/Layout/Widgets/AdvancedSettingsPanel.vala
src/Layout/Widgets/Display.vala
src/Plug.vala
src/Shortcuts/Backend/ConflictsManager.vala
src/Shortcuts/Backend/CustomShortcutSettings.vala
src/Shortcuts/Backend/CustomShortcuts.vala
src/Shortcuts/Backend/Settings.vala
src/Shortcuts/Backend/ShortcutsList.vala
src/Shortcuts/Backend/Shortcut.vala
src/Shortcuts/Shortcuts.vala
src/Shortcuts/Backend/ShortcutsList.vala
src/Shortcuts/Backend/Utils.vala
src/Shortcuts/Widgets/AppChooser.vala
src/Shortcuts/Widgets/AppChooserRow.vala
src/Shortcuts/Widgets/CustomShortcutListBox.vala
src/Shortcuts/Widgets/CustomShortcutRow.vala
src/Shortcuts/Widgets/ShortcutListBox.vala
src/Shortcuts/Shortcuts.vala
15 changes: 14 additions & 1 deletion src/Shortcuts/Backend/ConflictsManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,20 @@ class Keyboard.Shortcuts.ConflictsManager : GLib.Object {
private static bool custom_shortcut_conflicts (Shortcut shortcut, out string name, out string group) {
name = "";
group = SectionID.CUSTOM.to_string ();
return CustomShortcutSettings.shortcut_conflicts (shortcut, out name, null);

var application_shortcuts = new GLib.Settings (CustomShortcuts.SETTINGS_SCHEMA);
var shortcuts = (CustomShortcuts.ParsedShortcut[]) application_shortcuts.get_value (CustomShortcuts.APPLICATION_SHORTCUTS);
for (int i = 0; i < shortcuts.length; i++) {
for (int j = 0; j < shortcuts[i].keybindings.length; j++) {
var action_shortcut = new Shortcut.parse (shortcuts[i].keybindings[j]);
if (shortcut.is_equal (action_shortcut)) {
name = shortcuts[i].target;
return true;
}
}
}

return false;
}

private static bool standard_shortcut_conflicts (Shortcut shortcut, out string name, out string group) {
Expand Down
158 changes: 0 additions & 158 deletions src/Shortcuts/Backend/CustomShortcutSettings.vala

This file was deleted.

21 changes: 21 additions & 0 deletions src/Shortcuts/Backend/CustomShortcuts.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
*/

namespace Keyboard.Shortcuts.CustomShortcuts {
public enum ActionType {
DESKTOP_FILE,
COMMAND_LINE
}

public struct ParsedShortcut {
ActionType type;
string target;
GLib.HashTable<string, Variant> parameters;
string[] keybindings;
}

public const string SETTINGS_SCHEMA = "io.elementary.settings-daemon.applications";
public const string APPLICATION_SHORTCUTS = "application-shortcuts";
}
29 changes: 29 additions & 0 deletions src/Shortcuts/Backend/Utils.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
*/

namespace Keyboard.Shortcuts.Utils {
public GLib.Icon? get_action_icon (GLib.DesktopAppInfo app_info, string action) {
unowned var icon_theme = Gtk.IconTheme.get_for_display (Gdk.Display.get_default ());

GLib.Icon? action_icon = null;
try {
var keyfile = new GLib.KeyFile ();
keyfile.load_from_file (app_info.get_filename (), GLib.KeyFileFlags.NONE);

var group = "Desktop Action %s".printf (action);
if (keyfile.has_key (group, GLib.KeyFileDesktop.KEY_ICON)) {
var icon_name = keyfile.get_string (group, GLib.KeyFileDesktop.KEY_ICON);

if (icon_theme.has_icon (icon_name)) {
action_icon = new ThemedIcon (icon_name);
}
}
} catch (Error e) {
warning (e.message);
}

return action_icon;
}
}
9 changes: 1 addition & 8 deletions src/Shortcuts/Shortcuts.vala
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ namespace Keyboard.Shortcuts {
private SwitcherRow custom_shortcuts_row;

construct {
CustomShortcutSettings.init ();

unowned var list = Shortcuts.ShortcutsList.get_default ();

section_switcher = new Gtk.ListBox ();
Expand Down Expand Up @@ -113,12 +111,7 @@ namespace Keyboard.Shortcuts {
for (int id = 0; id < SectionID.CUSTOM; id++) {
shortcut_views += new ShortcutListBox ((SectionID) id);
}

if (CustomShortcutSettings.available) {
var custom_tree = new CustomShortcutListBox ();

shortcut_views += custom_tree;
}
shortcut_views += new CustomShortcutListBox ();

foreach (unowned Gtk.Widget view in shortcut_views) {
stack.add_child (view);
Expand Down
118 changes: 118 additions & 0 deletions src/Shortcuts/Widgets/AppChooser.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
*/

public class Keyboard.Shortcuts.AppChooser : Granite.Dialog {
public signal void app_chosen (string filename, GLib.HashTable<string, Variant> parameters);
public signal void custom_command_chosen (string command, GLib.HashTable<string, Variant> parameters);

private Gtk.ListBox list;
private Gtk.SearchEntry search_entry;
private Gtk.Entry custom_entry;

construct {
search_entry = new Gtk.SearchEntry () {
placeholder_text = _("Search Applications")
};
Comment thread
lenemter marked this conversation as resolved.
search_entry.set_key_capture_widget (this);

list = new Gtk.ListBox () {
hexpand = true,
vexpand = true
};
list.add_css_class (Granite.STYLE_CLASS_RICH_LIST);
list.set_sort_func (sort_function);
list.set_filter_func (filter_function);

var scrolled = new Gtk.ScrolledWindow () {
child = list,
has_frame = true
};

custom_entry = new Gtk.Entry () {
placeholder_text = _("Type in a custom command"),
primary_icon_activatable = false,
primary_icon_name = "utilities-terminal-symbolic"
};

var box = new Gtk.Box (VERTICAL, 6);
box.append (search_entry);
box.append (scrolled);
box.append (custom_entry);

modal = true;
default_height = 500;
default_width = 400;
get_content_area ().append (box);
add_button (_("Cancel"), Gtk.ResponseType.CANCEL);

// TRANSLATORS: This string is used by screen reader
update_property (Gtk.AccessibleProperty.LABEL, _("Select an app"), -1);

search_entry.grab_focus ();
search_entry.search_changed.connect (() => {
list.invalidate_filter ();
});

response.connect (hide);

list.row_activated.connect (on_app_selected);
custom_entry.activate.connect (on_custom_command_entered);
}

public void init_list (GLib.List<GLib.DesktopAppInfo> app_infos) {
foreach (var app_info in app_infos) {
var icon = app_info.get_icon () ?? new ThemedIcon ("application-default-icon");
var name = app_info.get_name ();
var description = app_info.get_description ();
var filename = File.new_for_path (app_info.get_filename ()).get_basename ();

list.prepend (new AppChooserRow ({icon, null, name, description, null, filename}));

unowned var icon_theme = Gtk.IconTheme.get_for_display (Gdk.Display.get_default ());
var actions = app_info.list_actions ();
for (var i = 0; i < actions.length; i++) {
var action = actions[i];
var action_icon = Utils.get_action_icon (app_info, action);
var action_name = "%s → %s".printf (name, app_info.get_action_name (action));
list.prepend (new AppChooserRow ({icon, action_icon, action_name, description, action, filename}));
}
}
}

private int sort_function (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
unowned AppChooserRow row_1 = (AppChooserRow) row1.get_child ();
unowned AppChooserRow row_2 = (AppChooserRow) row2.get_child ();

var name_1 = row_1.info.name;
var name_2 = row_2.info.name;

return name_1.collate (name_2);
}

private bool filter_function (Gtk.ListBoxRow list_box_row) {
var app_row = (AppChooserRow) list_box_row.get_child ();
return search_entry.text.down () in app_row.info.name.down ()
|| search_entry.text.down () in app_row.info.name.down ();
}

private void on_app_selected (Gtk.ListBoxRow list_box_row) {
var app_row = (AppChooserRow) list_box_row.get_child ();

var parameters = new GLib.HashTable<string, Variant> (null, null);
if (app_row.info.action != null) {
parameters["action"] = app_row.info.action;
}

app_chosen (app_row.info.filename, parameters);
hide ();
}

private void on_custom_command_entered () {
custom_command_chosen (
custom_entry.text,
new GLib.HashTable<string, Variant> (null, null)
);
hide ();
}
}
Loading