Search code examples
cgtk3gtktreeview

Gtk Tree View columns size to view


I'm a newbie to Gtk3 but I can't find how to do what I want anywhere. I'm trying to get a tree view representing article citations.

Desired outcome:

  • columns are resizable
  • no horizontal scrollbar
  • columns take only as much space as needed, they don't expand the window if content is too long
  • in case a cell's content is too long, either break the text over several lines (best) or trim it and add "..." (ok), just trim it (I could settle for that)

As an example of what I want, here's Zotero trimming columns with "..."

Zotero can do it, why not me?

This is my widget hierarchy:

Widget hierarchy

What I tried (using Glade + C):

  1. Default settings: a horizontal scrollbar appears
  2. Set ScrolledWindow horizontal scroll policy to "Never", auto sizing to column: window expands to fit content, e.g. A tragically wide window
  3. Set ScrolledWindow horizontal scroll policy to "Never", fixed sizing columns, with maximum width: contents is trimmed (not "...", no text reflow), but I can only resize columns to the arbiratry size I've given them.
  4. Set ScrolledWindow horizontal scroll policy to "External", auto sizing columns, columns set to resizable: columns are not resizable, contents is not trimmed.

Here's variation 4 with source + XML file generated by Glade:

#include <gtk/gtk.h>

void on_window_main_destroy();

enum {
    AUTHORS_COLUMN = 0,
    TITLE_COLUMN,
    N_COLUMNS
};

static void init_reference_list(GtkBuilder* builder)
{
    GtkCellRenderer*   renderer;
    GtkWidget*         tree_widget;
    tree_widget = GTK_WIDGET(gtk_builder_get_object(builder, "tree_view"));

    // -- Add attributes to Glade-generated columns
    GtkTreeViewColumn* column;
    renderer = gtk_cell_renderer_text_new();
    column = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "title"));
    gtk_tree_view_column_pack_start(column, renderer, FALSE);
    gtk_tree_view_column_add_attribute(column, renderer, "text", TITLE_COLUMN ); 
    column = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "authors"));
    gtk_tree_view_column_pack_start(column, renderer, FALSE);
    gtk_tree_view_column_add_attribute(column, renderer, "text", AUTHORS_COLUMN );
    // -- Set up store and connect it to view
    GtkListStore* store;
    store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING ); 
    gtk_tree_view_set_model(GTK_TREE_VIEW(tree_widget), GTK_TREE_MODEL(store));


    // -- Placing fake data in store
    char title[]   =  "A long long long long long long long long long long long long long long long long long long long long long long long long  long long long long long long long long long long title";
    char authors[] =  "Me You Us Them All of us";
    GtkTreeIter iter;
    int i;
    for (int i = 0; i < 500; ++i)
    {
        gtk_list_store_append(store, &iter);
        gtk_list_store_set(store, &iter, AUTHORS_COLUMN, authors, TITLE_COLUMN,   title, -1 );
    }

}

// called when window is closed
void on_window_main_destroy()
{
    gtk_main_quit();
}


