]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0587: GTK4: left scrollbar overlaps drawarea v9.2.0587
authorFoxe Chen <chen.foxe@gmail.com>
Tue, 2 Jun 2026 17:44:42 +0000 (17:44 +0000)
committerChristian Brabandt <cb@256bit.org>
Tue, 2 Jun 2026 17:44:42 +0000 (17:44 +0000)
Problem:  GTK4: the drawarea is wrapped in a GtkOverlay with the
          scrollbar form layered on top, so the left scrollbar
          appears over the drawarea instead of beside it.
Solution: Place the drawarea and the scrollbars as siblings inside
          the form widget, removing the GtkOverlay.  Add
          gui_mch_update_scrollbar_size() to query the actual
          scrollbar dimensions and call it from gui_set_shellsize(),
          rename GtkForm to VimForm so the GTK namespace is not used
          (Foxe Chen).

closes: #20375

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/gui.c
src/gui_gtk4.c
src/gui_gtk4_f.c
src/gui_gtk4_f.h
src/proto/gui_gtk4.pro
src/version.c

index b0235eec28aa270c1dd86302cca29c6575f28359..2b5c0f24a7fc6e17ebd39f2ccd6da2064b6973a8 100644 (file)
--- a/src/gui.c
+++ b/src/gui.c
@@ -1703,6 +1703,11 @@ gui_set_shellsize(
     if (!gui.shell_created)
        return;
 
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4)
+    // Get the scrollbar width + height if possible
+    gui_mch_update_scrollbar_size();
+#endif
+
 #if defined(MSWIN) || defined(FEAT_GUI_GTK)
     // If not setting to a user specified size and maximized, calculate the
     // number of characters that fit in the maximized window.
index d6d32b7ccc986c8341c2d686f003ff7697dd1aae..20390879afab72bbe4c458cbc55a5d2f89055b96 100644 (file)
@@ -499,30 +499,21 @@ gui_mch_init(void)
                     G_CALLBACK(on_tab_reordered), NULL);
 #endif
 
-    // The form widget manages absolute positioning of scrollbars.
-    gui.formwin = gui_gtk_form_new();
+    // The form widget manages absolute positioning of scrollbars and the draw
+    // area.
+    gui.formwin = vim_form_new();
     gtk_widget_set_name(gui.formwin, "vim-gtk-form");
-    // formwin is overlaid on top of drawarea for scrollbar positioning.
-    // GtkForm's contains() returns FALSE so empty-area clicks fall through
-    // to the drawarea, while the scrollbar children still receive events.
+    gtk_widget_set_vexpand(gui.formwin, TRUE);
+    gtk_widget_set_hexpand(gui.formwin, TRUE);
+    gtk_box_append(GTK_BOX(vbox), gui.formwin);
 
     // The drawing area for the editor content.
-    // Placed in an overlay so it fills the formwin, with scrollbars on top.
     gui.drawarea = gtk_drawing_area_new();
     gui.surface = NULL;
     gtk_widget_set_focusable(gui.drawarea, TRUE);
     gtk_widget_set_vexpand(gui.drawarea, TRUE);
     gtk_widget_set_hexpand(gui.drawarea, TRUE);
-
-    {
-       // Use GtkOverlay: drawarea as the main child, formwin as overlay
-       GtkWidget *overlay = gtk_overlay_new();
-       gtk_overlay_set_child(GTK_OVERLAY(overlay), gui.drawarea);
-       gtk_overlay_add_overlay(GTK_OVERLAY(overlay), gui.formwin);
-       gtk_widget_set_vexpand(overlay, TRUE);
-       gtk_widget_set_hexpand(overlay, TRUE);
-       gtk_box_append(GTK_BOX(vbox), overlay);
-    }
+    vim_form_put(VIM_FORM(gui.formwin), gui.drawarea, 0, 0);
 
     // Set up drawing.
     gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gui.drawarea),
