patch 9.2.0669: GTK4: toolbar can be improved

Commit: 
https://github.com/vim/vim/commit/028314a8deced7dbb20aba569eaea7a36c47d050
Author: Foxe Chen <[email protected]>
Date:   Wed Jun 17 19:53:57 2026 +0000

    patch 9.2.0669: GTK4: toolbar can be improved
    
    Problem:  GTK4: toolbar can be improved
    Solution: Implement gui_mch_menu_set_tip(), make the UI respect the
              'toolbar' option (Foxe Chen).
    
    closes: #20541
    
    Signed-off-by: Foxe Chen <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/Filelist b/Filelist
index 5526e539a..1f36de4d4 100644
--- a/Filelist
+++ b/Filelist
@@ -515,6 +515,8 @@ SRC_UNIX =  \
                src/gui_gtk4_cb.h \
                src/gui_gtk4_da.c \
                src/gui_gtk4_da.h \
+               src/gui_gtk4_tb.c \
+               src/gui_gtk4_tb.h \
                src/gui_gtk_res.xml \
                src/gui_motif.c \
                src/gui_xmdlg.c \
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 244a7510f..9743dea43 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 Jun 09
+*options.txt*  For Vim version 9.2.  Last change: 2026 Jun 17
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -9697,7 +9697,8 @@ A jump table for the options with a short description can 
be found at |Q_op|.
                icons           Toolbar buttons are shown with icons.
                text            Toolbar buttons shown with text.
                horiz           Icon and text of a toolbar button are
-                               horizontally arranged.  {only in GTK+ 2 GUI}
+                               horizontally arranged.
+                               {only in GTK+ 2 and GTK 4 GUI}
                tooltips        Tooltips are active for toolbar buttons.
        Tooltips refer to the popup help text which appears after the mouse
        cursor is placed over a toolbar button for a brief moment.
diff --git a/src/Makefile b/src/Makefile
index a5f23612c..ef0cb8553 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1243,12 +1243,14 @@ GTK4_SRC        = gui.c gui_gtk4.c gui_gtk4_f.c \
                        gui_gtk4_cb.c \
                        gui_gtk4_da.c \
                        gui_beval.o \
+                       gui_gtk4_tb.c \
                        $(GRESOURCE_SRC)
 GTK4_OBJ       = objects/gui.o objects/gui_gtk4.o \
                        objects/gui_gtk4_f.o \
                        objects/gui_gtk4_cb.o \
                        objects/gui_gtk4_da.o \
                        objects/gui_beval.o \
+                       objects/gui_gtk4_tb.o \
                        $(GRESOURCE_OBJ)
 GTK4_DEFS      = -DFEAT_GUI_GTK $(NARROW_PROTO)
 GTK4_IPATH     = $(GUI_INC_LOC)
@@ -1318,7 +1320,7 @@ HAIKUGUI_TESTTARGET = gui
 HAIKUGUI_BUNDLE =
 
 # All GUI files
-ALL_GUI_SRC  = gui.c gui_gtk.c gui_gtk_f.c gui_gtk4.c gui_gtk4_f.c 
gui_gtk4_cb.c gui_gtk4_da.c gui_motif.c gui_xmdlg.c gui_xmebw.c gui_gtk_x11.c 
gui_x11.c gui_haiku.cc
+ALL_GUI_SRC  = gui.c gui_gtk.c gui_gtk_f.c gui_gtk4.c gui_gtk4_f.c 
gui_gtk4_cb.c gui_gtk4_da.c gui_gtk4_tb.c gui_motif.c gui_xmdlg.c gui_xmebw.c 
gui_gtk_x11.c gui_x11.c gui_haiku.cc
 ALL_GUI_PRO  = proto/gui.pro proto/gui_gtk.pro proto/gui_gtk4.pro 
proto/gui_motif.pro proto/gui_xmdlg.pro proto/gui_gtk_x11.pro proto/gui_x11.pro 
proto/gui_w32.pro proto/gui_photon.pro
 
 # }}}
