From 4da5e662a7544e8ec0b440c322f934dd8c5e4058 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 24 May 2024 06:41:11 +0200
Subject: Measure and allocate space from the GtkHexView widget code.

---
 src/gtkext/hexview.c   | 349 ++++++++++++++++++++++++++++++++++---------------
 src/gtkext/hexview.css |  10 +-
 src/gtkext/hexview.ui  |   2 +-
 3 files changed, 249 insertions(+), 112 deletions(-)

diff --git a/src/gtkext/hexview.c b/src/gtkext/hexview.c
index 56a167d..7acf131 100644
--- a/src/gtkext/hexview.c
+++ b/src/gtkext/hexview.c
@@ -28,6 +28,7 @@
 
 
 
+/* ------------------------- BASES D'UN COMPOSANT GRAPHIQUE ------------------------- */
 
 
 /* Composant d'affichage générique (instance) */
@@ -35,9 +36,23 @@ struct _GtkHexView
 {
     GtkWidget parent;                       /* A laisser en premier        */
 
-    GtkWidget *offsets;                     /* Affichage des positions     */
-    GtkWidget *hex;                         /* Affichage des octets brut   */
-    GtkWidget *ascii;                       /* Affichage des imprimables   */
+    union
+    {
+#define _CHILDREN_COUNT 4
+
+        GtkWidget *children[_CHILDREN_COUNT];/* Sous-composants d'affichage*/
+
+        struct
+        {
+            GtkWidget *offsets;             /* Affichage des positions     */
+            GtkWidget *hex;                 /* Affichage des octets brut   */
+            GtkWidget *ascii;               /* Affichage des imprimables   */
+            GtkWidget *vscroll;             /* Barre de défilement         */
+        };
+
+    };
+
+    bool need_vscrolling;                   /* Besoin de défilement ?      */
 
 };
 
@@ -63,17 +78,28 @@ static void gtk_hex_view_finalize(GtkHexView *);
 
 
 
-void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot);
+void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent);
+
+
+
 
-void demo_measure (GtkWidget      *widget,
-              GtkOrientation  orientation,
-              int             for_size,
-              int            *minimum_size,
-              int            *natural_size,
-              int            *minimum_baseline,
-                   int            *natural_baseline);
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
 
 
+/* Prend acte de la taille allouée au composant d'affichage. */
+static void gtk_hex_view_size_allocate(GtkWidget *, int, int, int);
+
+/* Fournit le mode de calcul pour déterminer la taille. */
+static GtkSizeRequestMode gtk_hex_view_get_request_mode(GtkWidget *);
+
+/* Fournit les mesures mainimale et idéale du composant. */
+static void gtk_hex_view_measure(GtkWidget *, GtkOrientation, int, int *, int *, int *, int *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                           BASES D'UN COMPOSANT GRAPHIQUE                           */
+/* ---------------------------------------------------------------------------------- */
 
 
 /* Détermine le type du composant d'affichage générique. */
@@ -106,8 +132,6 @@ static void gtk_hex_view_class_init(GtkHexViewClass *class)
 
     gtk_widget_class_set_css_name(widget, "GtkHexView");
 
-    gtk_widget_class_set_layout_manager_type(widget, GTK_TYPE_BOX_LAYOUT);
-
     g_type_ensure(GTK_TYPE_COMPOSING_AREA);
 
     gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/hexview.ui");
@@ -115,31 +139,11 @@ static void gtk_hex_view_class_init(GtkHexViewClass *class)
     gtk_widget_class_bind_template_child(widget, GtkHexView, offsets);
     gtk_widget_class_bind_template_child(widget, GtkHexView, hex);
     gtk_widget_class_bind_template_child(widget, GtkHexView, ascii);
+    gtk_widget_class_bind_template_child(widget, GtkHexView, vscroll);
 
-
-
-    //widget->snapshot = demo_snapshot;
-
-    //widget->measure = demo_measure;
-
-
-    /*
-
-
-
-
-
-
-     */
-
-
-    /*
-    widget->destroy = gtk_hex_view_destroy;
-    widget->realize = gtk_hex_view_realize;
     widget->size_allocate = gtk_hex_view_size_allocate;
-    widget->get_preferred_height = gtk_hex_view_get_preferred_height;
-    widget->get_preferred_width = gtk_hex_view_get_preferred_width;
-    */
+    widget->get_request_mode = gtk_hex_view_get_request_mode;
+    widget->measure = gtk_hex_view_measure;
 
 }
 