@@ -3007,8 +2998,8 @@ gui_gtk_draw_string_ext(
        column_offset = len;
     }
     else
-not_ascii:
     {
+not_ascii:;
        PangoAttrList   *attr_list;
        GList           *item_list;
        int             cluster_width;
@@ -4269,10 +4260,7 @@ gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max)
 gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h)
 {
     if (sb->id != NULL)
-    {
-       gtk_widget_set_size_request(sb->id, w, h);
-       gui_gtk_form_move(GTK_FORM(gui.formwin), sb->id, x, y);
-    }
+       vim_form_move_resize(VIM_FORM(gui.formwin), sb->id, x, y, w, h);
 }
 
     int
@@ -4317,24 +4305,21 @@ adjustment_value_changed(GtkAdjustment *adj, gpointer data UNUSED)
     void
 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
 {
+    GtkAdjustment *adj;
     if (orient == SBAR_HORIZ)
        sb->id = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
     else
        sb->id = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
 
-    if (sb->id != NULL && GTK_IS_SCROLLBAR(sb->id))
-    {
-       GtkAdjustment *adj = gtk_scrollbar_get_adjustment(GTK_SCROLLBAR(sb->id));
+    gtk_widget_add_css_class(sb->id, "vim-scrollbar");
+    adj = gtk_scrollbar_get_adjustment(GTK_SCROLLBAR(sb->id));
 
-       gtk_widget_set_visible(sb->id, FALSE);
-       gui_gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
-       if (adj != NULL && G_IS_OBJECT(adj))
-       {
-           g_object_set_data(G_OBJECT(adj), "vim-sb", (gpointer)sb);
-           g_signal_connect(G_OBJECT(adj), "value-changed",
-                   G_CALLBACK(adjustment_value_changed), NULL);
-       }
-    }
+    gtk_widget_set_visible(sb->id, FALSE);
+    vim_form_put(VIM_FORM(gui.formwin), sb->id, 0, 0);
+
+    g_object_set_data(G_OBJECT(adj), "vim-sb", (gpointer)sb);
+    g_signal_connect(G_OBJECT(adj), "value-changed",
+           G_CALLBACK(adjustment_value_changed), NULL);
 }
 
     void
@@ -4342,11 +4327,68 @@ gui_mch_destroy_scrollbar(scrollbar_T *sb)
 {
     if (sb->id != NULL)
     {
-       gui_gtk_form_remove(GTK_FORM(gui.formwin), sb->id);
+       vim_form_remove(VIM_FORM(gui.formwin), sb->id);
        sb->id = NULL;
     }
 }
 
