From feabe6662e13aa168287dfe9dd56370a6eda256f Mon Sep 17 00:00:00 2001 From: Adrien 'neox' Bourmault Date: Wed, 21 Feb 2024 20:23:35 +0100 Subject: [PATCH] First (quite working) real tree in Gem-graph --- include/ui.h | 17 ++++ src/ui/events.c | 61 +++++++++++++ src/ui/gemgraph.ui | 34 +++----- src/ui/views.c | 209 +++++++++++++++++++++++++++++++++++++++++++++ src/ui/window.c | 5 ++ 5 files changed, 303 insertions(+), 23 deletions(-) create mode 100644 src/ui/views.c diff --git a/include/ui.h b/include/ui.h index 343198b..3468bb4 100644 --- a/include/ui.h +++ b/include/ui.h @@ -146,6 +146,21 @@ void on_glarea_unrealize(GtkWidget *widget); void on_close_window(GtkWidget *widget); +// +// Tree-related events +// +void on_tree_expander_toggled(GtkExpander *expander, gpointer user_data); + +void on_tree_setup_factory(GtkSignalListItemFactory *factory, + GObject* object, gpointer user_data); + +void on_tree_bind_factory(GtkSignalListItemFactory *factory, GObject* object, + gpointer user_data); + +void on_tree_selection_changed(GtkSelectionModel* self, guint position, + guint n_items, gpointer user_data); + + /* -------------------------------------------------------------------------- */ // @@ -230,3 +245,5 @@ void ui_shutdown_all_graphic_stacks(void); */ bool ui_update_axis_stack(GtkWidget *container_widget, int axis, int value); +// XXX +void ui_create_tree (GtkWidget *target_widget); diff --git a/src/ui/events.c b/src/ui/events.c index 90c69dc..4e053de 100644 --- a/src/ui/events.c +++ b/src/ui/events.c @@ -367,3 +367,64 @@ void ui_model_loading(GObject *source_object, g_free(content); } +/* -------------------------------------------------------------------------- */ + +/* + * Tree-related signals + */ + +void on_tree_expander_toggled(GtkExpander *expander, gpointer user_data) +{ + // This is a conceptual callback for when an expander is toggled + GtkTreeListRow *row = GTK_TREE_LIST_ROW(user_data); + gboolean is_expanded = gtk_tree_list_row_get_expanded(row); + gtk_tree_list_row_set_expanded(row, !is_expanded); +} + +void on_tree_setup_factory (GtkSignalListItemFactory *factory, + GObject* object, + gpointer user_data) +{ + GtkWidget* expander = gtk_expander_new (NULL); + gtk_list_item_set_child (GTK_LIST_ITEM(object), expander); + printf("[on_tree_setup_factory] here is an expander\n"); +} + +void on_tree_bind_factory (GtkSignalListItemFactory *factory, + GObject* object, + gpointer user_data) +{ + GObject *item; + const gchar *text; + GtkTreeListRow *row; + GtkListItem *list_item; + GtkWidget *expander; + + list_item= GTK_LIST_ITEM(object); + row = gtk_list_item_get_item(list_item); + if (row != NULL) { + text = gtk_string_object_get_string(GTK_STRING_OBJECT(gtk_tree_list_row_get_item(row))); + expander = gtk_list_item_get_child(list_item); + gtk_expander_set_label(GTK_EXPANDER(expander), text); + + // Disconnect previous signal handlers to avoid stacking them + g_signal_handlers_disconnect_by_func(expander, G_CALLBACK(on_tree_expander_toggled), row); + + // Connect the signal handler + g_signal_connect(expander, "activate", G_CALLBACK(on_tree_expander_toggled), row); + + gtk_widget_set_margin_start(expander, gtk_tree_list_row_get_depth(row)*20); + gboolean is_expanded = gtk_tree_list_row_get_expanded(row); + printf("[on_tree_bind_factory] here is %s content and expander is %d\n", + text, + is_expanded); + } else { + printf("[on_tree_bind_factory] here is NON content\n"); + } +} + +void on_tree_selection_changed (GtkSelectionModel* self, guint position, + guint n_items, gpointer user_data) +{ + printf("[on_tree_selection_changed]\n"); +} diff --git a/src/ui/gemgraph.ui b/src/ui/gemgraph.ui index e5469a7..f716aed 100644 --- a/src/ui/gemgraph.ui +++ b/src/ui/gemgraph.ui @@ -65,32 +65,24 @@ - runlib_objects - Objects + runlib_conditions + Conditions True - - center - <b>runlib_objects</b> - True - 50 - 50 + + vertical - runlib_conditions_and_rules - Rules & Conds + runlib_objects + Objects True - - center - <b>runlib_conditions_and_rules</b> - True - 50 - 50 + + vertical @@ -98,15 +90,11 @@ runlib_states - Savedstates + Savestates True - - center - <b>runlib_states</b> - True - 50 - 50 + + vertical diff --git a/src/ui/views.c b/src/ui/views.c new file mode 100644 index 0000000..af46956 --- /dev/null +++ b/src/ui/views.c @@ -0,0 +1,209 @@ +/* + * Gem-graph OpenGL experiments + * + * Desc: User interface functions + * + * Copyright (C) 2023 Arthur Menges + * Copyright (C) 2023 Adrien Bourmault + * + * This file is part of Gem-graph. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "../../include/base.h" +#include "../../include/ui.h" + +// Simplified TreeNode structure for demonstration purposes +struct TreeNode_t +{ + gchar *text; + struct TreeNode_t *child; + struct TreeNode_t *next; +}; + +// Function to create a new TreeNode instance +static struct TreeNode_t *create_tree_node (const gchar* text) +{ + struct TreeNode_t *node = g_malloc0 (sizeof(struct TreeNode_t)); + node->text = g_strdup(text); + node->child = NULL; + return node; +} + +// Function to add a child node to a parent node +static void add_child_node (struct TreeNode_t *parent, struct TreeNode_t *child) +{ + struct TreeNode_t *cur; + + if (parent->child) { + cur = parent->child; + while (cur && cur->next) { + cur = cur->next; + } + cur->next = child; + } else { + parent->child = child; + } +} + +// Recursive function to free a TreeNode and its children +static void free_tree_node (struct TreeNode_t *node) +{ + struct TreeNode_t *cur; + struct TreeNode_t *tmp; + + if (!node) return; + + // free siblings + cur = node; + while (cur) { + tmp = cur->next; + g_free(cur); + cur = tmp; + } + + // recursive free + free_tree_node(node->child); + g_free(node->text); + g_free(node); +} + +// Function to simulate getting a GListModel of children for a given TreeNode +GListModel* ui_tree_get_children_model (struct TreeNode_t *parent) +{ + struct TreeNode_t *child; + + GtkStringList *list = NULL; + + if (parent) { + printf("[ui_tree_get_children_model] here is %s content : ", parent->text); + + child = parent->child; + + if (child) { + list = gtk_string_list_new(NULL); + } + while(child) { + gtk_string_list_append(list, child->text); + printf("%s ", child->text); + child = child->next; + } + } + printf("\n"); + + return G_LIST_MODEL(list); +} + +// GtkTreeListModelCreateModelFunc callback implementation +GListModel* ui_tree_create_model_func(GObject *item, gpointer root) +{ + struct TreeNode_t *cur = (struct TreeNode_t *)root; + struct TreeNode_t *parent = NULL; + + gchar *string = gtk_string_object_get_string(GTK_STRING_OBJECT(item)); + + parent = root; + while (cur) { + if (strcmp(string, cur->text) == 0) { + break; + } + + cur = cur->next; + if (cur == NULL) { + cur = parent->child; + parent = cur; + } + } + + printf("[ui_tree_create_model_func] looked for %s in %s item\n", + cur->text, + string); + + return ui_tree_get_children_model(cur); +} + +// Application activation callback +void ui_create_tree (GtkWidget *target_widget) +{ + GtkStringList *model; + GtkTreeListModel *tree_model; + GtkSignalListItemFactory *factory; + GtkSingleSelection *selection_model; + GtkWidget *list_view; + GtkScrolledWindow *scrolled_window; + + assert(target_widget); + + // AD HOC XXX & no free() + struct TreeNode_t *tree_root = create_tree_node("Root"); + struct TreeNode_t *A1 = create_tree_node("A1"); + struct TreeNode_t *A2 = create_tree_node("A2"); + struct TreeNode_t *A3 = create_tree_node("A3"); + struct TreeNode_t *B1 = create_tree_node("B1"); + struct TreeNode_t *B2 = create_tree_node("B2"); + struct TreeNode_t *B3 = create_tree_node("B3"); + struct TreeNode_t *C = create_tree_node("C"); + + add_child_node(tree_root, A1); + add_child_node(tree_root, A2); + add_child_node(tree_root, A3); + add_child_node(A1, B1); + add_child_node(B1, C); + + model = gtk_string_list_new(NULL); + gtk_string_list_append(model, tree_root->text); + + // Create and setup the list view and item factory + factory = gtk_signal_list_item_factory_new (); + g_signal_connect (factory, "setup", G_CALLBACK(on_tree_setup_factory), NULL); + g_signal_connect (factory, "bind", G_CALLBACK(on_tree_bind_factory), NULL); + + // Create a GtkTreeListModel + tree_model = gtk_tree_list_model_new( + G_LIST_MODEL(model), + FALSE, // Passthrough - False in actual usage with dynamic children retrieval + FALSE, // autoexpand + (GtkTreeListModelCreateModelFunc)ui_tree_create_model_func, + tree_root, + NULL //(GDestroyNotify)free_tree_node + ); + + selection_model = gtk_single_selection_new ( + G_LIST_MODEL (tree_model)); + + gtk_single_selection_set_autoselect (selection_model, FALSE); + gtk_single_selection_set_can_unselect (selection_model, TRUE); + /* g_signal_connect (selection_model, "selection-changed", */ + /* G_CALLBACK(on_tree_selection_changed), NULL); */ + + list_view = gtk_list_view_new (selection_model, factory); + + scrolled_window = gtk_scrolled_window_new(); + gtk_scrolled_window_set_child(scrolled_window, list_view); + + gtk_scrolled_window_set_policy (scrolled_window, + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_vexpand(GTK_WIDGET(scrolled_window), TRUE); + + gtk_box_append(GTK_BOX(target_widget), GTK_WIDGET(scrolled_window)); + gtk_widget_show(GTK_WIDGET(scrolled_window)); + gtk_widget_show(GTK_WIDGET(list_view)); +} + diff --git a/src/ui/window.c b/src/ui/window.c index a7ed883..a92df9b 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -53,6 +53,7 @@ struct _GemGraphClientWindow GtkBox *run_glarea_box; GtkBox *edition_glarea_box; GtkBox *presentation_glarea_box; + GtkBox *run_conditions_tree_box; }; G_DEFINE_FINAL_TYPE (GemGraphClientWindow, @@ -113,6 +114,9 @@ static void gem_graph_client_window_class_init(GemGraphClientWindowClass *klass) gtk_widget_class_bind_template_child(widget_class, GemGraphClientWindow, presentation_glarea_box); + gtk_widget_class_bind_template_child(widget_class, + GemGraphClientWindow, + run_conditions_tree_box); } static void gem_graph_client_window_init(GemGraphClientWindow *self) @@ -162,6 +166,7 @@ void ui_set_stack(int mode) gtk_menu_button_set_icon_name(window->main_button_mode, "system-run-symbolic"); ui_setup_glarea(RUN_MODE, GTK_WIDGET(window->run_glarea_box)); + ui_create_tree(window->run_conditions_tree_box); break; case PRESENTATION_MODE: gtk_stack_set_visible_child_full(window->main_stack,