@@ -3416,6 +3418,9 @@ objects/gui_gtk4_cb.o: gui_gtk4_cb.c
 objects/gui_gtk4_da.o: gui_gtk4_da.c
        $(CCC) -o $@ gui_gtk4_da.c
 
+objects/gui_gtk4_tb.o: gui_gtk4_tb.c
+       $(CCC) -o $@ gui_gtk4_tb.c
+
 
 objects/gui_haiku.o: gui_haiku.cc
        $(CCC) -o $@ gui_haiku.cc
@@ -4513,7 +4518,7 @@ objects/gui_gtk4.o: auto/osdef.h gui_gtk4.c vim.h 
protodef.h auto/config.h featu
  structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
  globals.h errors.h gui_gtk4_f.h auto/gui_gtk_gresources.h \
- gui_gtk4_da.h
+ gui_gtk4_cb.h gui_gtk4_da.h gui_gtk4_tb.h
 objects/gui_gtk4_f.o: auto/osdef.h gui_gtk4_f.c vim.h protodef.h auto/config.h 
feature.h \
  os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \
  beval.h structs.h regexp.h gui.h \
@@ -4529,6 +4534,11 @@ objects/gui_gtk4_da.o: auto/osdef.h gui_gtk4_da.c vim.h 
protodef.h auto/config.h
  beval.h structs.h regexp.h gui.h \
  libvterm/include/vterm.h libvterm/include/vterm_keycodes.h alloc.h \
  ex_cmds.h spell.h proto.h globals.h errors.h gui_gtk4_da.h
+objects/gui_gtk4_tb.o: auto/osdef.h gui_gtk4_tb.c vim.h protodef.h 
auto/config.h feature.h \
+ os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \
+ beval.h structs.h regexp.h gui.h \
+ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h alloc.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h gui_gtk4_tb.h
 objects/gui_gtk_f.o: auto/osdef.h gui_gtk_f.c vim.h protodef.h auto/config.h 
feature.h \
  os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \
  beval.h structs.h regexp.h gui.h \
diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index 8a11027b7..27cf55422 100644
--- a/src/gui_gtk4.c
+++ b/src/gui_gtk4.c
@@ -33,6 +33,9 @@
 #ifdef USE_GTK4_SNAPSHOT
 # include "gui_gtk4_da.h"
 #endif
