diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 9e12315a03..6629c184ec 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -178,7 +178,7 @@ Multiline Text Editor - + @@ -189,7 +189,7 @@ Tree, List and Icon Grid Widgets - + diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index cdeaeec609..e96c7b6fee 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -352,15 +352,11 @@ content_files = [ 'gtk4-widget-factory.xml', 'overview.xml', 'question_index.xml', - 'text_widget.xml', - 'tree_widget.xml', 'visual_index.xml', ] expand_content_files = [ 'question_index.xml', - 'text_widget.xml', - 'tree_widget.xml', ] expand_content_md_files = [ @@ -368,7 +364,7 @@ expand_content_md_files = [ 'osx.md', 'wayland.md', 'windows.md', -: 'x11.md', + 'x11.md', 'getting_started.md', 'resources.md', 'building.md', @@ -380,7 +376,9 @@ expand_content_md_files = [ 'input-handling.md', 'drawing-model.md', 'css-overview.md', - 'css-properties.md' + 'css-properties.md', + 'section-text-widget.md', + 'section-tree-widget.md', ] types_conf = configuration_data() diff --git a/docs/reference/gtk/section-text-widget.md b/docs/reference/gtk/section-text-widget.md new file mode 100644 index 0000000000..4184b88878 --- /dev/null +++ b/docs/reference/gtk/section-text-widget.md @@ -0,0 +1,157 @@ +# Text Widget Overview {#TextWidget} + +GTK has an extremely powerful framework for multiline text editing. The +primary objects involved in the process are #GtkTextBuffer, which represents the +text being edited, and #GtkTextView, a widget which can display a #GtkTextBuffer. +Each buffer can be displayed by any number of views. + +One of the important things to remember about text in GTK is that it's in +the UTF-8 encoding. This means that one character can be encoded as multiple +bytes. Character counts are usually referred to as _offsets_, while byte +counts are called _indexes_. If you confuse these two, things will work fine +with ASCII, but as soon as your buffer contains multibyte characters, bad +things will happen. + +Text in a buffer can be marked with _tags_. A tag is an attribute that can +be applied to some range of text. For example, a tag might be called "bold" +and make the text inside the tag bold. However, the tag concept is more +general than that; tags don't have to affect appearance. They can instead +affect the behavior of mouse and key presses, "lock" a range of text so the +user can't edit it, or countless other things. A tag is represented by a +#GtkTextTag object. One #GtkTextTag can be applied to any number of text +ranges in any number of buffers. + +Each tag is stored in a #GtkTextTagTable. A tag table defines a set of +tags that can be used together. Each buffer has one tag table associated with +it; only tags from that tag table can be used with the buffer. A single tag +table can be shared between multiple buffers, however. + +Tags can have names, which is convenient sometimes (for example, you can name +your tag that makes things bold "bold"), but they can also be anonymous (which +is convenient if you're creating tags on-the-fly). + +Most text manipulation is accomplished with _iterators_, represented by a +#GtkTextIter. An iterator represents a position between two characters in +the text buffer. #GtkTextIter is a struct designed to be allocated on the +stack; it's guaranteed to be copiable by value and never contain any +heap-allocated data. Iterators are not valid indefinitely; whenever the +buffer is modified in a way that affects the number of characters in the +buffer, all outstanding iterators become invalid. (Note that deleting 5 +characters and then reinserting 5 still invalidates iterators, though you +end up with the same number of characters you pass through a state with a +different number). + +Because of this, iterators can't be used to preserve positions across buffer +modifications. To preserve a position, the #GtkTextMark object is ideal. You +can think of a mark as an invisible cursor or insertion point; it floats in +the buffer, saving a position. If the text surrounding the mark is deleted, +the mark remains in the position the text once occupied; if text is inserted +at the mark, the mark ends up either to the left or to the right of the new +text, depending on its _gravity_. The standard text cursor in left-to-right +languages is a mark with right gravity, because it stays to the right of +inserted text. + +Like tags, marks can be either named or anonymous. There are two marks +built-in to #GtkTextBuffer; these are named "insert" and "selection_bound" +and refer to the insertion point and the boundary of the selection which +is not the insertion point, respectively. If no text is selected, these +two marks will be in the same position. You can manipulate what is selected +and where the cursor appears by moving these marks around. + +If you want to place the cursor in response to a user action, be sure to use +gtk_text_buffer_place_cursor(), which moves both at once without causing a +temporary selection (moving one then the other temporarily selects the range in +between the old and new positions). + +Text buffers always contain at least one line, but may be empty (that +is, buffers can contain zero characters). The last line in the text +buffer never ends in a line separator (such as newline); the other +lines in the buffer always end in a line separator. Line separators +count as characters when computing character counts and character +offsets. Note that some Unicode line separators are represented with +multiple bytes in UTF-8, and the two-character sequence "\r\n" is also +considered a line separator. + +Text buffers support undo and redo if gtk_text_buffer_set_enable_undo() +has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo() +to perform the necessary action. Note that these operations are ignored if +the buffer is not editable. Developers may want some operations to not be +undoable. To do this, wrap your changes in +gtk_text_buffer_begin_irreversible_action() and +gtk_text_buffer_end_irreversible_action(). + +## Simple Example + +The simplest usage of #GtkTextView might look like this: + +``` {.c} +GtkWidget *view; +GtkTextBuffer *buffer; + +view = gtk_text_view_new (); + +buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + +gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); + +/* Now you might put the view in a container and display it on the + * screen; when the user edits the text, signals on the buffer + * will be emitted, such as "changed", "insert_text", and so on. + */ +``` + +In many cases it's also convenient to first create the buffer with +gtk_text_buffer_new(), then create a widget for that buffer with +gtk_text_view_new_with_buffer(). Or you can change the buffer the widget +displays after the widget is created with gtk_text_view_set_buffer(). + +## Example of Changing Text Attributes + +The way to affect text attributes in #GtkTextView is to +apply tags that change the attributes for a region of text. +For text features that come from the theme — such as font and +foreground color -- use CSS to override their default values. + +``` +GtkWidget *view; +GtkTextBuffer *buffer; +GtkTextIter start, end; +PangoFontDescription *font_desc; +GdkRGBA rgba; +GtkTextTag *tag; +GtkCssProvider *provider; +GtkStyleContext *context; + +view = gtk_text_view_new (); + +buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + +gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); + +/* Change default font and color throughout the widget */ +provider = gtk_css_provider_new (); +gtk_css_provider_load_from_data (provider, + "textview {" + " font: 15 serif;" + " color: green;" + "}", + -1); +context = gtk_widget_get_style_context (view); +gtk_style_context_add_provider (context, + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + +/* Change left margin throughout the widget */ +gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30); + +/* Use a tag to change the color for just one part of the widget */ +tag = gtk_text_buffer_create_tag (buffer, "blue_foreground", + "foreground", "blue", + NULL); +gtk_text_buffer_get_iter_at_offset (buffer, &start, 7); +gtk_text_buffer_get_iter_at_offset (buffer, &end, 12); +gtk_text_buffer_apply_tag (buffer, tag, &start, &end); +``` + +The `gtk4-demo` application that comes with +GTK contains more example code for #GtkTextView. diff --git a/docs/reference/gtk/section-tree-widget.md b/docs/reference/gtk/section-tree-widget.md new file mode 100644 index 0000000000..b51cd3fb7a --- /dev/null +++ b/docs/reference/gtk/section-tree-widget.md @@ -0,0 +1,282 @@ +# Tree and List Widget Overview {#TreeWidget} + +To create a tree or list in GTK, use the #GtkTreeModel interface in +conjunction with the #GtkTreeView widget. This widget is designed around +a _Model/View/Controller_ design and consists of four major parts: + +- The tree view widget (GtkTreeView) +- The view column (GtkTreeViewColumn) +- The cell renderers (GtkCellRenderer etc.) +- The model interface (GtkTreeModel) + +The _View_ is composed of the first three objects, while the last is the +_Model_. One of the prime benefits of the MVC design is that multiple views +can be created of a single model. For example, a model mapping the file +system could be created for a file manager. Many views could be created +to display various parts of the file system, but only one copy need be +kept in memory. + +The purpose of the cell renderers is to provide extensibility to the +widget and to allow multiple ways of rendering the same type of data. +For example, consider how to render a boolean variable. Should it +render it as a string of "True" or "False", "On" or "Off", or should +it be rendered as a checkbox? + +## Creating a model + +GTK provides two simple models that can be used: the #GtkListStore +and the #GtkTreeStore. GtkListStore is used to model list widgets, +while the GtkTreeStore models trees. It is possible to develop a new +type of model, but the existing models should be satisfactory for all +but the most specialized of situations. Creating the model is quite + +``` {.c} +GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); +``` + +This creates a list store with two columns: a string column and a boolean +column. Typically the 2 is never passed directly like that; usually an +enum is created wherein the different columns are enumerated, followed by +a token that represents the total number of columns. The next example will +illustrate this, only using a tree store instead of a list store. Creating +a tree store operates almost exactly the same. + +``` {.c} +enum +{ + TITLE_COLUMN, + AUTHOR_COLUMN, + CHECKED_COLUMN, + N_COLUMNS +}; + +GtkTreeStore *store = gtk_tree_store_new (N_COLUMNS, /* Total number of columns */ + G_TYPE_STRING, /* Book title */ + G_TYPE_STRING, /* Author */ + G_TYPE_BOOLEAN); /* Is checked out? */ +``` + +Adding data to the model is done using gtk_tree_store_set() or +gtk_list_store_set(), depending upon which sort of model was +created. To do this, a #GtkTreeIter must be acquired. The iterator +points to the location where data will be added. + +Once an iterator has been acquired, gtk_tree_store_set() is used to +apply data to the part of the model that the iterator points to. +Consider the following example: + +``` {.c} +GtkTreeIter iter; + +gtk_tree_store_append (store, &iter, NULL); /* Acquire an iterator */ + +gtk_tree_store_set (store, &iter, + TITLE_COLUMN, "The Principle of Reason", + AUTHOR_COLUMN, "Martin Heidegger", + CHECKED_COLUMN, FALSE, + -1); +``` + +Notice that the last argument is -1. This is always done because +this is a variable-argument function and it needs to know when to stop +processing arguments. It can be used to set the data in any or all +columns in a given row. + +The third argument to gtk_tree_store_append() is the parent iterator. +It is used to add a row to a GtkTreeStore as a child of an existing row. +This means that the new row will only be visible when its parent is visible +and in its expanded state. Consider the following example: + +``` {.c} +GtkTreeIter iter1; /* Parent iter */ +GtkTreeIter iter2; /* Child iter */ + +gtk_tree_store_append (store, &iter1, NULL); /* Acquire a top-level iterator */ +gtk_tree_store_set (store, &iter1, + TITLE_COLUMN, "The Art of Computer Programming", + AUTHOR_COLUMN, "Donald E. Knuth", + CHECKED_COLUMN, FALSE, + -1); + +gtk_tree_store_append (store, &iter2, &iter1); /* Acquire a child iterator */ +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 1: Fundamental Algorithms", + -1); + +gtk_tree_store_append (store, &iter2, &iter1); +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 2: Seminumerical Algorithms", + -1); + +gtk_tree_store_append (store, &iter2, &iter1); +gtk_tree_store_set (store, &iter2, + TITLE_COLUMN, "Volume 3: Sorting and Searching", + -1); +``` + +## Creating the view component + +While there are several different models to choose from, there is +only one view widget to deal with. It works with either the list +or the tree store. Setting up a #GtkTreeView is not a difficult +matter. It needs a #GtkTreeModel to know where to retrieve its data +from. + +``` {.c} +GtkWidget *tree; + +tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); +``` + +## Colums and cell renderers + +Once the #GtkTreeView widget has a model, it will need to know how +to display the model. It does this with columns and cell renderers. + +Cell renderers are used to draw the data in the tree model in a +way. There are a number of cell renderers that come with GTK, +including the #GtkCellRendererText, #GtkCellRendererPixbuf and +the #GtkCellRendererToggle. It is relatively easy to write a +custom renderer. + +A #GtkTreeViewColumn is the object that GtkTreeView uses to organize +the vertical columns in the tree view. It needs to know the name of +the column to label for the user, what type of cell renderer to use, +and which piece of data to retrieve from the model for a given row. + +``` {.c} +GtkCellRenderer *renderer; +GtkTreeViewColumn *column; + +renderer = gtk_cell_renderer_text_new (); +column = gtk_tree_view_column_new_with_attributes ("Author", + renderer, + "text", AUTHOR_COLUMN, + NULL); +gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); +``` + +At this point, all the steps in creating a displayable tree have been +covered. The model is created, data is stored in it, a tree view is +created and columns are added to it. + +## Selection handling + +Most applications will need to not only deal with displaying data, +but also receiving input events from users. To do this, simply get +a reference to a selection object and connect to the +#GtkTreeSelection::changed signal. + +``` {.c} +/* Prototype for selection handler callback */ +static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data); + +/* Setup the selection handler */ +GtkTreeSelection *select; + +select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); +gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); +g_signal_connect (G_OBJECT (select), "changed", + G_CALLBACK (tree_selection_changed_cb), + NULL); +``` + +Then to retrieve data for the row selected: + +``` {.c} +static void +tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + gchar *author; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, AUTHOR_COLUMN, &author, -1); + + g_print ("You selected a book by %s\n", author); + + g_free (author); + } +} +``` + +## Simple Example + +Here is a simple example of using a #GtkTreeView widget in context +of the other widgets. It simply creates a simple model and view, +and puts them together. Note that the model is never populated +with data — that is left as an exercise for the reader. +More information can be found on this in the #GtkTreeModel section. + +``` {.c} +enum +{ + TITLE_COLUMN, + AUTHOR_COLUMN, + CHECKED_COLUMN, + N_COLUMNS +}; + +void +setup_tree (void) +{ + GtkTreeStore *store; + GtkWidget *tree; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + /* Create a model. We are using the store model for now, though we + * could use any other GtkTreeModel */ + store = gtk_tree_store_new (N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + /* custom function to fill the model with data */ + populate_tree_model (store); + + /* Create a view */ + tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + + /* The view now holds a reference. We can get rid of our own + * reference */ + g_object_unref (G_OBJECT (store)); + + /* Create a cell render and arbitrarily make it red for demonstration + * purposes */ + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), + "foreground", "red", + NULL); + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + column = gtk_tree_view_column_new_with_attributes ("Author", renderer, + "text", AUTHOR_COLUMN, + NULL); + + /* Add the column to the view. */ + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Second column.. title of the book. */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Title", + renderer, + "text", TITLE_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Last column.. whether a book is checked out. */ + renderer = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes ("Checked out", + renderer, + "active", CHECKED_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); + + /* Now we can manipulate the view just like any other GTK widget */ + ... +} +``` diff --git a/docs/reference/gtk/text_widget.xml b/docs/reference/gtk/text_widget.xml deleted file mode 100644 index bea104ac9d..0000000000 --- a/docs/reference/gtk/text_widget.xml +++ /dev/null @@ -1,215 +0,0 @@ - - - - -Text Widget Overview -3 -GTK Library - - - -Text Widget Overview -Overview of GtkTextBuffer, GtkTextView, and friends - - - -Conceptual Overview - - -GTK has an extremely powerful framework for multiline text editing. The -primary objects involved in the process are #GtkTextBuffer, which represents the -text being edited, and #GtkTextView, a widget which can display a #GtkTextBuffer. -Each buffer can be displayed by any number of views. - - - -One of the important things to remember about text in GTK is that it's in the -UTF-8 encoding. This means that one character can be encoded as multiple -bytes. Character counts are usually referred to as -offsets, while byte counts are called -indexes. If you confuse these two, things will work fine -with ASCII, but as soon as your buffer contains multibyte characters, bad -things will happen. - - - -Text in a buffer can be marked with tags. A tag is an -attribute that can be applied to some range of text. For example, a tag might -be called "bold" and make the text inside the tag bold. However, the tag -concept is more general than that; tags don't have to affect appearance. They -can instead affect the behavior of mouse and key presses, "lock" a range of -text so the user can't edit it, or countless other things. A tag is -represented by a #GtkTextTag object. One #GtkTextTag can be applied to any -number of text ranges in any number of buffers. - - - -Each tag is stored in a #GtkTextTagTable. A tag table defines a set of -tags that can be used together. Each buffer has one tag table associated with -it; only tags from that tag table can be used with the buffer. A single tag -table can be shared between multiple buffers, however. - - - -Tags can have names, which is convenient sometimes (for example, you can name -your tag that makes things bold "bold"), but they can also be anonymous (which -is convenient if you're creating tags on-the-fly). - - - -Most text manipulation is accomplished with iterators, -represented by a #GtkTextIter. An iterator represents a position between two -characters in the text buffer. #GtkTextIter is a struct designed to be -allocated on the stack; it's guaranteed to be copiable by value and never -contain any heap-allocated data. Iterators are not valid indefinitely; -whenever the buffer is modified in a way that affects the number of characters -in the buffer, all outstanding iterators become invalid. (Note that deleting -5 characters and then reinserting 5 still invalidates iterators, though you -end up with the same number of characters you pass through a state with a -different number). - - - -Because of this, iterators can't be used to preserve positions across buffer -modifications. To preserve a position, the #GtkTextMark object is ideal. You -can think of a mark as an invisible cursor or insertion point; it floats in -the buffer, saving a position. If the text surrounding the mark is deleted, -the mark remains in the position the text once occupied; if text is inserted -at the mark, the mark ends up either to the left or to the right of the new -text, depending on its gravity. The standard text -cursor in left-to-right languages is a mark with right gravity, because it -stays to the right of inserted text. - - - -Like tags, marks can be either named or anonymous. There are two marks built-in -to #GtkTextBuffer; these are named "insert" and -"selection_bound" and refer to the insertion point and the -boundary of the selection which is not the insertion point, respectively. If -no text is selected, these two marks will be in the same position. You can -manipulate what is selected and where the cursor appears by moving these -marks around. - - -If you want to place the cursor in response to a user action, be sure to use -gtk_text_buffer_place_cursor(), which moves both at once without causing a -temporary selection (moving one then the other temporarily selects the range in -between the old and new positions). - - - - - -Text buffers always contain at least one line, but may be empty (that -is, buffers can contain zero characters). The last line in the text -buffer never ends in a line separator (such as newline); the other -lines in the buffer always end in a line separator. Line separators -count as characters when computing character counts and character -offsets. Note that some Unicode line separators are represented with -multiple bytes in UTF-8, and the two-character sequence "\r\n" is also -considered a line separator. - - - -Text buffers support undo and redo if gtk_text_buffer_set_enable_undo() -has been set to %TRUE. Use gtk_text_buffer_undo() or gtk_text_buffer_redo() -to perform the necessary action. Note that these operations are ignored if -the buffer is not editable. Developers may want some operations to not be -undoable. To do this, wrap your changes in -gtk_text_buffer_begin_irreversible_action() and -gtk_text_buffer_end_irreversible_action(). - - - - - - -Simple Example - - -The simplest usage of #GtkTextView might look like this: - - GtkWidget *view; - GtkTextBuffer *buffer; - - view = gtk_text_view_new (); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); - - /* Now you might put the view in a container and display it on the - * screen; when the user edits the text, signals on the buffer - * will be emitted, such as "changed", "insert_text", and so on. - */ - -In many cases it's also convenient to first create the buffer with -gtk_text_buffer_new(), then create a widget for that buffer with -gtk_text_view_new_with_buffer(). Or you can change the buffer the widget -displays after the widget is created with gtk_text_view_set_buffer(). - - - - - -Example of Changing Text Attributes - - - -The way to affect text attributes in #GtkTextView is to -apply tags that change the attributes for a region of text. -For text features that come from the theme — such as font and -foreground color — use CSS to override their default values. - - - GtkWidget *view; - GtkTextBuffer *buffer; - GtkTextIter start, end; - PangoFontDescription *font_desc; - GdkRGBA rgba; - GtkTextTag *tag; - GtkCssProvider *provider; - GtkStyleContext *context; - - view = gtk_text_view_new (); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1); - - /* Change default font and color throughout the widget */ - provider = gtk_css_provider_new (); - gtk_css_provider_load_from_data (provider, - "textview {" - " font: 15 serif;" - " color: green;" - "}", - -1); - context = gtk_widget_get_style_context (view); - gtk_style_context_add_provider (context, - GTK_STYLE_PROVIDER (provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - /* Change left margin throughout the widget */ - gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30); - - /* Use a tag to change the color for just one part of the widget */ - tag = gtk_text_buffer_create_tag (buffer, "blue_foreground", - "foreground", "blue", NULL); - gtk_text_buffer_get_iter_at_offset (buffer, &start, 7); - gtk_text_buffer_get_iter_at_offset (buffer, &end, 12); - gtk_text_buffer_apply_tag (buffer, tag, &start, &end); - - - - - -The gtk-demo application that comes with -GTK contains more example code for #GtkTextView. - - - - - diff --git a/docs/reference/gtk/tree_widget.xml b/docs/reference/gtk/tree_widget.xml deleted file mode 100644 index 1789b42bd3..0000000000 --- a/docs/reference/gtk/tree_widget.xml +++ /dev/null @@ -1,321 +0,0 @@ - - - - - Tree and List Widget Overview - 3 - GTK Library - - - - Tree and List Widget Overview - Overview of GtkTreeModel, GtkTreeView, and friends - - - - Overview - - To create a tree or list in GTK, use the #GtkTreeModel interface in - conjunction with the #GtkTreeView widget. This widget is - designed around a Model/View/Controller - design and consists of four major parts: - - The tree view widget (GtkTreeView) - The view column (GtkTreeViewColumn) - The cell renderers (GtkCellRenderer etc.) - The model interface (GtkTreeModel) - - The View is composed of the first three - objects, while the last is the Model. One - of the prime benefits of the MVC design is that multiple views - can be created of a single model. For example, a model mapping - the file system could be created for a file manager. Many views - could be created to display various parts of the file system, - but only one copy need be kept in memory. - - - The purpose of the cell renderers is to provide extensibility to the - widget and to allow multiple ways of rendering the same type of data. - For example, consider how to render a boolean variable. Should it - render it as a string of "True" or "False", "On" or "Off", or should - it be rendered as a checkbox? - - - - Creating a model - - GTK provides two simple models that can be used: the #GtkListStore - and the #GtkTreeStore. GtkListStore is used to model list widgets, - while the GtkTreeStore models trees. It is possible to develop a new - type of model, but the existing models should be satisfactory for all - but the most specialized of situations. Creating the model is quite - simple: - - - - This creates a list store with two columns: a string column and a boolean - column. Typically the 2 is never passed directly like that; usually an - enum is created wherein the different columns are enumerated, followed by - a token that represents the total number of columns. The next example will - illustrate this, only using a tree store instead of a list store. Creating - a tree store operates almost exactly the same. - - - - Adding data to the model is done using gtk_tree_store_set() or - gtk_list_store_set(), depending upon which sort of model was - created. To do this, a #GtkTreeIter must be acquired. The iterator - points to the location where data will be added. - - - Once an iterator has been acquired, gtk_tree_store_set() is used to - apply data to the part of the model that the iterator points to. - Consider the following example: - - - - - Notice that the last argument is -1. This is always done because - this is a variable-argument function and it needs to know when to stop - processing arguments. It can be used to set the data in any or all - columns in a given row. - - - The third argument to gtk_tree_store_append() is the parent iterator. It - is used to add a row to a GtkTreeStore as a child of an existing row. This - means that the new row will only be visible when its parent is visible and - in its expanded state. Consider the following example: - - - - - - Creating the view component - - While there are several different models to choose from, there is - only one view widget to deal with. It works with either the list - or the tree store. Setting up a #GtkTreeView is not a difficult - matter. It needs a #GtkTreeModel to know where to retrieve its data - from. - - - - - Columns and cell renderers - - Once the #GtkTreeView widget has a model, it will need to know how - to display the model. It does this with columns and cell renderers. - - - Cell renderers are used to draw the data in the tree model in a - way. There are a number of cell renderers that come with GTK, - including the #GtkCellRendererText, #GtkCellRendererPixbuf and - the #GtkCellRendererToggle. - It is relatively easy to write a custom renderer. - - - A #GtkTreeViewColumn is the object that GtkTreeView uses to organize - the vertical columns in the tree view. It needs to know the name of - the column to label for the user, what type of cell renderer to use, - and which piece of data to retrieve from the model for a given row. - - -GtkCellRenderer *renderer; -GtkTreeViewColumn *column; - -renderer = gtk_cell_renderer_text_new (); -column = gtk_tree_view_column_new_with_attributes ("Author", - renderer, - "text", AUTHOR_COLUMN, - NULL); -gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - - At this point, all the steps in creating a displayable tree have been - covered. The model is created, data is stored in it, a tree view is - created and columns are added to it. - - - - - Selection handling - - Most applications will need to not only deal with displaying data, but - also receiving input events from users. To do this, simply get a - reference to a selection object and connect to the - #GtkTreeSelection::changed signal. - - - - Then to retrieve data for the row selected: - - - - - - - Simple Example - - Here is a simple example of using a #GtkTreeView widget in context - of the other widgets. It simply creates a simple model and view, - and puts them together. Note that the model is never populated - with data — that is left as an exercise for the reader. - More information can be found on this in the #GtkTreeModel section. - -enum -{ - TITLE_COLUMN, - AUTHOR_COLUMN, - CHECKED_COLUMN, - N_COLUMNS -}; - -void -setup_tree (void) -{ - GtkTreeStore *store; - GtkWidget *tree; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - - /* Create a model. We are using the store model for now, though we - * could use any other GtkTreeModel */ - store = gtk_tree_store_new (N_COLUMNS, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_BOOLEAN); - - /* custom function to fill the model with data */ - populate_tree_model (store); - - /* Create a view */ - tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); - - /* The view now holds a reference. We can get rid of our own - * reference */ - g_object_unref (G_OBJECT (store)); - - /* Create a cell render and arbitrarily make it red for demonstration - * purposes */ - renderer = gtk_cell_renderer_text_new (); - g_object_set (G_OBJECT (renderer), - "foreground", "red", - NULL); - - /* Create a column, associating the "text" attribute of the - * cell_renderer to the first column of the model */ - column = gtk_tree_view_column_new_with_attributes ("Author", renderer, - "text", AUTHOR_COLUMN, - NULL); - - /* Add the column to the view. */ - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Second column.. title of the book. */ - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Title", - renderer, - "text", TITLE_COLUMN, - NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Last column.. whether a book is checked out. */ - renderer = gtk_cell_renderer_toggle_new (); - column = gtk_tree_view_column_new_with_attributes ("Checked out", - renderer, - "active", CHECKED_COLUMN, - NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); - - /* Now we can manipulate the view just like any other GTK widget */ - ... -} - - - -