From 225e40fb53d99e83b83a13eacd19be45acfd13ca Mon Sep 17 00:00:00 2001 From: Jean Sirmai Date: Wed, 20 Dec 2023 12:02:05 +0100 Subject: [PATCH] WIP: before learning GtkCellEditable (cleaning) --- Gtk text example | 389 ++++++++++++++++++++++++++++++++++++ demos/gtk-demo/tree_store.c | 73 +++---- 2 files changed, 428 insertions(+), 34 deletions(-) create mode 100644 Gtk text example diff --git a/Gtk text example b/Gtk text example new file mode 100644 index 0000000..dde9a15 --- /dev/null +++ b/Gtk text example @@ -0,0 +1,389 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2019 Red Hat, Inc. + * + * Authors: + * - Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include "demotaggedentry.h" +#include + +struct _DemoTaggedEntry +{ + GtkWidget parent_instance; + GtkWidget *text; +}; + +struct _DemoTaggedEntryClass +{ + GtkWidgetClass parent_class; +}; + +static void demo_tagged_entry_editable_init (GtkEditableInterface *iface); +G_DEFINE_TYPE_WITH_CODE (DemoTaggedEntry, demo_tagged_entry, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, demo_tagged_entry_editable_init)) +static void +demo_tagged_entry_init (DemoTaggedEntry *entry) +{ + GtkCssProvider *provider; + entry->text = gtk_text_new (); + gtk_widget_set_hexpand (entry->text, TRUE); + gtk_widget_set_vexpand (entry->text, TRUE); + gtk_widget_set_parent (entry->text, GTK_WIDGET (entry)); + gtk_editable_init_delegate (GTK_EDITABLE (entry)); + gtk_editable_set_width_chars (GTK_EDITABLE (entry->text), 6); + gtk_editable_set_max_width_chars (GTK_EDITABLE (entry->text), 6); + gtk_widget_add_css_class (GTK_WIDGET (entry), "tagged"); + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/tagged_entry/tagstyle.css"); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + 800); + g_object_unref (provider); +} + +static void +demo_tagged_entry_dispose (GObject *object) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (object); + GtkWidget *child; + if (entry->text) + gtk_editable_finish_delegate (GTK_EDITABLE (entry)); + while ((child = gtk_widget_get_first_child (GTK_WIDGET (entry)))) + gtk_widget_unparent (child); + entry->text = NULL; + G_OBJECT_CLASS (demo_tagged_entry_parent_class)->dispose (object); +} + +static void +demo_tagged_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + if (gtk_editable_delegate_set_property (object, prop_id, value, pspec)) + return; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} +static void +demo_tagged_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + if (gtk_editable_delegate_get_property (object, prop_id, value, pspec)) + return; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static gboolean +demo_tagged_entry_grab_focus (GtkWidget *widget) +{ + DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget); + return gtk_widget_grab_focus (entry->text); +} + +static void +demo_tagged_entry_class_init (DemoTaggedEntryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + object_class->dispose = demo_tagged_entry_dispose; + object_class->get_property = demo_tagged_entry_get_property; + object_class->set_property = demo_tagged_entry_set_property; + widget_class->grab_focus = demo_tagged_entry_grab_focus; + gtk_editable_install_properties (object_class, 1); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); + gtk_widget_class_set_css_name (widget_class, "entry"); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TEXT_BOX); +} + +static GtkEditable * +demo_tagged_entry_get_delegate (GtkEditable *editable) +{ + return GTK_EDITABLE (DEMO_TAGGED_ENTRY (editable)->text); +} + +static void +demo_tagged_entry_editable_init (GtkEditableInterface *iface) +{ + iface->get_delegate = demo_tagged_entry_get_delegate; +} +GtkWidget * +demo_tagged_entry_new (void) +{ + return GTK_WIDGET (g_object_new (DEMO_TYPE_TAGGED_ENTRY, NULL)); +} + +void +demo_tagged_entry_add_tag (DemoTaggedEntry *entry, + GtkWidget *tag) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + gtk_widget_set_parent (tag, GTK_WIDGET (entry)); +} + +void +demo_tagged_entry_insert_tag_after (DemoTaggedEntry *entry, + GtkWidget *tag, + GtkWidget *sibling) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + gtk_widget_insert_after (tag, GTK_WIDGET (entry), sibling); +} + +void +demo_tagged_entry_remove_tag (DemoTaggedEntry *entry, + GtkWidget *tag) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry)); + gtk_widget_unparent (tag); +} + +struct _DemoTaggedEntryTag +{ + GtkWidget parent; + GtkWidget *box; + GtkWidget *label; + GtkWidget *button; + gboolean has_close_button; + char *style; +}; + +struct _DemoTaggedEntryTagClass +{ + GtkWidgetClass parent_class; +}; + +enum { + PROP_0, + PROP_LABEL, + PROP_HAS_CLOSE_BUTTON, +}; + +enum { + SIGNAL_CLICKED, + SIGNAL_BUTTON_CLICKED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; +G_DEFINE_TYPE (DemoTaggedEntryTag, demo_tagged_entry_tag, GTK_TYPE_WIDGET) +static void +on_released (GtkGestureClick *gesture, + int n_press, + double x, + double y, + DemoTaggedEntryTag *tag) +{ + g_signal_emit (tag, signals[SIGNAL_CLICKED], 0); +} + +static void +demo_tagged_entry_tag_init (DemoTaggedEntryTag *tag) +{ + GtkGesture *gesture; + tag->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_parent (tag->box, GTK_WIDGET (tag)); + tag->label = gtk_label_new (""); + gtk_box_append (GTK_BOX (tag->box), tag->label); + gesture = gtk_gesture_click_new (); + g_signal_connect (gesture, "released", G_CALLBACK (on_released), tag); + gtk_widget_add_controller (GTK_WIDGET (tag), GTK_EVENT_CONTROLLER (gesture)); +} + +static void +demo_tagged_entry_tag_dispose (GObject *object) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + g_clear_pointer (&tag->box, gtk_widget_unparent); + G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->dispose (object); +} + +static void +demo_tagged_entry_tag_finalize (GObject *object) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + g_clear_pointer (&tag->box, gtk_widget_unparent); + g_clear_pointer (&tag->style, g_free); + G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->finalize (object); +} + +static void +demo_tagged_entry_tag_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + switch (prop_id) + { + case PROP_LABEL: + demo_tagged_entry_tag_set_label (tag, g_value_get_string (value)); + break; + case PROP_HAS_CLOSE_BUTTON: + demo_tagged_entry_tag_set_has_close_button (tag, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +demo_tagged_entry_tag_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object); + switch (prop_id) + { + case PROP_LABEL: + g_value_set_string (value, demo_tagged_entry_tag_get_label (tag)); + break; + case PROP_HAS_CLOSE_BUTTON: + g_value_set_boolean (value, demo_tagged_entry_tag_get_has_close_button (tag)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +demo_tagged_entry_tag_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget); + gtk_widget_measure (tag->box, orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); +} + +static void +demo_tagged_entry_tag_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget); + gtk_widget_size_allocate (tag->box, + &(GtkAllocation) { 0, 0, width, height }, + baseline); +} + +static void +demo_tagged_entry_tag_class_init (DemoTaggedEntryTagClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + object_class->dispose = demo_tagged_entry_tag_dispose; + object_class->finalize = demo_tagged_entry_tag_finalize; + object_class->set_property = demo_tagged_entry_tag_set_property; + object_class->get_property = demo_tagged_entry_tag_get_property; + widget_class->measure = demo_tagged_entry_tag_measure; + widget_class->size_allocate = demo_tagged_entry_tag_size_allocate; + signals[SIGNAL_CLICKED] = + g_signal_new ("clicked", + DEMO_TYPE_TAGGED_ENTRY_TAG, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[SIGNAL_BUTTON_CLICKED] = + g_signal_new ("button-clicked", + DEMO_TYPE_TAGGED_ENTRY_TAG, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", "Label", "Label", + NULL, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_HAS_CLOSE_BUTTON, + g_param_spec_boolean ("has-close-button", "Has close button", "Whether this tag has a close button", + FALSE, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); + gtk_widget_class_set_css_name (widget_class, "tag"); +} + +DemoTaggedEntryTag * +demo_tagged_entry_tag_new (const char *label) +{ + return DEMO_TAGGED_ENTRY_TAG (g_object_new (DEMO_TYPE_TAGGED_ENTRY_TAG, + "label", label, + NULL)); +} + +const char * +demo_tagged_entry_tag_get_label (DemoTaggedEntryTag *tag) +{ + g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), NULL); + return gtk_label_get_label (GTK_LABEL (tag->label)); +} + +void +demo_tagged_entry_tag_set_label (DemoTaggedEntryTag *tag, + const char *label) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag)); + gtk_label_set_label (GTK_LABEL (tag->label), label); +} + +static void +on_button_clicked (GtkButton *button, + DemoTaggedEntryTag *tag) +{ + g_signal_emit (tag, signals[SIGNAL_BUTTON_CLICKED], 0); +} + +void +demo_tagged_entry_tag_set_has_close_button (DemoTaggedEntryTag *tag, + gboolean has_close_button) +{ + g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag)); + if ((tag->button != NULL) == has_close_button) + return; + if (!has_close_button && tag->button) + { + gtk_box_remove (GTK_BOX (tag->box), tag->button); + tag->button = NULL; + } + else if (has_close_button && tag->button == NULL) + { + GtkWidget *image; + image = gtk_image_new_from_icon_name ("window-close-symbolic"); + tag->button = gtk_button_new (); + gtk_button_set_child (GTK_BUTTON (tag->button), image); + gtk_widget_set_halign (tag->button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (tag->button, GTK_ALIGN_CENTER); + gtk_button_set_has_frame (GTK_BUTTON (tag->button), FALSE); + gtk_box_append (GTK_BOX (tag->box), tag->button); + g_signal_connect (tag->button, "clicked", G_CALLBACK (on_button_clicked), tag); + } + g_object_notify (G_OBJECT (tag), "has-close-button"); +} + +gboolean +demo_tagged_entry_tag_get_has_close_button (DemoTaggedEntryTag *tag) +{ + g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), FALSE); + return tag->button != NULL; +} diff --git a/demos/gtk-demo/tree_store.c b/demos/gtk-demo/tree_store.c index aa4f0ba..7459a36 100644 --- a/demos/gtk-demo/tree_store.c +++ b/demos/gtk-demo/tree_store.c @@ -1,46 +1,51 @@ -/************************************************** R E M E M B E R ! *****************************************************/ -/* */ -/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk$ guix shell -m manifest.scm --check */ -/* guix shell: vérification des variables d'environnement visibles depuis le shell « /gnu/store/ --- (a commit) --- /bin/bash »… */ -/* guix shell: Tout va bien ! Le shell a les bonnes variables d'environnement. */ -/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk [env] $ cd builddir/ */ -/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk/builddir [env] $ */ -/* jean@Project:~/Gem-Graph/gem-graph-client/tree...way/gtk/builddir [env] $ clear && meson compile && demos/gtk-demo/gtk4-demo */ -/* */ -/* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- A Session -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- */ -/* */ -/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk/builddir [env] $ ctrl D (avant de fermer builder) */ -/* */ -/*************************************************************************************************************************************/ +/*************************************************** R E M E M B E R ! *******************************************************/ +/* */ +/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk$ guix shell -m manifest.scm --check */ +/* guix shell: vérification des variables d'environnement visibles depuis le shell « /gnu/store/ --- (a commit) --- /bin/bash »… */ +/* guix shell: Tout va bien ! Le shell a les bonnes variables d'environnement. */ +/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk [env] $ cd builddir/ */ +/* jean@Project:~/Gem-Graph/gem-graph-client/tree...way/gtk/builddir [env] $ clear && meson compile && demos/gtk-demo/gtk4-demo */ +/* */ +/* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- A Session -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- */ +/* */ +/* jean@Project:~/Gem-Graph/gem-graph-client/tree (learning)/The_Gnome_way/gtk/builddir [env] $ ctrl D (avant de fermer builder) */ +/* */ +/****************************************************************************************************************************************/ -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // -/************************************************** <> *****************************************************/ -/* */ -/* https://developer-old.gnome.org/gtk4/stable/GtkTreeView.html */ -/* GtkTreeViewColumn, GtkTreeSelection, GtkTreeModel, GtkTreeView drag-and-drop, GtkTreeSortable, GtkTreeModelSort, GtkListStore, */ -/* GtkTreeStore, GtkCellRenderer, GtkCellEditable, GtkCellRendererPixbuf, GtkCellRendererText, GtkCellRendererToggle */ -/* */ -/* https://developer-old.gnome.org/gtk4/stable/GtkTreeView.html#gtk-tree-view-get-path-at-pos */ -/* Finds the path at the point (x , y ), relative to bin_window coordinates. */ -/* Widget-relative coordinates must be converted using gtk_tree_view_convert_widget_to_bin_window_coords(). */ -/* */ -/* https://www.gnu.org/software/guile-gnome/docs/gtk/html/GtkCellRenderer.html */ -/* */ -/*************************************************************************************************************************************/ - - -/* The GtkTreeStore is used to store data in tree form, to be used later on by a GtkTreeView to display it */ +/************************************************** W I D G E T S ********************************************************/ +/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ +/* _ _ _ _ _ _ _ _ _ _ _ _ _ E D I T A B L E _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ +/* */ +/* https://developer-old.gnome.org/gtk4/stable/ch03s02.html Which widget should I use ?... */ +/* https://developer-old.gnome.org/gtk4/stable/GtkCellEditable.html#GtkCellEditable-struct */ +/* */ +/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ +/* _ _ _ _ _ _ _ _ _ _ _ _ _ _ T R E E _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ +/* */ +/* https://developer-old.gnome.org/gtk4/stable/GtkTreeView.html */ +/* https://developer-old.gnome.org/gtk4/stable/GtkTreeSelection.html#GtkTreeSelection-struct */ +/* https://developer-old.gnome.org/gtk4/stable/GtkTreeView.html#gtk-tree-view-get-path-at-pos << get-path-at-pos */ +/* Finds the path at the point (x , y ), relative to bin_window coordinates. Use gtk_tree_view_convert_widget_to_bin_window_coords(). */ +/* https://www.gnu.org/software/guile-gnome/docs/gtk/html/GtkCellRenderer.html */ +/* GtkTreeSelection, GtkTreeView drag-and-drop, GtkTreeSortable, GtkTreeModelSort, GtkCellEditable, GtkCellRendererText,... */ +/* gtk_tree_view_get_search_entry (treeview) */ +/* */ +/****************************************************************************************************************************************/ #include #include +#include "config.h" G_GNUC_BEGIN_IGNORE_DEPRECATIONS +/* The GtkTreeStore is used to store data in tree form, to be used later on by a GtkTreeView to display it. */ + + /* TreeItem structure */ typedef struct _TreeItem TreeItem; struct _TreeItem @@ -83,7 +88,7 @@ static GtkTreeModel *create_node_recursive (GtkTreeStore *model, gtk_tree_store_set (model, &iter, COLUMN_0, current_item->label, -1); if (current_item->children) - create_node_recursive (model, current_item->children, &iter, depth+1); + create_node_recursive (model, current_item->children, &iter, depth + 1); else break; @@ -91,7 +96,7 @@ static GtkTreeModel *create_node_recursive (GtkTreeStore *model, } if (depth == 0) - return GTK_TREE_MODEL(model); + return GTK_TREE_MODEL(model); // cast from GtkTreeModel to GtkTreeStore else return NULL; } @@ -105,7 +110,7 @@ do_tree_store (GtkWidget *do_widget) if (!window) { GtkWidget *vbox; - GtkWidget *sw; + GtkWidget *sw; // sw : 'scrolled_window' GtkWidget *treeview; GtkTreeModel *tree_model; GtkTreeStore *tree_store = NULL;