+/*
+ * Try getting the actual size of the scrollbar, and update gui.scrollbar_width
+ * and gui.scrollbar_height.
+ */
+    void
+gui_mch_update_scrollbar_size(void)
+{
+    win_T      *wp;
+    int                w = -1, h = -1;
+    GtkWidget  *sbar;
+
+    FOR_ALL_WINDOWS(wp)
+    {
+       sbar = wp->w_scrollbars[SBAR_LEFT].id;
+
+       if (sbar == NULL || !gtk_widget_get_visible(sbar)
+               || (!gui.which_scrollbars[SBAR_LEFT]
+                   && wp->w_scrollbars[SBAR_RIGHT].id != NULL))
+           sbar = wp->w_scrollbars[SBAR_RIGHT].id;
+
+       if (sbar != NULL && gtk_widget_get_visible(sbar))
+       {
+           GtkRequisition  min, nat;
+           int             sw;
+
+           // Use preferred size, since widget may not have its size allocated
+           // yet.
+           gtk_widget_get_preferred_size(sbar, &min, &nat);
+           sw = MAX(min.width, nat.width);
+           if (sw > 0)
+           {
+               w = sw;
+               break;
+           }
+       }
+
+    }
+
+    sbar = gui.bottom_sbar.id;
+    if (sbar != NULL && gtk_widget_get_visible(sbar))
+    {
+       GtkRequisition min, nat;
+       int                 sh;
+
+       gtk_widget_get_preferred_size(sbar, &min, &nat);
+       sh = MAX(min.height, nat.height);
+
+       if (sh > 0)
+           h = sh;
+    }
+
+    if (w != -1)
+       gui.scrollbar_width = w;
+    if (h != -1)
+       gui.scrollbar_height = h;
+}
+
 /*
  * ============================================================
  * Text area position
@@ -4358,11 +4400,11 @@ gui_mch_set_text_area_pos(int x, int y, int w, int h)
 {
     last_text_area_w = w;
     last_text_area_h = h;
-    // Don't use gui_gtk_form_move_resize for drawarea because its
+    // Don't use vim_form_move_resize for drawarea because its
     // set_size_request would prevent the window from shrinking.
     // Just update position; the actual allocation is handled by
-    // form_size_allocate which gives drawarea the formwin's full size.
-    gui_gtk_form_move(GTK_FORM(gui.formwin), gui.drawarea, x, y);
+    // vim_form_size_allocate which gives drawarea the formwin's full size.
+    vim_form_move(VIM_FORM(gui.formwin), gui.drawarea, x, y);
 
     // Surface sizing is owned by drawarea_resize_cb; don't recreate it
     // here. Recreating on every text-area change wiped any preserved
@@ -4704,7 +4746,7 @@ find_replace_dialog_create(char_u *arg, int do_replace)
     char_u             *entry_text;
     int                        wword = FALSE;
     int                        mcase = !p_ic;
-    GtkWidget          *vbox, *grid, *hbox, *tmp, *btn;
+    GtkWidget          *vertbox, *grid, *hbox, *tmp, *btn;
     gboolean           sensitive;
 
     frdp = do_replace ? &repl_widgets : &find_widgets;
@@ -4748,18 +4790,18 @@ find_replace_dialog_create(char_u *arg, int do_replace)
     g_signal_connect(frdp->dialog, "destroy",
            G_CALLBACK(dialog_destroyed_cb), &frdp->dialog);
 
-    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
-    gtk_widget_set_margin_start(vbox, 12);
-    gtk_widget_set_margin_end(vbox, 12);
-    gtk_widget_set_margin_top(vbox, 12);
-    gtk_widget_set_margin_bottom(vbox, 12);
-    gtk_window_set_child(GTK_WINDOW(frdp->dialog), vbox);
+    vertbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+    gtk_widget_set_margin_start(vertbox, 12);
+    gtk_widget_set_margin_end(vertbox, 12);
+    gtk_widget_set_margin_top(vertbox, 12);
+    gtk_widget_set_margin_bottom(vertbox, 12);
+    gtk_window_set_child(GTK_WINDOW(frdp->dialog), vertbox);
 
     // Grid for labels + entries
     grid = gtk_grid_new();
     gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
     gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
-    gtk_box_append(GTK_BOX(vbox), grid);
+    gtk_box_append(GTK_BOX(vertbox), grid);
 
     // "Find what:" label + entry
     tmp = gtk_label_new(_("Find what:"));
@@ -4787,7 +4829,7 @@ find_replace_dialog_create(char_u *arg, int do_replace)
 
     // Checkboxes
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
-    gtk_box_append(GTK_BOX(vbox), hbox);
+    gtk_box_append(GTK_BOX(vertbox), hbox);
 
     frdp->wword = gtk_check_button_new_with_label(_("Match whole word only"));
     gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->wword),
@@ -4801,7 +4843,7 @@ find_replace_dialog_create(char_u *arg, int do_replace)
 
     // Direction radio buttons
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
-    gtk_box_append(GTK_BOX(vbox), hbox);
+    gtk_box_append(GTK_BOX(vertbox), hbox);
 
     tmp = gtk_label_new(_("Direction:"));
     gtk_box_append(GTK_BOX(hbox), tmp);
@@ -4818,7 +4860,7 @@ find_replace_dialog_create(char_u *arg, int do_replace)
     // Action buttons
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
     gtk_widget_set_halign(hbox, GTK_ALIGN_END);
-    gtk_box_append(GTK_BOX(vbox), hbox);
+    gtk_box_append(GTK_BOX(vertbox), hbox);
 
     btn = gtk_button_new_with_label(_("Find Next"));
     gtk_widget_set_sensitive(btn, sensitive);
index 80aa7a48d005709433661c7da515b99772d3e72c..14ff85f625347b3626cb7345ca2890bf3ca9b98c 100644 (file)
  * Do ":help uganda"  in Vim to read copying and usage conditions.
  * Do ":help credits" in Vim to see a list of people who contributed.
  * See README.txt for an overview of the Vim source code.
- *
- * GTK4 GtkForm widget - a simple container for absolute child positioning.
- * This is a clean rewrite of gui_gtk_f.c for GTK4.
- *
- * In GTK4, widgets no longer have their own GdkWindows (now GdkSurface),
- * GtkContainer is removed, and child positioning uses GskTransform via
- * gtk_widget_allocate().  This makes the form widget much simpler.
  */
 
 #include "vim.h"
 #include <gtk/gtk.h>
 #include "gui_gtk4_f.h"
 