@@ -160,24 +164,13 @@ static void gtk_hex_view_init(GtkHexView *view)
 {
     gtk_widget_init_template(GTK_WIDGET(view));
 
-
-
-
-
-    g_object_set(G_OBJECT(view->offsets), "width-request", 30, NULL);
-
-    g_object_set(G_OBJECT(view->ascii), "width-request", 230, NULL);
-
-
     g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->offsets), demo_snapshot, GTK_WIDGET(view));
 
     g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->hex), demo_snapshot, GTK_WIDGET(view));
 
     g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->ascii), demo_snapshot, GTK_WIDGET(view));
 
-
-
-
+    view->need_vscrolling = true;
 
 }
 
@@ -225,25 +218,11 @@ static void gtk_hex_view_finalize(GtkHexView *view)
 
 
 
-void demo_measure (GtkWidget      *widget,
-              GtkOrientation  orientation,
-              int             for_size,
-              int            *minimum_size,
-              int            *natural_size,
-              int            *minimum_baseline,
-              int            *natural_baseline)
-{
-    printf("set size\n");
-
-    *minimum_size = 100;
-  *natural_size = 200;
-}
-
 
 
 
 
-void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
+void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent)
 {
   GdkRGBA red, green, yellow, blue;
   float w, h;
@@ -280,14 +259,82 @@ void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
 
 
 
-#if 0
+
+
+
+
+
+bool g_buffer_view_allocate_widths(void *ptr, int width, int height, int fill, int *out);
+
+bool g_buffer_view_allocate_widths(void *ptr, int width, int height, int fill, int *out)
+{
+
+    int i;                                  /* Boucle de parcours          */
+
+
+    for (i = 0; i < fill; i++)
+    {
+
+        if (i == 0)
+        {
+            out[0] = 40;
+        }
+
+        else if ((i + 1) == fill)
+        {
+            out[i] = width;
+        }
+
+        else
+        {
+            out[i] = 230;
+        }
+
+
+        width -= out[i];
+
+
+
+    }
+
+    return false;
+
+
+}
+
+int g_buffer_view_measure_height(void *ptr, int width);
+
+int g_buffer_view_measure_height(void *ptr, int width)
+{
+    int result;                             /* Mesure à retourner          */
+
+    if (width == -1)
+        result = 1;
+
+    else
+        result = 5000 / width;
+
+    result *= 16;
+
+    return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
+/* ---------------------------------------------------------------------------------- */
+
+
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : widget  = composant GTK à examiner.                          *
-*                minimum = largeur minimale à préciser ou NULL. [OUT]         *
-*                natural = largeur idéale à préciser ou NULL. [OUT]           *
+*  Paramètres  : widget = composant GTK à examiner.                           *
+*                width  = largeur affectée au composant graphique.            *
+*                height = hauteur affectée au composant graphique.            *
+*                baseline = ligne de base affectée au composant graphique.    *
 *                                                                             *
-*  Description : Fournit la largeur idéale pour le composant d'affichage.     *
+*  Description : Prend acte de la taille allouée au composant d'affichage.    *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -295,82 +342,166 @@ void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
 *                                                                             *
 ******************************************************************************/
 
-static void gtk_hex_view_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)
+static void gtk_hex_view_size_allocate(GtkWidget *widget, int width, int height, int baseline)
 {
-    GtkHexView *panel;                 /* Autre version du composant  */
-    gint req;                               /* Dimension requise           */
+    GtkHexView *view;                       /* Version spécialisée         */
+    int vscroll_width;                      /* Largeur idéale de la barre  */
 
-    panel = GTK_HEX_VIEW(widget);
+    GtkAllocation allocated;                /* Zone allouée                */
+    size_t i;                               /* Boucle de parcours          */
+    int sub_widths[_CHILDREN_COUNT - 1];    /* Sous-largeurs calculées     */
 
-    GTK_HEX_VIEW_GET_CLASS(widget)->compute_size(panel, &req, NULL);
+    view = GTK_HEX_VIEW(widget);
 
-    req *= panel->scale;
+    allocated.y = 0;
+    allocated.height = height;
 
-    if (minimum != NULL) *minimum = req;
-    if (natural != NULL) *natural = req;
+    /* Barre de défilement ? */
 
-}
+    view->need_vscrolling = g_buffer_view_allocate_widths(NULL, width, height, _CHILDREN_COUNT - 1, sub_widths);
+
+    gtk_widget_set_visible(view->vscroll, view->need_vscrolling);
+
+    if (view->need_vscrolling)
+    {
+        gtk_widget_remove_css_class(widget, "without_vscroll");
+        gtk_widget_add_css_class(widget, "with_vscroll");
+    }
+    else
+    {
+        gtk_widget_remove_css_class(widget, "with_vscroll");
+        gtk_widget_add_css_class(widget, "without_vscroll");
+    }
+
+    /**
+     * Validité de la consistence des feuilles CSS : le changement de classe
+     * ne doit pas faire évoluer les tailles.
+     */
+    assert(view->need_vscrolling == g_buffer_view_allocate_widths(NULL, width, height, _CHILDREN_COUNT - 1, sub_widths));
+
+    if (view->need_vscrolling)
+    {
+        gtk_widget_measure(view->vscroll, GTK_ORIENTATION_HORIZONTAL, height, &vscroll_width, NULL, NULL, NULL);
+
+        allocated.x = width - vscroll_width;
+        allocated.width = vscroll_width;
+
+        gtk_widget_size_allocate(view->vscroll, &allocated, baseline);
+
+        width -= vscroll_width;
+
+    }
 
+    /* Placement des composants d'affichage */
 
+    g_buffer_view_allocate_widths(NULL, width, height, _CHILDREN_COUNT - 1, sub_widths);
+
+    allocated.x = 0;
+
+    for (i = 0; i < (_CHILDREN_COUNT - 1); i++)
+    {
+        allocated.width = sub_widths[i];
+
+        gtk_widget_size_allocate(view->children[i], &allocated, baseline);
+
+        allocated.x += sub_widths[i];
+
+    }
+
+}
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : panel = composant GTK à venir consulter.                     *
-*                cr    = contexte graphique associé à l'événement.            *
+*  Paramètres  : widget = composant GTK à consulter.                          *
 *                                                                             *
