gem-graph-client/tree (learning)/(mes notes)

395 lines
18 KiB
Plaintext
Raw Normal View History

http://web.mit.edu/ghudson/dev/nokrb/third/gtk2/docs/reference/gtk/html/GtkListStore.html << GtkListStore
/---------------------------------------------------------------------------------------------------------------------------/
GDK = Guimp Drawing Kit
Guimp = GNU Image Manipulator
GObject = Any GDK GTK Object
For the impatient, here is a small tree view Hello World program:
https://docs.gtk.org/gtk3/treeview-tutorial.html
GtkTreeModel is basically just an interface to the data store, meaning that it is a standardised set of functions that allows a GtkTreeView widget (and the application programmer) to query certain characteristics of a data store, for example how many rows there are, which rows have children, and how many children a particular row has. It also provides functions to retrieve data from the data store, and tell the tree view what type of data is stored in the model.
Every data store must implement the GtkTreeModel interface and provide these functions, which you can use by casting a store to a tree model with GTK_TREE_MODEL(store). GtkTreeModel itself only provides a way to query a data stores characteristics and to retrieve existing data, it does not provide a way to remove or add rows to the store or put data into the store. This is done using the specific stores functions.
GTK comes with two built-in data stores (models): GtkListStore and GtkTreeStore. ...you should consider implementing your own custom model that stores and manipulates data your own way and implements the GtkTreeModel interface. GtkTreeStore will take care of the view side for you once you have configured the GtkTreeView to display what you want.
A model (data store) has model columns and rows. While a tree view will display each row in the model as a row in the view, the models columns are not to be confused with a views columns. A model column represents a certain data field of an item that has a fixed data type. You need to know what kind of data you want to store when you create a list store or a tree store, as you can not add new fields later on.
If you operate with tree paths, you are most likely to use a given tree path, and use functions like gtk_tree_path_up(), gtk_tree_path_down(), gtk_tree_path_next(), gtk_tree_path_prev(), gtk_tree_path_is_ancestor(), or gtk_tree_path_is_descendant().
Tree iters are used to retrieve data from the store, and to put data into the store. You get a tree iter as result if you are using gtk_tree_store_append().
Tree iters are only valid for a short time.
gtk_tree_view_set_model() connects our data store to the tree view, so it knows where to get the data to display from.
gtk_tree_view_get_model() will return the model that is currently attached to a given tree view.
http://web.mit.edu/ghudson/dev/nokrb/third/gtk2/docs/reference/gtk/html/GtkListStore.html << GtkListStore
/---------------------------------------------------------------------------------------------------------------------------/
#include <gtk/gtk.h>
enum
{
COL_NAME = 0,
COL_AGE,
NUM_COLS
} ;
static GtkTreeModel *
create_and_fill_model (void)
{
GtkListStore *store = gtk_list_store_new (NUM_COLS,
G_TYPE_STRING,
G_TYPE_UINT);
/* Append a row and fill in some data */
GtkTreeIter iter;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COL_NAME, "Heinz El-Mann",
COL_AGE, 51,
-1);
/* append another row and fill in some data */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COL_NAME, "Jane Doe",
COL_AGE, 23,
-1);
/* ... and a third row */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COL_NAME, "Joe Bungop",
COL_AGE, 91,
-1);
return GTK_TREE_MODEL (store);
}
static GtkWidget *
create_view_and_model (void)
{
GtkWidget *view = gtk_tree_view_new ();
GtkCellRenderer *renderer;
/* --- Column #1 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Name",
renderer,
"text", COL_NAME,
NULL);
/* --- Column #2 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Age",
renderer,
"text", COL_AGE,
NULL);
GtkTreeModel *model = create_and_fill_model ();
gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
/* The tree view has acquired its own reference to the
* model, so we can drop ours. That way the model will
* be freed automatically when the tree view is destroyed
*/
g_object_unref (model);
return view;
}
int
main (int argc, char **argv)
{
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", gtk_main_quit, NULL);
GtkWidget *view = create_view_and_model ();
gtk_container_add (GTK_CONTAINER (window), view);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
src/libide/tree/ide-tree.h
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/src/libide/gtk/ide-tree-expander.h?ref_type=heads
#include <gtk/gtk.h>
#include <libide-core.h>
G_BEGIN_DECLS
#define IDE_TYPE_TREE_EXPANDER (ide_tree_expander_get_type())
G_DECLARE_FINAL_TYPE (IdeTreeExpander, ide_tree_expander, IDE, TREE_EXPANDER, GtkWidget)
GtkWidget *ide_tree_expander_new (void);
GMenuModel *ide_tree_expander_get_menu_model (IdeTreeExpander *self);
void ide_tree_expander_set_menu_model (IdeTreeExpander *self, GMenuModel *menu_model);
GIcon *ide_tree_expander_get_icon (IdeTreeExpander *self);
void ide_tree_expander_set_icon (IdeTreeExpander *self, GIcon *icon);
void ide_tree_expander_set_icon_name (IdeTreeExpander *self, const char *icon_name);
GIcon *ide_tree_expander_get_expanded_icon (IdeTreeExpander *self);
void ide_tree_expander_set_expanded_icon (IdeTreeExpander *self, GIcon *icon);
void ide_tree_expander_set_expanded_icon_name (IdeTreeExpander *self, const char *expanded_icon_name);
const char *ide_tree_expander_get_title (IdeTreeExpander *self);
void ide_tree_expander_set_title (IdeTreeExpander *self, const char *title);
GtkWidget *ide_tree_expander_get_suffix (IdeTreeExpander *self);
void ide_tree_expander_set_suffix (IdeTreeExpander *self, GtkWidget *suffix);
GtkTreeListRow *ide_tree_expander_get_list_row (IdeTreeExpander *self);
void ide_tree_expander_set_list_row (IdeTreeExpander *self, GtkTreeListRow *list_row);
gpointer ide_tree_expander_get_item (IdeTreeExpander *self);
gboolean ide_tree_expander_get_use_markup (IdeTreeExpander *self);
void ide_tree_expander_set_use_markup (IdeTreeExpander *self, gboolean use_markup);
void ide_tree_expander_show_popover (IdeTreeExpander *self, GtkPopover *popover);
G_END_DECLS
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/src/libide/gtk/ide-tree-expander.c?ref_type=heads
(709) if (list_row != NULL)
{
self->list_row = g_object_ref (list_row);
self->list_row_notify_expanded =
g_signal_connect_object (self->list_row,
"notify::expanded",
G_CALLBACK (ide_tree_expander_notify_expanded_cb),
self,
G_CONNECT_SWAPPED);
ide_tree_expander_update_depth (self);
ide_tree_expander_update_icon (self);
}
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/src/libide/gtk/ide-truncate-model.c?ref_type=heads
#define G_LOG_DOMAIN "ide-truncate-model"
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
# my_plugin.py from : https://builder.readthedocs.io/plugins/workbench/panels.html
import gi
from gi.repository import GObject, Gtk, Dazzle, Ide
class MyEditorAddin(GObject.Object, Ide.EditorAddin):
def do_load(self, editor: Ide.EditorSurface):
# Add a widget to the left panel (aka Sidebar)
self.panel = Gtk.Label(visible=True, label='My Left Panel')
left_panel = editor.get_sidebar()
left_panel.add_section('my-section',
'My Section Title',
'my-section-icon-name',
None, # Menu id if necessary
None, # Menu icon name if necessary
self.panel,
100) # Sort priority
# To add a utility section
self.bottom = Dazzle.DockWidget(title='My Bottom Panel', icon_name='gtk-missing', visible=True)
self.bottom.add(Gtk.Label(label='Hello, Bottom Panel', visible=True))
editor.get_utilities().add(self.bottom)
def do_unload(self, editor: Ide.EditorSurface):
# Remove our widgets
self.panel.destroy()
self.bottom.destroy()
self.bottom = None
self.panel = None
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
ide = integrated development environment
src/libide/tree/ide-tree.c (597) static void ide_tree_init (IdeTree *self) {.}
src/libide/tree/ide-tree-model.c (663) static void tree_model_iface_init (GtkTreeModelIface *iface) {.}
src/libide/tree/ide-tree-node.c (31) Since: 3.32
* The #IdeTreeNode class is used to represent an item that should be displayed in the tree of the Ide application.
* The #IdeTreeAddin plugins create and maintain these nodes during the lifetime of the program.
* Plugins that want to add items to the tree should implement the #IdeTreeAddin interface and register it during plugin initialization.
src/libide/tree/ide-tree-node.c (127) enum {
PROP_0,
PROP_CHILDREN_POSSIBLE,
PROP_DESTROY_ITEM,
PROP_DISPLAY_NAME,
PROP_EXPANDED_ICON,
PROP_EXPANDED_ICON_NAME,
PROP_HAS_ERROR,
PROP_ICON,
PROP_ICON_NAME,
PROP_IS_HEADER,
PROP_ITEM,
PROP_RESET_ON_COLLAPSE,
PROP_TAG,
PROP_USE_MARKUP,
N_PROPS
};
src/libide/tree/ide-tree-node.c (48) struct _IdeTreeNode
{
GObject parent_instance;
/* A pointer to the model, which is only set on the root node. */
IdeTreeModel *model;
/* The following are fields containing the values for various properties on the tree node.
* Usually, icon, display_name, and item will be set on all nodes. */
GIcon *icon;
GIcon *expanded_icon;
gchar *display_name;
GObject *item;
gchar *tag;
GList *emblems;
/* The following items are used to maintain a tree structure of nodes for which we can use O(1) operations.
* The link is inserted into the parents children queue. The parent pointer is unowned, and set by the parent (cleared upon removal).
* This also allows maintaining the tree structure with zero additional allocations beyond the nodes themselves.
*/
IdeTreeNode *parent;
GQueue children;
GList link;
/* Foreground and Background colors */
GdkRGBA background;
GdkRGBA foreground;
/* Flags for state cell renderer */
IdeTreeNodeFlags flags;
/* When did we start loading? This is used to avoid drawing "Loading..." when the tree loads really quickly. Otherwise, we risk looking janky when the loads are quite fast. */
gint64 started_loading_at;
/* If we're currently loading */
guint is_loading : 1;
/* If the node is a header (bold, etc) */
guint is_header : 1;
/* If this is a synthesized empty node */
guint is_empty : 1;
/* If there are errors associated with the node's item */
guint has_error : 1;
/* If the node maybe has children */
guint children_possible : 1;
/* If this node needs to have the children built */
guint needs_build_children : 1;
/* If true, we remove all children on collapse */
guint reset_on_collapse : 1;
/* If pango markup should be used */
guint use_markup : 1;
/* If true, we use ide_clear_and_destroy_object() */
guint destroy_item : 1;
/* If colors are set */
guint background_set : 1;
guint foreground_set : 1;
};
src/libide/tree/ide-tree-node.c (1763) /* If the node is not on screen, we need to animate until we get there. */
src/libide/tree/ide-tree-node.c (1776) /* FIXME: Time period comes from gtk animation duration. Not curently available in pubic API.
* We need to be greater than the max timeout it could take to move, since we must have it on screen by then.
* One alternative might be to check the result and if we are still not on screen, then just pin it to a row-height from the top or bottom.
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
src/libide/tree/ide-tree-addin.c (108) /* ide_tree_addin_build_children_async() Since: 3.32
* This function is called when building the children of a node.
* This happens when expanding an node that might have children, or building the root node.
* You may want to use ide_tree_node_holds() to determine if the node contains an item that you are interested in.
* This function will call the synchronous form of IdeTreeAddin.build_children() if no asynchronous form is available.
src/libide/tree/ide-tree-addin.c (143) /* ide_tree_addin_build_children_finish() Since: 3.32
* Completes an asynchronous request to ide_tree_addin_build_children_async().
* Returns: %TRUE if successful; otherwise %FALSE and @error is set.
src/libide/tree/ide-tree-addin.c (167) /* ide_tree_addin_build_node() Since: 3.32
* This function is called when preparing a node for display in the tree.
* Addins should adjust any state on the node that makes sense based on the addin.
* You may want to use ide_tree_node_holds() to determine if the node contains an item that you are interested in.
src/libide/tree/ide-tree-addin.c (194) /* ide_tree_addin_activated() Since: 3.32
* This function is called when a node has been activated in the tree and allows for the addin to perform any necessary operations in response to that.
* If the addin performs an action based on the activation request, then it should return %TRUE from this function so that no further addins may respond to the action.
* Returns: %TRUE if the activation was handled, otherwise %FALSE
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
src/libide/tree/ide-cell-renderer-status (29) #define CELL_HEIGHT 16, CELL_WIDTH 16, RPAD 8, LPAD 3
/----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/
src/libide/gui/ide-cell-renderer-fancy.c (166)
HACK: @width is the min_width returned in our get_preferred_width() function. That results in pretty bad values here, so we will do this by assuming we are the only widget in the tree view.
* This makes this cell very much not usable for generic situations, but it does make it so we can do text wrapping without resorting to GtkListBox *for our exact usecase only*.
* The problem here is that we require the widget to already be realized and allocated and that we are the only renderer within the only column (and also, in a treeview) without exotic styling.
* If we get something absurdly small (like 50) that is because we are hitting our minimum size of (xpad * 2).
* So this works around the issue and tries to get something reasonable with wrapping at the 200px mark (our ~default width for panels).
* Furthermore, we need to queue a resize when the column size changes (as it will from resizing the widget). So the tree view must also call gtk_tree_view_column_queue_resize().