customizing completion of GtkComboBoxText

前端 未结 2 783
甜味超标
甜味超标 2020-12-20 14:00

How can I customize the completion of a GtkComboBoxText with both a \"static\" aspect and a \"dynamic\" one? The static aspect is because some entries are known and added to

2条回答
  •  旧时难觅i
    2020-12-20 14:50

    Here is my suggestion:

    Use a GtkListStore to contain a list of GTK-managed strings (essentially, copies of your identifier string) that match the current prefix string.

    (As documented for gtk_list_store_set(), a G_TYPE_STRING item is copied. I consider the overhead of the extra copy acceptable here; it should not affect real-world performance much anyway, I think, and in return, GTK+ will manage the reference counting for us.)

    The above is implemented in a GTK+ callback function, which gets an extra pointer as payload (set at the time the GUI is created or activated; I suggest you use some structure to keep references you need to generate the matches). The callback is connected to the combobox popup signal, so that it gets called whenever the list is expanded.

    Note that as B8vrede noted in a comment, a GtkComboBoxText should not be modified via its model; that is why one should/must use a GtkComboBox instead.

    Practical example

    For simplicity, let's assume all the data you need to find or generate all known identifiers matched against is held in a structure, say

    struct generator {
        /* Whatever data you need to generate prefix matches */
    };
    

    and the combo box populator helper function is then something like

    static void combo_box_populator(GtkComboBox *combobox, gpointer genptr)
    {
        struct generator *const generator = genptr;
    
        GtkListStore *combo_list = GTK_LIST_STORE(gtk_combo_box_get_model(combobox));
    
        GtkWidget *entry = gtk_bin_get_child(GTK_BIN(combobox));
        const char *prefix = gtk_entry_get_text(GTK_ENTRY(entry));
        const size_t prefix_len = (prefix) ? strlen(prefix) : 0;
    
        GtkTreeIter iterator;
    
        /* Clear the current store */
        gtk_list_store_clear(combo_list);
    
        /* Initialize the list iterator */
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(combo_list), &iterator);
    
        /* Find all you want to have in the combo box;
           for each  const char *match, do:
        */
    
            gtk_list_store_append(combo_list, &iterator);
            gtk_list_store_set(combo_list, &iterator, 0, match, -1);
    
        /* Note that the string pointed to by match is copied;
           match is not referred to after the _set() returns.
        */
    }
    

    When the UI is built or activated, you need to ensure the GtkComboBox has an entry (so the user can write text into it), and a GtkListStore model:

        struct generator *generator;
        GtkWidget *combobox;
        GtkListStore *combo_list;
    
        combo_list = gtk_list_store_new(1, G_TYPE_STRING);
        combobox = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(combo_list));
        gtk_combo_box_set_id_column(GTK_COMBO_BOX(combobox), 0);
        gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combobox), 0);
        gtk_combo_box_set_button_sensitivity(GTK_COMBO_BOX(combobox), GTK_SENSITIVITY_ON);
    
        g_signal_connect(combobox, "popup", G_CALLBACK(combo_box_populator), generator);
    

    On my system, the default pop-up accelerator is Alt+Down, but I assume you've already changed that to Tab.

    I have a crude working example here (a .tar.xz tarball, CC0): it reads lines from standard input, and lists the ones matching the user prefix in reverse order in the combo box list (when popped-up). If the entry is empty, the combobox will contain all input lines. I didn't change the default accelerators, so instead of Tab, try Alt+Down.

    I also have the same example, but using GtkComboBoxText instead, here (also CC0). This does not use a GtkListStore model, but uses gtk_combo_box_text_remove_all() and gtk_combo_box_text_append_text() functions to manipulate the list contents directly. (There is just a few different lines in the two examples.) Unfortunately, the documentation is not explicit whether this interface references or copies the strings. Although copying is the only option that makes sense, and this can be verified from the current Gtk+ sources, the lack of explicit documentation makes me hesitant.

    Comparing the two examples I linked to above (both grab some 500 random words from /usr/share/dict/words if you compile and run it with make), I don't see any speed difference. Both use the same naïve way of picking prefix matches from a linked list, which means the two methods (GtkComboBox + model, or GtkComboBoxText) should be about equally fast.

    On my own machine, both get annoyingly slow with more than 1000 or so matches in the popup; with just a hundred or less matches, it feels instantaneous. This, to me, indicates that the slow/naïve way of picking prefix matches from a linked list is not the culprit (because the entire list is traversed in both cases), but that the GTK+ combo boxes are just not designed for large lists. (The slowdown is definitely much, much worse than linear.)

提交回复
热议问题