-typedef struct _GtkFormChild GtkFormChild;
-
-struct _GtkFormChild
+/*
+ * Child widget at position (x, y).
+ */
+typedef struct
 {
-    GtkWidget *widget;
-    gint x;
-    gint y;
+    GtkWidget  *widget;
+    int                x;
+    int                y;
+} VimFormChild;
+
+/*
+ * Similar to the GtkFixed widget, allows absolute position and sizing of child
+ * widgets within. Vim already has logic for positioning and sizing UI elements,
+ * so this is needed to take advantage of that. We don't use GtkFixed directly
+ * since we need to override some vfuncs.
+ */
+struct _VimForm
+{
+    GtkWidget parent;
+
+    GList *children;
+
+    // See vim_form_size_allocate()
+    guint resize_idle_id;
+    int last_width;
+    int last_height;
 };
 
-// Forward declarations
-static void gui_gtk_form_class_init(GtkFormClass *klass);
-static void gui_gtk_form_init(GtkForm *form);
-static void form_measure(GtkWidget *widget, GtkOrientation orientation,
-       int for_size, int *minimum, int *natural,
-       int *minimum_baseline, int *natural_baseline);
-static void form_size_allocate(GtkWidget *widget, int width, int height,
-       int baseline);
-static void form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot);
-static gboolean form_contains(GtkWidget *widget, double x, double y);
-static void form_dispose(GObject *object);
-static void form_position_child(GtkForm *form, GtkFormChild *child,
-       gboolean force_allocate);
+G_DEFINE_TYPE(VimForm, vim_form, GTK_TYPE_WIDGET)
 
-G_DEFINE_TYPE(GtkForm, gui_gtk_form, GTK_TYPE_WIDGET)
+static void vim_form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot);
+static void vim_form_size_allocate(GtkWidget *widget, int width, int height, int baseline);
+static void vim_form_measure(GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline);
+static gboolean vim_form_contains(GtkWidget *widget, double x, double y);
 
-// Public interface
+    static void
+vim_form_dispose(GObject *obj)
+{
+    VimForm *self = VIM_FORM(obj);
+    GList   *ele = self->children;
+
+    while (ele != NULL)
+    {
+       VimFormChild *child = ele->data;
+
+       ele = ele->next;
+       gtk_widget_unparent(child->widget);
+       g_free(child);
+    }
+    g_list_free(self->children);
+    self->children = NULL;
+
+    G_OBJECT_CLASS(vim_form_parent_class)->dispose(obj);
+}
+
+    static void
+vim_form_class_init(VimFormClass *class)
+{
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
+    GObjectClass    *obj_class = G_OBJECT_CLASS(class);
+
+    widget_class->snapshot = vim_form_snapshot;
+    widget_class->size_allocate = vim_form_size_allocate;
+    widget_class->measure = vim_form_measure;
+    widget_class->contains = vim_form_contains;
+
+    obj_class->dispose = vim_form_dispose;
+}
+
+    static void
+vim_form_init(VimForm *self)
+{
+
+}
 
     GtkWidget *