int main(int argc, char *argv[])
{
        gtk_init(&argc, &argv);

        // -- load builder
        GtkBuilder* builder; 
        builder = gtk_builder_new();
        gtk_builder_add_from_file (builder, "resources/ui/ref_search.glade", NULL);
        gtk_builder_connect_signals(builder, NULL);

        // -- Cell Renderer and fake data
        init_reference_list(builder);

        // -- Launch
        GtkWidget         *window;
        window  = GTK_WIDGET(gtk_builder_get_object(builder, "window_main"));
        gtk_widget_show(window);     
        g_object_unref(builder);
        gtk_main();

        return 0;
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkEntryBuffer" id="search_buffer"/>
  <object class="GtkWindow" id="window_main">
    <property name="can_focus">False</property>
    <property name="halign">baseline</property>
    <property name="valign">baseline</property>
    <property name="title" translatable="yes">Reference Manager</property>
    <property name="resizable">False</property>
    <property name="default_width">640</property>
    <property name="default_height">480</property>
    <signal name="destroy" handler="on_window_main_destroy" swapped="no"/>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkSearchEntry" id="search_bar">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="buffer">search_buffer</property>
            <property name="primary_icon_name">edit-find-symbolic</property>
            <property name="primary_icon_activatable">False</property>
            <property name="primary_icon_sensitive">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hscrollbar_policy">external</property>
            <property name="shadow_type">in</property>
            <property name="max_content_height">500</property>
            <child>
              <object class="GtkTreeView" id="tree_view">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="rules_hint">True</property>
                <property name="enable_search">False</property>
                <property name="show_expanders">False</property>
                <property name="enable_grid_lines">horizontal</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection"/>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="authors">
                    <property name="resizable">True</property>
                    <property name="sizing">autosize</property>
                    <property name="title" translatable="yes">Author(s)</property>
                    <property name="expand">True</property>
                    <property name="clickable">True</property>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="title">
                    <property name="resizable">True</property>
                    <property name="sizing">autosize</property>
                    <property name="title" translatable="yes">Title</property>
                    <property name="expand">True</property>
                    <property name="clickable">True</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Apologies if I'm missing the obvious!


Solution

  • This might be closer to what you are looking for in the Glade file:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.2 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkListStore" id="liststore1">
        <columns>
          <!-- column-name authors -->
          <column type="gchararray"/>
          <!-- column-name titles -->
          <column type="gchararray"/>
        </columns>
        <data>
          <row>
            <col id="0" translatable="yes">Me You Us Them All of us</col>
            <col id="1" translatable="yes">A long long long long long long long long long long long long long long long long long long long long long long long long  long long long long long long long long long long title</col>
          </row>
        </data>
      </object>
      <object class="GtkEntryBuffer" id="search_buffer"/>
      <object class="GtkWindow" id="window_main">
        <property name="can_focus">False</property>
        <property name="halign">baseline</property>
        <property name="valign">baseline</property>
        <property name="title" translatable="yes">Reference Manager</property>
        <property name="resizable">False</property>
        <property name="default_width">640</property>
        <property name="default_height">480</property>
        <signal name="destroy" handler="on_window_main_destroy" swapped="no"/>
        <child type="titlebar">
          <placeholder/>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchEntry" id="search_bar">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="buffer">search_buffer</property>
                <property name="primary_icon_name">edit-find-symbolic</property>
                <property name="primary_icon_activatable">False</property>
                <property name="primary_icon_sensitive">False</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hscrollbar_policy">external</property>
                <property name="shadow_type">in</property>
                <property name="max_content_height">500</property>
                <child>
                  <object class="GtkTreeView" id="tree_view">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">liststore1</property>
                    <property name="rules_hint">True</property>
                    <property name="enable_search">False</property>
                    <property name="show_expanders">False</property>
                    <property name="enable_grid_lines">horizontal</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"/>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn" id="authors">
                        <property name="resizable">True</property>
                        <property name="sizing">fixed</property>
                        <property name="title" translatable="yes">Author(s)</property>
                        <property name="expand">True</property>
                        <property name="clickable">True</property>
                        <child>
                          <object class="GtkCellRendererText">
                            <property name="ellipsize">middle</property>
                          </object>
                          <attributes>
                            <attribute name="text">0</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn" id="title">
                        <property name="resizable">True</property>
                        <property name="sizing">fixed</property>
                        <property name="title" translatable="yes">Title</property>
                        <property name="expand">True</property>
                        <property name="clickable">True</property>
                        <child>
                          <object class="GtkCellRendererText">
                            <property name="ellipsize">middle</property>
                          </object>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
        </child>
      </object>
    </interface>
    

    Edit with another attempt:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.1 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkListStore" id="liststore1">
        <columns>
          <!-- column-name authors -->
          <column type="gchararray"/>
          <!-- column-name titles -->
          <column type="gchararray"/>
        </columns>
        <data>
          <row>
            <col id="0" translatable="yes">Me You Us Them All of us</col>
            <col id="1" translatable="yes">A long long long long long long long long long long long long long long long long long long long long long long long long  long long long long long long long long long long title</col>
          </row>
        </data>
      </object>
      <object class="GtkEntryBuffer" id="search_buffer"/>
      <object class="GtkWindow" id="window_main">
        <property name="width_request">250</property>
        <property name="can_focus">False</property>
        <property name="title" translatable="yes">Reference Manager</property>
        <property name="resizable">False</property>
        <property name="default_width">640</property>
        <property name="default_height">480</property>
        <signal name="destroy" handler="on_window_main_destroy" swapped="no"/>
        <child type="titlebar">
          <placeholder/>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkSearchEntry" id="search_bar">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="buffer">search_buffer</property>
                <property name="primary_icon_name">edit-find-symbolic</property>
                <property name="primary_icon_activatable">False</property>
                <property name="primary_icon_sensitive">False</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hscrollbar_policy">never</property>
                <property name="shadow_type">in</property>
                <property name="max_content_height">500</property>
                <child>
                  <object class="GtkTreeView" id="tree_view">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">liststore1</property>
                    <property name="rules_hint">True</property>
                    <property name="enable_search">False</property>
                    <property name="show_expanders">False</property>
                    <property name="enable_grid_lines">horizontal</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"/>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn" id="authors">
                        <property name="resizable">True</property>
                        <property name="sizing">fixed</property>
                        <property name="min_width">25</property>
                        <property name="title" translatable="yes">Author(s)</property>
                        <property name="expand">True</property>
                        <property name="clickable">True</property>
                        <child>
                          <object class="GtkCellRendererText">
                            <property name="ellipsize">middle</property>
                          </object>
                          <attributes>
                            <attribute name="text">0</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                    <child>
                      <object class="GtkTreeViewColumn" id="title">
                        <property name="resizable">True</property>
                        <property name="min_width">25</property>
                        <property name="title" translatable="yes">Title</property>
                        <property name="expand">True</property>
                        <property name="clickable">True</property>
                        <child>
                          <object class="GtkCellRendererText">
                            <property name="ellipsize">middle</property>
                          </object>
                          <attributes>
                            <attribute name="text">1</attribute>
                          </attributes>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
        </child>
      </object>
    </interface>