+#ifdef FEAT_TOOLBAR
+# include "gui_gtk4_tb.h"
+#endif
 
 /*
  * Geometry string parser, replacing XParseGeometry to remove X11 dependency.
@@ -508,10 +511,11 @@ gui_mch_init(void)
 #endif
 
 #ifdef FEAT_TOOLBAR
-    gui.toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    gui.toolbar = vim_toolbar_new();
     gtk_widget_set_name(gui.toolbar, "vim-toolbar");
     gtk_widget_set_visible(gui.toolbar, FALSE);
     gtk_box_append(GTK_BOX(vbox), gui.toolbar);
+    vim_toolbar_set_style(VIM_TOOLBAR(gui.toolbar), toolbar_flags, tbis_flags);
 #endif
 
 #ifdef FEAT_GUI_TABLINE
@@ -973,7 +977,12 @@ gui_mch_enable_menu(int showit)
 gui_mch_show_toolbar(int showit)
 {
     if (gui.toolbar != NULL)
+    {
        gtk_widget_set_visible(gui.toolbar, showit);
+       if (showit)
+           vim_toolbar_set_style(VIM_TOOLBAR(gui.toolbar),
+                   toolbar_flags, tbis_flags);
+    }
 }
 #endif
 
@@ -4472,7 +4481,7 @@ gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED)
 }
 
     void
-gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
+gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 {
     vimmenu_T *parent = menu->parent;
 
@@ -4481,32 +4490,32 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
     {
        if (menu_is_separator(menu->name))
        {
-           GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
-           gtk_box_append(GTK_BOX(gui.toolbar), sep);
-           menu->id = sep;
+           menu->id =
+               vim_toolbar_insert_separator(VIM_TOOLBAR(gui.toolbar), idx);
        }
        else
        {
            GtkWidget   *btn;
            GtkWidget   *icon;
+           char_u      *text;
            char_u      *tooltip;
 
-           icon = create_toolbar_icon(menu);
-           btn = gtk_button_new();
-           gtk_button_set_child(GTK_BUTTON(btn), icon);
-           gtk_widget_set_focusable(btn, FALSE);
-           gtk_widget_add_css_class(btn, "flat");
-
+           text    = CONVERT_TO_UTF8(menu->dname);
            tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
-           if (tooltip != NULL && utf_valid_string(tooltip, NULL))
-               gtk_widget_set_tooltip_text(btn, (const gchar *)tooltip);
-           CONVERT_TO_UTF8_FREE(tooltip);
+           if (tooltip != NULL && !utf_valid_string(tooltip, NULL))
+               CONVERT_TO_UTF8_FREE(tooltip);
+
+           icon = create_toolbar_icon(menu);
+           btn = vim_toolbar_insert_button(VIM_TOOLBAR(gui.toolbar),
+                   icon, (const char *)text, idx);
+           gtk_widget_set_tooltip_text(btn, (const gchar *)tooltip);
 
            g_signal_connect(btn, "clicked",
                    G_CALLBACK(toolbar_button_clicked_cb), menu);
 
-           gtk_box_append(GTK_BOX(gui.toolbar), btn);
            menu->id = btn;
+           CONVERT_TO_UTF8_FREE(text);
+           CONVERT_TO_UTF8_FREE(tooltip);
        }
        return;
     }
@@ -4523,7 +4532,8 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
        {
            // GMenu doesn't have real separators; use a section
            GMenu *section = g_menu_new();
-           g_menu_append_section(parent_menu, NULL, G_MENU_MODEL(section));
+           g_menu_insert_section(parent_menu, idx, NULL,
+                   G_MENU_MODEL(section));
            g_object_unref(section);
            menu->id = NULL;
        }
@@ -4552,7 +4562,7 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
 
            label = CONVERT_TO_UTF8(menu->dname);
            vim_snprintf(detailed, sizeof(detailed), "menu.%s", action_name);
-           g_menu_append(parent_menu, (const char *)label, detailed);
+           g_menu_insert(parent_menu, idx, (const char *)label, detailed);
            CONVERT_TO_UTF8_FREE(label);
 
            menu->id = (GtkWidget *)1;  // non-NULL marker
@@ -4570,8 +4580,17 @@ gui_mch_toggle_tearoffs(int enable UNUSED)
 }
 
     void
-gui_mch_menu_set_tip(vimmenu_T *menu UNUSED)
+gui_mch_menu_set_tip(vimmenu_T *menu)
 {
+    char_u *tooltip;
+
+    if (menu->id == NULL || menu->parent == NULL || gui.toolbar == NULL)
+       return;
+
+    tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
+    if (tooltip != NULL && utf_valid_string(tooltip, NULL))
+       gtk_widget_set_tooltip_text(menu->id, (const char *)tooltip);
+    CONVERT_TO_UTF8_FREE(tooltip);
 }
 
 /*
diff --git a/src/gui_gtk4_f.c b/src/gui_gtk4_f.c
index cd71122b1..52941874f 100644
--- a/src/gui_gtk4_f.c
+++ b/src/gui_gtk4_f.c
@@ -69,7 +69,7 @@ vim_form_dispose(GObject *obj)
     static void
 vim_form_class_init(VimFormClass *class)
 {
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
+    GtkWidgetClass  *widget_class = GTK_WIDGET_CLASS(class);
     GObjectClass    *obj_class = G_OBJECT_CLASS(class);
 
     widget_class->snapshot = vim_form_snapshot;
diff --git a/src/gui_gtk4_tb.c b/src/gui_gtk4_tb.c
new file mode 100644
index 000000000..e3ed80b47
--- /dev/null
+++ b/src/gui_gtk4_tb.c
@@ -0,0 +1,455 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved           by Bram Moolenaar
+ *
+ * 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.
+ */
+
+#include "vim.h"
+
+#ifdef FEAT_TOOLBAR
+
+#include <gtk/gtk.h>
+#include "gui_gtk4_tb.h"
+
+/*
+ * GTK4 removed GtkToolbar, so this is our version of it.
+ */
+struct _VimToolbar
+{
+    GtkWidget parent;
+
+    int style; // TOOLBAR_* flags
+    int iconsize; // TBIS_* values
+
+    GList *items;
+
+    GtkWidget  *root;
+    GtkWidget  *strip;
+
+    GtkWidget  *overflow_btn;
+    GtkWidget  *overflow_box;
+};
+
+G_DEFINE_TYPE(VimToolbar, vim_toolbar, GTK_TYPE_WIDGET)
+
+    static void vim_toolbar_size_allocate(GtkWidget *widget, int width, int 
height, int baseline);
+    static void vim_toolbar_measure(GtkWidget *widget, GtkOrientation 
orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, 
int *natural_baseline);
+
+    static void
+vim_toolbar_dispose(GObject *object)
+{
+    VimToolbar *self = VIM_TOOLBAR(object);
+
+    g_clear_list(&self->items, g_object_unref);
+    g_clear_pointer(&self->root, gtk_widget_unparent);
+
+    G_OBJECT_CLASS(vim_toolbar_parent_class)->dispose(object);
+}
+
+    static void
+vim_toolbar_class_init(VimToolbarClass *class)
+{
+    GtkWidgetClass  *widget_class = GTK_WIDGET_CLASS(class);
+    GObjectClass    *obj_class = G_OBJECT_CLASS(class);
+
+    widget_class->size_allocate = vim_toolbar_size_allocate;
+    widget_class->measure = vim_toolbar_measure;
+
+    obj_class->dispose = vim_toolbar_dispose;
+
+    gtk_widget_class_set_css_name(widget_class, "toolbar");
+}
+
+    static void
+vim_toolbar_init(VimToolbar *self)
+{
+    GtkWidget *popover;
+
+    self->style = TOOLBAR_ICONS | TOOLBAR_TOOLTIPS;
+    self->iconsize = TBIS_MEDIUM;
+
+    self->root = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
+    gtk_widget_set_parent(self->root, GTK_WIDGET(self));
+
+    self->strip = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_widget_set_hexpand(self->strip, TRUE);
+    gtk_widget_set_halign(self->strip, GTK_ALIGN_START);
+    gtk_widget_set_overflow(self->strip, GTK_OVERFLOW_HIDDEN);
+    gtk_box_append(GTK_BOX(self->root), self->strip);
+
+    self->overflow_btn = gtk_menu_button_new();
+    gtk_widget_set_hexpand(self->overflow_btn, FALSE);
+    gtk_widget_set_halign(self->overflow_btn, GTK_ALIGN_END);
+    gtk_widget_add_css_class(self->overflow_btn, "flat");
+    gtk_box_append(GTK_BOX(self->root), self->overflow_btn);
+    g_object_set_data(G_OBJECT(self->overflow_btn),
+           "toolbar-width", GINT_TO_POINTER(-1));
+
+    popover = gtk_popover_new();
+    self->overflow_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
+    gtk_popover_set_child(GTK_POPOVER(popover), self->overflow_box);
+    gtk_popover_set_has_arrow(GTK_POPOVER(popover), FALSE);
+
+    gtk_menu_button_set_popover(GTK_MENU_BUTTON(self->overflow_btn), popover);
+}
+
+    GtkWidget *
+vim_toolbar_new(void)
+{
+    return g_object_new(VIM_TYPE_TOOLBAR, NULL);
+}
+
+/*
+ * Inser the widget at the given index, then queue a size allocate to check for
+ * overflowing items.
+ */
+    static void
+vim_toolbar_insert(VimToolbar *self, GtkWidget *widget, int idx)
+{
+    GtkWidget  *cur_child = gtk_widget_get_first_child(self->strip);
+    int                i = 0;
+
+    while (cur_child != NULL && i != idx - 1)
+    {
+       cur_child = gtk_widget_get_next_sibling(cur_child);
+       i++;
+    }
+
+    gtk_box_insert_child_after(GTK_BOX(self->strip), widget, cur_child);
+    self->items = g_list_insert(self->items, g_object_ref(widget), idx);
+    gtk_widget_queue_allocate(GTK_WIDGET(self));
+}
+
+/*
+ * Update "btn" according to "style" and "iconsize".
+ */
+    static void
+set_button_style(GtkWidget *btn, int style, int iconsize, gboolean invalidate)
+{
+    GtkWidget *box;
+    GtkWidget *icon;
+    GtkWidget *label;
+
+    if (GTK_IS_SEPARATOR(btn))
+       return;
+
+    box = gtk_button_get_child(GTK_BUTTON(btn));
+    if (style & TOOLBAR_HORIZ)
+    {
+       gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
+               GTK_ORIENTATION_HORIZONTAL);
+       gtk_box_set_spacing(GTK_BOX(box), 8);
+    }
+    else
+    {
+       gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
+               GTK_ORIENTATION_VERTICAL);
+       gtk_box_set_spacing(GTK_BOX(box), 4);
+    }
+
+    gtk_widget_set_has_tooltip(btn, style & TOOLBAR_TOOLTIPS);
+
+    icon = g_object_get_data(G_OBJECT(btn), "icon");
+    if (icon != NULL)
+    {
+       if (style & TOOLBAR_ICONS)
+       {
+           GtkIconSize size;
+
+           switch (iconsize)
+           {
+               case TBIS_TINY:
+               case TBIS_SMALL:
+               case TBIS_MEDIUM:
+                   size = GTK_ICON_SIZE_NORMAL;
+                   break;
+               case TBIS_LARGE:
+               case TBIS_HUGE:
+               case TBIS_GIANT:
+                   size = GTK_ICON_SIZE_LARGE;
+                   break;
+               default:
+                   size = GTK_ICON_SIZE_NORMAL;
+                   break;
+           }
+           gtk_image_set_icon_size(GTK_IMAGE(icon), size);
+           gtk_widget_set_visible(icon, TRUE);
+       }
+       else
+           gtk_widget_set_visible(icon, FALSE);
+    }
+
+    label = g_object_get_data(G_OBJECT(btn), "label");
+    if (label != NULL)
+       gtk_widget_set_visible(label, style & TOOLBAR_TEXT);
+
+    // Used to cache width in toolbar ("self->strip"). -1 means not calculated
+    // yet.
+    if (invalidate)
+       g_object_set_data(G_OBJECT(btn), "toolbar-width", GINT_TO_POINTER(-1));
+}
+
+/*
+ * Add a new toolbar button at the given index and return the widget. "icon" 
and
+ * "text" may be NULL..
+ */
+    GtkWidget *
+vim_toolbar_insert_button(
+       VimToolbar  *self,
+       GtkWidget   *icon,
+       const char  *text,
+       int         idx)
+{
+    GtkWidget *btn = gtk_button_new();
+    GtkWidget *box;
+
+    gtk_widget_add_css_class(btn, "flat");
+    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+
+    gtk_button_set_child(GTK_BUTTON(btn), box);
+
+    if (icon != NULL)
+    {
+       gtk_widget_set_valign (icon, GTK_ALIGN_CENTER);
+       gtk_widget_set_vexpand (icon, TRUE);
+       gtk_box_append(GTK_BOX(box), icon);
+       g_object_set_data(G_OBJECT(btn), "icon", icon);
+    }
+    if (text != NULL)
+    {
+       GtkWidget *label = gtk_label_new(text);
+
+       gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
+       gtk_widget_set_vexpand (label, TRUE);
+       gtk_box_append(GTK_BOX(box), label);
+       g_object_set_data(G_OBJECT(btn), "label", label);
+    }
+
+    set_button_style(btn, self->style, self->iconsize, TRUE);
+
+    vim_toolbar_insert(self, btn, idx);
+    return btn;
+}
+
+/*
+ * Insert a toolbar separator at the given index and return the widget.
+ */
+    GtkWidget *
+vim_toolbar_insert_separator(VimToolbar *self, int idx)
+{
+    GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
+
+    g_object_set_data(G_OBJECT(sep), "toolbar-width", GINT_TO_POINTER(-1));
+    vim_toolbar_insert(self, sep, idx);
+    return sep;
+}
+
+/*
+ * Update the style and iconsize of the toolbar
+ */
+    void
+vim_toolbar_set_style(VimToolbar *self, int style, int iconsize)
+{
+    GtkWidget *cur_child = gtk_widget_get_first_child(self->strip);
+
+    if (style == self->style && self->iconsize == iconsize)
+       return;
+
+    self->style = style;
+    self->iconsize = iconsize;
+
+    while (cur_child != NULL)
+    {
+       if (!GTK_IS_SEPARATOR(cur_child))
+           set_button_style(cur_child, style, iconsize, TRUE);
+       cur_child = gtk_widget_get_next_sibling(cur_child);
+    }
+
+    cur_child = gtk_widget_get_first_child(self->overflow_box);
+    while (cur_child != NULL)
+    {
+       if (!GTK_IS_SEPARATOR(cur_child))
+           set_button_style(cur_child, style, iconsize, TRUE);
+       cur_child = gtk_widget_get_next_sibling(cur_child);
+    }
+    // Sizes may have changed
+    gtk_widget_queue_allocate(GTK_WIDGET(self));
+}
+
+/*
+ * If "overflow" is TRUE, then move "item" from the toolbar to the overflow, 
and
+ * vice versa.
+ */
+    static void
+vim_toolbar_move_item_to(
+       VimToolbar  *self,
+       GtkWidget   *item,
+       gboolean    overflow)
+{
+    GtkBox *from, *to;
+
+    to = overflow ? GTK_BOX(self->overflow_box) : GTK_BOX(self->strip);
+    if (gtk_widget_get_parent(item) == GTK_WIDGET(to))
+       return;
+
+    from = overflow ? GTK_BOX(self->strip) : GTK_BOX(self->overflow_box);
+
+    gtk_box_remove(from, item);
+    gtk_box_append(to, item);
+
+    if (GTK_IS_SEPARATOR(item))
+       gtk_widget_set_visible(item, !overflow);
+    else
+    {
+       if (overflow)
+       {
+           int style = self->style;
+
+           // Force set these flags for the overflow menu, to mimic what GTK3
+           // GtkToolbar does.
+           style |= TOOLBAR_HORIZ;
+           style |= TOOLBAR_TEXT;
+           style |= TOOLBAR_ICONS;
+           set_button_style(item, style, self->iconsize, FALSE);
+       }
+       else
+           set_button_style(item, self->style, self->iconsize, FALSE);
+    }
+}
+
+    static int
+get_item_width(GtkWidget *item)
+{
+    int toolbar_width = -1;
+
+    // Always use cached width for item, because width in overflow menu is
+    // different (GtkBox is in horizontal layout). Also use it for separators,
+    // since if its not visible, then width is zero.
+    toolbar_width = GPOINTER_TO_INT(
+           g_object_get_data(G_OBJECT(item), "toolbar-width"));
+
+    if (toolbar_width == -1)
+    {
+       GtkRequisition min_req, nat_req;
+       gtk_widget_get_preferred_size(item, &min_req, &nat_req);
+       toolbar_width = MAX(min_req.width, nat_req.width);
+
+       // Save width for later
+       g_object_set_data(G_OBJECT(item),
+               "toolbar-width", GINT_TO_POINTER(toolbar_width));
+    }
+    return toolbar_width;
+}
+
+    static void
+vim_toolbar_size_allocate(
+       GtkWidget   *widget,
+       int         width,
+       int         height,
+       int         baseline)
+{
+    VimToolbar     *self = VIM_TOOLBAR(widget);
+    GtkAllocation   alloc;
+    int                    used = 0;
+    int                    avail;
+    GList          *overflow_ele = NULL;
+    GtkWidget      *child;
+
+    avail = width - get_item_width(self->overflow_btn);
+
+    for (GList *ele = self->items; ele != NULL; ele = ele->next)
+    {
+       GtkWidget   *item = ele->data;
+       int         toolbar_width = get_item_width(item);
+
+       if (used + toolbar_width > avail)
+       {
+           overflow_ele = ele;
+           break;
+       }
+       else
+           vim_toolbar_move_item_to(self, item, FALSE);
+       used += toolbar_width;
+    }
+
+    if (overflow_ele != NULL)
+    {
+       int         remaining = width - used;
+       gboolean    need_overflow_btn = FALSE;
+
+       // Move all items in the overflow to the toolbar, so we can add them
+       // in the correct order back again. Also check if the overflow button is
+       // still needed (can fit all toolbar items by disabling it).
+       while ((child = gtk_widget_get_first_child(self->overflow_box))
+               != NULL)
+           vim_toolbar_move_item_to(self, child, FALSE);
+
+       for (GList *ele = overflow_ele; ele != NULL; ele = ele->next)
+       {
+           remaining -= get_item_width(ele->data);
+           if (remaining < 0)
+           {
+               need_overflow_btn = TRUE;
+               break;
+           }
+       }
+
+       if (need_overflow_btn)
+           for (; overflow_ele != NULL; overflow_ele = overflow_ele->next)
+               vim_toolbar_move_item_to(self, overflow_ele->data, TRUE);
+       gtk_widget_set_visible(self->overflow_btn, need_overflow_btn);
+    }
+    else
+       gtk_widget_set_visible(self->overflow_btn, FALSE);
+
+    alloc.x = alloc.y = 0;
+    alloc.width = width;
+    alloc.height = height;
+
+    gtk_widget_size_allocate(self->root, &alloc, baseline);
+}
+
+    static void
+vim_toolbar_measure(
+       GtkWidget       *widget,
+       GtkOrientation  orientation,
+       int             for_size,
+       int             *min,
+       int             *nat,
+       int             *min_baseline,
+       int             *nat_baseline)
+{
+    VimToolbar *self = VIM_TOOLBAR(widget);
+    int                strip_min = 0, strip_nat = 0;
+    int                obtn_min = 0, obtn_nat = 0;
+
+    gtk_widget_measure(self->strip, orientation, for_size,
+           &strip_min, &strip_nat, NULL, NULL);
+    gtk_widget_measure(self->overflow_btn, orientation, for_size,
+           &obtn_min, &obtn_nat, NULL, NULL);
+
+    if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+       if (min != NULL)
+           *min = obtn_min;
+       if (nat != NULL)
+           *nat = strip_nat + obtn_nat;
+    }
+    else
+    {
+       if (min != NULL)
+           *min = MAX(strip_min, obtn_min);
+       if (nat != NULL)
+           *nat = MAX(strip_nat, obtn_nat);
+    }
+
+    if (min_baseline != NULL)
+       *min_baseline = -1;
+    if (nat_baseline != NULL)
+       *nat_baseline = -1;
+}
+
+#endif // FEAT_TOOLBAR
diff --git a/src/gui_gtk4_tb.h b/src/gui_gtk4_tb.h
new file mode 100644
index 000000000..d3ff4f782
--- /dev/null
+++ b/src/gui_gtk4_tb.h
@@ -0,0 +1,31 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved           by Bram Moolenaar
+ *
+ * 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.
+ */
+
+#ifndef GUI_GTK4_TB_H
+#define GUI_GTK4_TB_H
+
+#include "vim.h"
+
+#ifdef FEAT_TOOLBAR
+
+# include <gtk/gtk.h>
+
+# define VIM_TYPE_TOOLBAR (vim_toolbar_get_type())
+G_DECLARE_FINAL_TYPE(VimToolbar, vim_toolbar, VIM, TOOLBAR, GtkWidget)
+
+typedef struct _VimToolbarItem VimToolbarItem;
+
+GtkWidget *vim_toolbar_new(void);
+GtkWidget *vim_toolbar_insert_button(VimToolbar *self, GtkWidget *icon, const 
char *text, int idx);
+GtkWidget *vim_toolbar_insert_separator(VimToolbar *self, int idx);
+void vim_toolbar_set_style(VimToolbar *self, int style, int iconsize);
+
+#endif
+
+#endif
diff --git a/src/version.c b/src/version.c
index c404029ad..e4f09bcab 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    669,
 /**/
     668,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wZwQh-002leV-Cd%40256bit.org.

Raspunde prin e-mail lui