-gui_gtk_form_new(void)
+vim_form_new(void)
 {
-    return GTK_WIDGET(g_object_new(GTK_TYPE_FORM, NULL));
+    return g_object_new(VIM_TYPE_FORM, NULL);
 }
 
-    void
-gui_gtk_form_put(
-       GtkForm     *form,
-       GtkWidget   *child_widget,
-       gint        x,
-       gint        y)
+/*
+ * Transform the child
+ */
+    static void
+vim_form_position_child(VimForm        *self, VimFormChild *child)
 {
-    GtkFormChild *child;
+    GtkRequisition  requisition;
+    GskTransform    *transform;
+    int                    w, h;
+
+    gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
+    w = requisition.width;
+    h = requisition.height;
+
+    // If widget has no size request, use parent size
+    if (w <= 0)
+       w = gtk_widget_get_width(GTK_WIDGET(self));
+    if (h <= 0)
+       h = gtk_widget_get_height(GTK_WIDGET(self));
+    if (w <= 0) w = 1;
+    if (h <= 0) h = 1;
+
+    transform = gsk_transform_translate(NULL,
+           &GRAPHENE_POINT_INIT((float)child->x, (float)child->y));
+    gtk_widget_allocate(child->widget, w, h, -1, transform);
+}
 
-    g_return_if_fail(GTK_IS_FORM(form));
+/*
+ * Place the given widget at the point (x, y).
+ */
+    void
+vim_form_put(VimForm *self, GtkWidget *widget, int x, int y)
+{
+    VimFormChild *child;
 
-    child = g_new(GtkFormChild, 1);
-    if (child == NULL)
-       return;
+    child = g_new(VimFormChild, 1);
 
-    child->widget = child_widget;
+    child->widget = widget;
     child->x = x;
     child->y = y;
 
     gtk_widget_set_size_request(child->widget, -1, -1);
 
-    form->children = g_list_append(form->children, child);
+    self->children = g_list_append(self->children, child);
 
-    gtk_widget_set_parent(child_widget, GTK_WIDGET(form));
-    form_position_child(form, child, TRUE);
+    gtk_widget_set_parent(widget, GTK_WIDGET(self));
+    vim_form_position_child(self, child);
 }
 
+/*
+ * Move the widget (which should have already been added using vim_form_put())
+ * to the given point (x, y).
+ */
     void
-gui_gtk_form_move(
-       GtkForm     *form,
-       GtkWidget   *child_widget,
-       gint        x,
-       gint        y)
+vim_form_move(VimForm *self, GtkWidget *widget, int x, int y)
 {
-    GList *tmp_list;
-
-    g_return_if_fail(GTK_IS_FORM(form));
-
-    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
+    for (GList *ele = self->children; ele != NULL; ele = ele->next)
     {
-       GtkFormChild *child = tmp_list->data;
-       if (child->widget == child_widget)
+       VimFormChild *child = ele->data;
+       if (child->widget == widget)
        {
            child->x = x;
            child->y = y;
-           form_position_child(form, child, TRUE);
+           vim_form_position_child(self, child);
            return;
        }
     }
 }
 
+/*
+ * Move and resize the child.
+ */
     void
