This adds a filter entry in the keybindings tab in the preferences dialog, similar to the filter entry in the plugin manager dialog.
![Screenshot_2025-01-05_11-04-26](https://github.com/user-attachments/assets/6afee9e9-5f64-41ef-bcc9-39646f5ee...)
Remarks: - only the action name is considered for filtering, not the keybindings - the categories are always shown (hiding them on no matching children isn't trivial) - Ctrl-F in the TreeView sets focus to the filter entry - Ctrl-F in the TreeView in the plugin manager dialog sets focus to the filter entry - I also changed the dialog title of the assign keybinding dialog, as suggested in https://github.com/geany/geany/issues/4185
Closes #2848. You can view, comment on, or merge this pull request online at:
https://github.com/geany/geany/pull/4192
-- Commit Summary --
* Move UTF-8 capable sub string matching from plugin manager to utils * Preferences: add filter entry for keybindings * Plugin Manager: Bind Ctrl-F shortcut to focus the filter entry * Preferences: Retitle assign keybinding dialog title
-- File Changes --
M data/geany.glade (18) M src/plugins.c (56) M src/prefs.c (81) M src/utils.c (48) M src/utils.h (2)
-- Patch Links --
https://github.com/geany/geany/pull/4192.patch https://github.com/geany/geany/pull/4192.diff
@eht16 commented on this pull request.
GtkTreeViewColumn *column;
kbdata->tree = GTK_TREE_VIEW(ui_lookup_widget(ui_widgets.prefs_dialog, "treeview7"));
kbdata->store = gtk_tree_store_new(KB_TREE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INT); - gtk_tree_view_set_model(GTK_TREE_VIEW(kbdata->tree), GTK_TREE_MODEL(kbdata->store)); + filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(kbdata->store), NULL); + gtk_tree_model_filter_set_visible_func( + GTK_TREE_MODEL_FILTER(filter_model), kb_tree_filter_func, kb_filter_entry, NULL); + /* set model to tree view */ + gtk_tree_view_set_model(GTK_TREE_VIEW(kbdata->tree), filter_model); + g_object_unref(filter_model);
Is it correct to unref both objects? It seems to work this way but I'm never sure with unref'ing.
@eht16 pushed 4 commits.
54551a2879e61fc43b59029406a8684f85df69db Move UTF-8 capable sub string matching from plugin manager to utils 140dca74b6a8e7a0ec1674ecfc497fcb3ff81f0f Preferences: add filter entry for keybindings cdedd6ee3fc0a2fb97227e1b9122f92ed82681db Plugin Manager: Bind Ctrl-F shortcut to focus the filter entry 4708f66edf695a8301c00bc129e3d11919eebacc Preferences: Retitle assign keybinding dialog title
Yes!!! I've always wanted this, just didn't get to actually implementing it. (Not tested or reviewed yet, just a big conceptual thumbs up.)
---
While you remember what you are doing in terms of filtering, I think there are also open issues regarding filtering the open documents tab which I think would also be useful too (I don't use it that much myself though).
Also a good opportunity to finally test the LSP plugin :-P.
Setting it up for Geany development is really trivial, just 1. `sudo apt install clangd` 2. `meson setup build` in Geany source directory (so `compile_commands.json` is in the build directory, you don't actually have to build Geany using meson afterwards and can use autotools if you prefer them) 3. Points 2-5 from https://github.com/techee/geany-lsp?tab=readme-ov-file#quick-start
(I still have a feeling that the word "server" scares off many people as everyone imagines some complicated configuration like setting up Apache or whatever and in fact, you typically don't have to set up anything, it's pre-configured in the plugin for most common servers, and the plugin starts/terminates the server for you.)
While you remember what you are doing in terms of filtering, I think there are also open issues regarding filtering the open documents tab which
I might have a look.
(I don't use it that much myself though).
Me neither :(.
Also a good opportunity to finally test the LSP plugin :-P.
Setting it up for Geany development is really trivial, just
1. `sudo apt install clangd` 2. `meson setup build` in Geany source directory (so `compile_commands.json` is in the build directory, you don't actually have to build Geany using meson afterwards and can use autotools if you prefer them) 3. Points 2-5 from https://github.com/techee/geany-lsp?tab=readme-ov-file#quick-start
Thanks, I really should have a look. It'll probably take only a few more months or so :).
Thanks, I really should have a look. It'll probably take only a few more months or so :).
Just saying :-). If nothing else, seeing syntax errors underlined in the editor in the real time without the need to perform actual compilation is a big productivity boost for me.
@eht16 I've just tested the PR briefly but I didn't like that parents not containing any children were still displayed - when filtering for something in the last group, one would have to scroll the tree to see it for the empty groups above.
I think it's hard/impossible to achieve that with the filter function but I tried adding an extra column to the store indicating visibility and then using `gtk_tree_model_filter_set_visible_column()` and it seems to work. I also added `expand_all()` to the result because the collapsed entries weren't very nice.
The patch is below (created very quickly by reusing code from `kb_update()` for the update function, possibly needs improving).
What do you think?
```diff diff --git a/src/prefs.c b/src/prefs.c index 57e95837e..7b8129089 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -87,7 +87,6 @@ static gboolean kb_find_duplicate(GtkTreeStore *store, GtkWidget *parent, GtkTre static void kb_filter_entry_changed_cb(GtkEntry *entry, gpointer user_data); static void kb_filter_entry_icon_release_cb(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data); -static gboolean kb_tree_filter_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data); static gboolean kb_tree_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static void on_toolbar_show_toggled(GtkToggleButton *togglebutton, gpointer user_data); static void on_show_notebook_tabs_toggled(GtkToggleButton *togglebutton, gpointer user_data); @@ -146,6 +145,7 @@ enum KB_TREE_INDEX, KB_TREE_EDITABLE, KB_TREE_WEIGHT, + KB_TREE_VISIBLE, KB_TREE_COLUMNS };
@@ -276,10 +276,9 @@ static void kb_init_tree(KbData *kbdata, GtkWidget *kb_filter_entry) kbdata->tree = GTK_TREE_VIEW(ui_lookup_widget(ui_widgets.prefs_dialog, "treeview7"));
kbdata->store = gtk_tree_store_new(KB_TREE_COLUMNS, - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INT); + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_BOOLEAN); filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(kbdata->store), NULL); - gtk_tree_model_filter_set_visible_func( - GTK_TREE_MODEL_FILTER(filter_model), kb_tree_filter_func, kb_filter_entry, NULL); + gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(filter_model), KB_TREE_VISIBLE); /* set model to tree view */ gtk_tree_view_set_model(GTK_TREE_VIEW(kbdata->tree), filter_model); g_object_unref(filter_model); @@ -376,14 +375,14 @@ static void kb_init(KbData *kbdata, GtkWidget *kb_filter_entry) { gtk_tree_store_append(kbdata->store, &parent, NULL); gtk_tree_store_set(kbdata->store, &parent, KB_TREE_ACTION, group->label, - KB_TREE_INDEX, g, -1); + KB_TREE_INDEX, g, KB_TREE_VISIBLE, TRUE, -1);
foreach_ptr_array(kb, i, group->key_items) { label = keybindings_get_label(kb); gtk_tree_store_append(kbdata->store, &iter, &parent); gtk_tree_store_set(kbdata->store, &iter, KB_TREE_ACTION, label, - KB_TREE_EDITABLE, TRUE, KB_TREE_INDEX, kb->id, -1); + KB_TREE_EDITABLE, TRUE, KB_TREE_INDEX, kb->id, KB_TREE_VISIBLE, TRUE, -1); kb_set_shortcut(kbdata->store, &iter, kb->key, kb->mods); g_free(label); } @@ -1413,10 +1412,62 @@ static void kb_cell_edited_cb(GtkCellRendererText *cellrenderertext, }
+static void update_visibility(GtkTreeStore *store, const gchar *entry_text) +{ + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreeIter child, parent; + + /* get first parent */ + if (! gtk_tree_model_iter_children(model, &parent, NULL)) + return; + + /* foreach parent */ + while (TRUE) + { + gboolean visible_children = FALSE; + + /* get first child */ + if (! gtk_tree_model_iter_children(model, &child, &parent)) + return; + + /* foreach child */ + while (TRUE) + { + gboolean visible; + gchar *action_name; + + gtk_tree_model_get(model, &child, KB_TREE_ACTION, &action_name, -1); + if (!action_name || EMPTY(entry_text)) + visible = TRUE; + else + visible = utils_utf8_substring_match(entry_text, action_name); + g_free(action_name); + + if (visible) + visible_children = TRUE; + + gtk_tree_store_set(store, &child, KB_TREE_VISIBLE, visible, -1); + + if (! gtk_tree_model_iter_next(model, &child)) + break; + } + + gtk_tree_store_set(store, &parent, KB_TREE_VISIBLE, visible_children, -1); + + if (! gtk_tree_model_iter_next(model, &parent)) + return; + } +} + + static void kb_filter_entry_changed_cb(GtkEntry *entry, gpointer user_data) { GtkTreeModel *filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(global_kb_data.tree)); + const gchar *entry_text = gtk_entry_get_text(entry); + + update_visibility(global_kb_data.store, entry_text); gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(filter_model)); + gtk_tree_view_expand_all(global_kb_data.tree); }
@@ -1428,31 +1479,6 @@ static void kb_filter_entry_icon_release_cb(GtkEntry *entry, GtkEntryIconPositio }
-static gboolean kb_tree_filter_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) -{ - const gchar *key; - gboolean matched; - gchar *action_name; - GtkWidget *kb_filter_entry = user_data; - - // always show categories - if (gtk_tree_model_iter_has_child(model, iter)) - return TRUE; - - gtk_tree_model_get(model, iter, KB_TREE_ACTION, &action_name, -1); - if (!action_name) - return TRUE; - - key = gtk_entry_get_text(GTK_ENTRY(kb_filter_entry)); - if (EMPTY(key)) - return TRUE; - - matched = utils_utf8_substring_match(key, action_name); - g_free(action_name); - return matched; -} - - static gboolean kb_tree_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { if (event->keyval == GDK_KEY_f && (event->state & GEANY_PRIMARY_MOD_MASK)) ```
github-comments@lists.geany.org