-*  Description : Dessine si besoin est une bordure autour du composant.       *
+*  Description : Fournit le mode de calcul pour déterminer la taille.         *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Mode de calcul adapté au composant graphique.                *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-void gtk_hex_view_draw_border(GtkHexView *panel, cairo_t *cr)
+static GtkSizeRequestMode gtk_hex_view_get_request_mode(GtkWidget *widget)
 {
-    GtkWidget *widget;                      /* Autre version du composant  */
-    GtkStyleContext *context;               /* Contexte du thème actuel    */
-    GdkRGBA color;                          /* Couleur de thème récupérée  */
-    GtkRequisition req;                     /* Taille allouée à l'élément  */
-    GtkAllocation area;                     /* Emplacement à considérer    */
+    GtkSizeRequestMode result;              /* Configuration à remonter    */
 
-    if (panel->show_border)
-    {
-        widget = GTK_WIDGET(panel);
+    result = GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
 
-        gtk_widget_get_preferred_size(widget, NULL, &req);
+    return result;
 
-        context = gtk_widget_get_style_context(widget);
+}
 
-        gtk_style_context_save(context);
 
-        gtk_style_context_add_class(context, GTK_STYLE_CLASS_FRAME);
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : widget       = composant GTK à examiner.                     *
+*                orientation  = direction à observer pour les calculs.        *
+*                for_size     = taille de la direction opposée.               *
+*                minimum      = taille minimale pour le composant. [OUT]      *
+*                natural      = taille idéale pour le composant. [OUT]        *
+*                min_baseline = ligne de base minimale. [OUT]                 *
+*                nat_baseline = ligne de base idéale. [OUT]                   *
+*                                                                             *
+*  Description : Fournit les mesures mainimale et idéale du composant.        *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void gtk_hex_view_measure(GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *min_baseline, int *nat_baseline)
+{
+    GtkHexView *view;                       /* Version spécialisée         */
+    int requested;                          /* Taille requise à priori     */
+    size_t i;                               /* Boucle de parcours          */
+    int min;                                /* Valeur minimale locale      */
+    int nat;                                /* Valeur idéale locale        */
 
-        gtk_style_context_get(gtk_widget_get_style_context(widget),
-                              gtk_widget_get_state_flags(widget),
-                              GTK_STYLE_PROPERTY_COLOR, &color, NULL);
+    view = GTK_HEX_VIEW(widget);
 
-        cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha);
+    /* Demande de hauteur minimale / idéale */
+    if (orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        requested = g_buffer_view_measure_height(NULL, for_size);
 
-        cairo_set_line_width(cr, 1.0);
+        if (minimum != NULL) *minimum = requested;
+        if (natural != NULL) *natural = requested;
 
-        gtk_widget_get_preferred_size(GTK_WIDGET(panel), NULL, &req);
+        for (i = 0; i < _CHILDREN_COUNT; i++)
+        {
+            gtk_widget_measure(view->children[i], GTK_ORIENTATION_VERTICAL, -1, &min, &nat, NULL, NULL);
 
-        area.x = 0;
-        area.y = 0;
-        area.width = req.width;
-        area.height = req.height;
+            if (minimum != NULL && min > *minimum)
+                *minimum = min;
 
-        gtk_hex_view_define_border_path(panel, cr, &area);
-        cairo_stroke(cr);
+            if (natural != NULL && nat > *natural)
+                *natural = nat;
 
-        gtk_style_context_restore(context);
+        }
 
     }
 
-}
+    /* Demande de largeur minimale / idéale */
+    else
+    {
+        if (minimum != NULL) *minimum = 0;
+        if (natural != NULL) *natural = 0;
 
+        for (i = 0; i < _CHILDREN_COUNT; i++)
+        {
+            gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat, NULL, NULL);
 
+            if (minimum != NULL) *minimum += min;
+            if (natural != NULL) *natural += nat;
 
-#endif
+        }
+
+    }
+
+    if (min_baseline != NULL) *min_baseline = -1;
+    if (nat_baseline != NULL) *nat_baseline = -1;
+
+}
diff --git a/src/gtkext/hexview.css b/src/gtkext/hexview.css
index fdde1cc..9725509 100644
--- a/src/gtkext/hexview.css
+++ b/src/gtkext/hexview.css
@@ -17,13 +17,19 @@ GtkHexView > :not(scrollbar) {
 
 }
 
-GtkHexView > :nth-last-child(2) {
+/* ASCII */
+
+GtkHexView.with_vscroll > :nth-last-child(2),
+GtkHexView.without_vscroll > :nth-last-child(1) {
 
     padding-right: 4px;
 
 }
 
-GtkHexView > :nth-last-child(4) {
+/* Offset */
+
+GtkHexView.with_vscroll > :nth-last-child(4),
+GtkHexView.without_vscroll > :nth-last-child(3) {
 
     border-right: 1px solid @borders;
 
diff --git a/src/gtkext/hexview.ui b/src/gtkext/hexview.ui
index f0dd8b0..d2f6849 100644
--- a/src/gtkext/hexview.ui
+++ b/src/gtkext/hexview.ui
@@ -1,6 +1,6 @@
 <interface>
   <template class="GtkHexView" parent="GtkWidget">
-        <property name="css-name">GtkHexView</property>
+    <property name="css-name">GtkHexView</property>
     <child>
       <object class="GtkComposingArea" id="offsets">
         <style>
-- 
cgit v0.11.2-87-g4458