-gui_gtk_form_move_resize(
-       GtkForm     *form,
+vim_form_move_resize(
+       VimForm     *self,
        GtkWidget   *widget,
        gint        x,
        gint        y,
@@ -112,223 +174,106 @@ gui_gtk_form_move_resize(
        gint        h)
 {
     gtk_widget_set_size_request(widget, w, h);
-    gui_gtk_form_move(form, widget, x, y);
+    vim_form_move(self, widget, x, y);
 }
 
     void
-gui_gtk_form_remove(GtkForm *form, GtkWidget *child_widget)
+vim_form_remove(VimForm *self, GtkWidget *widget)
 {
-    GList *tmp_list;
-
-    g_return_if_fail(GTK_IS_FORM(form));
-
-    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
+    for (GList *ele = self->children; ele != NULL; ele = ele->next)
     {
-       GtkFormChild *child = tmp_list->data;
-       if (child->widget == child_widget)
+       VimFormChild *child = ele->data;
+       if (child->widget == widget)
        {
-           form->children = g_list_remove_link(form->children, tmp_list);
-           g_list_free_1(tmp_list);
-           gtk_widget_unparent(child_widget);
+           self->children = g_list_remove_link(self->children, ele);
+           g_list_free_1(ele);
+           gtk_widget_unparent(widget);
            g_free(child);
            return;
        }
     }
 }
 
-    void
-gui_gtk_form_freeze(GtkForm *form)
-{
-    g_return_if_fail(GTK_IS_FORM(form));
-    ++form->freeze_count;
-}
-
-    void
-gui_gtk_form_thaw(GtkForm *form)
+    static void
+vim_form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
 {
-    g_return_if_fail(GTK_IS_FORM(form));
+    VimForm *self = VIM_FORM(widget);
 
-    if (!form->freeze_count)
-       return;
-
-    if (!(--form->freeze_count))
+    for (GList *ele = self->children; ele != NULL; ele = ele->next)
     {
-       GList *tmp_list;
-
-       for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
-           form_position_child(form, tmp_list->data, FALSE);
-       gtk_widget_queue_draw(GTK_WIDGET(form));
+       VimFormChild *child = ele->data;
+       gtk_widget_snapshot_child(widget, child->widget, snapshot);
     }
 }
 
-// GObject/GtkWidget class implementation
-
-    static void
-gui_gtk_form_class_init(GtkFormClass *klass)
-{
-    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
-
-    gobject_class->dispose = form_dispose;
-
-    widget_class->measure = form_measure;
-    widget_class->size_allocate = form_size_allocate;
-    widget_class->snapshot = form_snapshot;
-    widget_class->contains = form_contains;
-}
-
-    static void
-gui_gtk_form_init(GtkForm *form)
-{
-    form->children = NULL;
-    form->freeze_count = 0;
-}
-
-    static void
-form_measure(
-       GtkWidget       *widget UNUSED,
-       GtkOrientation  orientation UNUSED,
-       int             for_size UNUSED,
-       int             *minimum,
-       int             *natural,
-       int             *minimum_baseline,
-       int             *natural_baseline)
-{
-    *minimum = 1;
-    *natural = 1;
-    *minimum_baseline = -1;
-    *natural_baseline = -1;
-}
-
-static guint form_resize_idle_id = 0;
-static int form_last_width = 0;
-static int form_last_height = 0;
-
     static gboolean
-form_resize_idle_cb(gpointer data UNUSED)
+vim_form_resize_idle_cb(VimForm *self)
 {
     int w, h;
 
-    form_resize_idle_id = 0;
+    self->resize_idle_id = 0;
 
     // Use drawarea's actual allocation, not formwin's
     if (gui.drawarea == NULL)
-       return FALSE;
+       goto exit;
     w = gtk_widget_get_width(gui.drawarea);
     h = gtk_widget_get_height(gui.drawarea);
 
     if (w > 1 && h > 1)
        gui_resize_shell(w, h);
 
-    return FALSE;
+exit:
+    g_object_unref(self);
+    return G_SOURCE_REMOVE;
 }
 
     static void
-form_size_allocate(GtkWidget *widget, int width, int height,
-       int baseline UNUSED)
+vim_form_size_allocate(
+       GtkWidget *widget,
+       int width,
+       int height,
+       int baseline)
 {
-    GtkForm *form;
-    GList *tmp_list;
+    VimForm *self = VIM_FORM(widget);
 
-    g_return_if_fail(GTK_IS_FORM(widget));
-
-    form = GTK_FORM(widget);
-
-    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
-       form_position_child(form, tmp_list->data, TRUE);
+    for (GList *ele = self->children; ele != NULL; ele = ele->next)
+       vim_form_position_child(self, ele->data);
 
     // Notify Vim about size change via idle callback
-    if (width != form_last_width || height != form_last_height)
+    if (width != self->last_width || height != self->last_height)
     {
-       form_last_width = width;
-       form_last_height = height;
-       if (form_resize_idle_id == 0)
-           form_resize_idle_id = g_idle_add(form_resize_idle_cb, NULL);
-    }
-}
+       self->last_width = width;
+       self->last_height = height;
 
-    static void
-form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
-{
-    GtkForm *form;
-    GList *tmp_list;
-
-    g_return_if_fail(GTK_IS_FORM(widget));
-
-    form = GTK_FORM(widget);
-
-    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
-    {
-       GtkFormChild *child = tmp_list->data;
-       if (child->widget != NULL
-               && GTK_IS_WIDGET(child->widget)
-               && gtk_widget_get_parent(child->widget) == widget)
-           gtk_widget_snapshot_child(widget, child->widget, snapshot);
+       if (self->resize_idle_id == 0)
+           self->resize_idle_id = g_idle_add(
+                   (GSourceFunc)vim_form_resize_idle_cb, g_object_ref(self));
     }
 }
 
-// Make the form itself input-transparent so clicks on its empty area fall
-// through to the drawarea below, while the scrollbar children stay pickable.
-    static gboolean
-form_contains(GtkWidget *widget UNUSED, double x UNUSED, double y UNUSED)
-{
-    return FALSE;
-}
-
     static void
-form_dispose(GObject *object)
+vim_form_measure(
+       GtkWidget       *widget UNUSED,
+       GtkOrientation  orientation UNUSED,
+       int             for_size UNUSED,
+       int             *minimum,
+       int             *natural,
+       int             *minimum_baseline,
+       int             *natural_baseline)
 {
-    GtkForm *form = GTK_FORM(object);
-    GList *tmp_list;
-
-    tmp_list = form->children;
-    while (tmp_list)
-    {
-       GtkFormChild *child = tmp_list->data;
-       tmp_list = tmp_list->next;
-
-       gtk_widget_unparent(child->widget);
-       g_free(child);
-    }
-    g_list_free(form->children);
-    form->children = NULL;
-
-    G_OBJECT_CLASS(gui_gtk_form_parent_class)->dispose(object);
+    *minimum = 1;
+    *natural = 1;
+    *minimum_baseline = -1;
+    *natural_baseline = -1;
 }
 
-// Child positioning using GskTransform
 
-    static void
-form_position_child(
-       GtkForm         *form UNUSED,
-       GtkFormChild    *child,
-       gboolean        force_allocate)
+/*
+ * Make the form itself input-transparent so clicks on its empty area fall
+ * through to the drawarea below, while the scrollbar children stay pickable.
+ */
+    static gboolean
+vim_form_contains(GtkWidget *widget UNUSED, double x UNUSED, double y UNUSED)
 {
-    if (!force_allocate)
-       return;
-
-    if (child->widget == NULL || !GTK_IS_WIDGET(child->widget))
-       return;
-
-    {
-       GtkRequisition requisition;
-       GskTransform *transform;
-       int w, h;
-
-       gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
-       w = requisition.width;
-       h = requisition.height;
-
-       // If widget has no size request (e.g. drawarea), use parent size
-       if (w <= 0)
-           w = gtk_widget_get_width(GTK_WIDGET(form));
-       if (h <= 0)
-           h = gtk_widget_get_height(GTK_WIDGET(form));
-       if (w <= 0) w = 1;
-       if (h <= 0) h = 1;
-
-       transform = gsk_transform_translate(NULL,
-               &GRAPHENE_POINT_INIT((float)child->x, (float)child->y));
-       gtk_widget_allocate(child->widget, w, h, -1, transform);
-    }
+    return FALSE;
 }
index a9270616086fc4e2c74cad3a71e15de555e44af6..d795b86c2e7fdd117329258fd5dc81884b3047d6 100644 (file)
@@ -4,8 +4,7 @@
  *
  * Do ":help uganda"  in Vim to read copying and usage conditions.
  * Do ":help credits" in Vim to see a list of people who contributed.
- *
- * GTK4 GtkForm widget - a simple container for absolute positioning.
+ * See README.txt for an overview of the Vim source code.
  */
 
 #ifndef GUI_GTK4_FORM_H
 
 #include <gtk/gtk.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GTK_TYPE_FORM          (gui_gtk_form_get_type())
-#define GTK_FORM(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_FORM, GtkForm))
-#define GTK_FORM_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_FORM, GtkFormClass))
-#define GTK_IS_FORM(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_FORM))
-
-typedef struct _GtkForm GtkForm;
-typedef struct _GtkFormClass GtkFormClass;
-
-struct _GtkForm
-{
-    GtkWidget widget;
-    GList *children;
-    gint freeze_count;
-};
-
-struct _GtkFormClass
-{
-    GtkWidgetClass parent_class;
-};
-
-GType gui_gtk_form_get_type(void);
-
-GtkWidget *gui_gtk_form_new(void);
-
-void gui_gtk_form_put(GtkForm *form, GtkWidget *widget, gint x, gint y);
-
-void gui_gtk_form_move(GtkForm *form, GtkWidget *widget, gint x, gint y);
-
-void gui_gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
-       gint x, gint y, gint w, gint h);
-
-void gui_gtk_form_remove(GtkForm *form, GtkWidget *widget);
+#define VIM_TYPE_FORM (vim_form_get_type())
+G_DECLARE_FINAL_TYPE(VimForm, vim_form, VIM, FORM, GtkWidget)
 
-void gui_gtk_form_freeze(GtkForm *form);
-void gui_gtk_form_thaw(GtkForm *form);
+GtkWidget *vim_form_new(void);
+void vim_form_put(VimForm *self, GtkWidget *widget, int x, int y);
+void vim_form_move(VimForm *self, GtkWidget *widget, int x, int y);
+void vim_form_move_resize(VimForm *self, GtkWidget *widget, gint x, gint y, gint w, gint h);
+void vim_form_remove(VimForm *self, GtkWidget *widget);
 
-#ifdef __cplusplus
-}
 #endif
-#endif // GUI_GTK4_FORM_H
index 4d21e6a7032daa45138f3b5015878ff04a1fdb87..0a1ba1296c93b765b97d1f13ffcfbec8e7952aa8 100644 (file)
@@ -99,6 +99,7 @@ int gui_mch_get_scrollbar_xpadding(void);
 int gui_mch_get_scrollbar_ypadding(void);
 void gui_mch_create_scrollbar(scrollbar_T *sb, int orient);
 void gui_mch_destroy_scrollbar(scrollbar_T *sb);
+void gui_mch_update_scrollbar_size(void);
 void gui_mch_set_text_area_pos(int x, int y, int w, int h);
 char_u *gui_mch_browse(int saving, char_u *title, char_u *dflt, char_u *ext, char_u *initdir, char_u *filter);
 char_u *gui_mch_browsedir(char_u *title, char_u *initdir);
index e020aedc51f8b859f4d85804eef733862a7e0c57..676c515f9599ed060981cc2e99e695a552d724b1 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    587,
 /**/
     586,
 /**/