Commit: patch 9.2.0710: GTK4 GUI resize handling can be improved

0 views
Skip to first unread message

Christian Brabandt

unread,
4:15 PM (6 hours ago) 4:15 PM
to vim...@googlegroups.com
patch 9.2.0710: GTK4 GUI resize handling can be improved

Commit: https://github.com/vim/vim/commit/7908164c9d6a5ac7bda34d45681d6f298e32419d
Author: Foxe Chen <chen...@gmail.com>
Date: Tue Jun 23 20:03:31 2026 +0000

patch 9.2.0710: GTK4 GUI resize handling can be improved

Problem: GTK4 GUI resize handling can be improved
Solution: Remove the resize debounce, set the draw area's size request
in gui_mch_set_text_area_pos() via vim_form_move_resize()
(Foxe Chen).

reverts: #20327
closes: #20486

Signed-off-by: Foxe Chen <chen...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/gui.c b/src/gui.c
index 0cd586114..0ca09d8eb 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -1625,6 +1625,14 @@ again:
gui_position_components(pixel_width);
gui_reset_scroll_region();

+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) && !defined(USE_GTK4_SNAPSHOT)
+ // We do not resize the draw area via the "resize" signal. This is because
+ // when the window is resized, the form widget is the one that is resized,
+ // so let that call gui_resize_shell() which will allocate the surface and
+ // allocate the drawing area size/position.
+ gui_gtk4_resize(pixel_width, pixel_height);
+#endif
+
/*
* At the "more" and ":confirm" prompt there is no redraw, put the cursor
* at the last line here (why does it have to be one row too low?).
@@ -1647,6 +1655,9 @@ again:

gui_update_scrollbars(TRUE);
gui_update_cursor(FALSE, TRUE);
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+ gui_gtk_calculate_bleed(pixel_width, pixel_height);
+#endif
#if defined(FEAT_XIM) && !defined(FEAT_GUI_GTK)
xim_set_status_area();
#endif
@@ -1821,6 +1832,10 @@ gui_set_shellsize(
gui_position_components(width);
gui_update_scrollbars(TRUE);
gui_reset_scroll_region();
+
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+ gui_gtk_calculate_bleed(width, height);
+#endif
}

/*
diff --git a/src/gui.h b/src/gui.h
index 674601e0f..78ad3a09b 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -273,6 +273,10 @@ typedef struct Gui
#ifdef FEAT_DIRECTX
bool directx_enabled; // DirectX (DirectWrite) rendering active
#endif
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+ int bleed_right; // Number of pixels to bleed bg color right
+ int bleed_bot; // Number of pixels to bleed bg color down
+#endif

#ifdef FEAT_MENU
# ifndef FEAT_GUI_GTK
diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index 5126f9919..7084516d8 100644
--- a/src/gui_gtk4.c
+++ b/src/gui_gtk4.c
@@ -299,12 +299,9 @@ static void tabline_menu_press_event(GtkGestureClick *gesture, int n_press, doub
static void mainwin_destroy_cb(GObject *object, gpointer data);
static gboolean delete_event_cb(GtkWindow *window, gpointer data);
static void mainwin_fullscreened_cb(GObject *obj, GParamSpec *pspec, gpointer user_data);
-#ifndef USE_GTK4_SNAPSHOT
static void drawarea_realize_cb(GtkWidget *widget, gpointer data);
-#endif
static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data);
#ifndef USE_GTK4_SNAPSHOT
-static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data);
static void drawarea_scale_factor_cb(GObject *object, GParamSpec *pspec, gpointer data);
static cairo_surface_t *create_backing_surface(int width, int height);
#endif
@@ -598,13 +595,11 @@ gui_mch_init(void)
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gui.drawarea),
(GtkDrawingAreaDrawFunc)draw_event, NULL, NULL);

- g_signal_connect(G_OBJECT(gui.drawarea), "resize",
- G_CALLBACK(drawarea_resize_cb), NULL);
g_signal_connect(G_OBJECT(gui.drawarea), "notify::scale-factor",
G_CALLBACK(drawarea_scale_factor_cb), NULL);
+#endif
g_signal_connect(G_OBJECT(gui.drawarea), "realize",
G_CALLBACK(drawarea_realize_cb), NULL);
-#endif
g_signal_connect(G_OBJECT(gui.drawarea), "unrealize",
G_CALLBACK(drawarea_unrealize_cb), NULL);

@@ -887,10 +882,10 @@ gui_mch_newfont(void)
{
int w, h;

+ // Do not subtract width and height with menubar, toolbar, etc, because
+ // those are not part of the shell.
w = gtk_widget_get_width(gui.formwin);
h = gtk_widget_get_height(gui.formwin);
- w -= get_menu_tool_width();
- h -= get_menu_tool_height();
gui_resize_shell(w, h);
}

@@ -968,7 +963,13 @@ gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
gui_mch_enable_menu(int showit)
{
if (gui.menubar != NULL)
+ {
gtk_widget_set_visible(gui.menubar, showit);
+ // Draw area might become blank after this for some reason, queue a
+ // redraw, same for toolbar as well.
+ if (gui.drawarea != NULL)
+ gtk_widget_queue_draw(gui.drawarea);
+ }
}
#endif

@@ -982,6 +983,8 @@ gui_mch_show_toolbar(int showit)
if (showit)
vim_toolbar_set_style(VIM_TOOLBAR(gui.toolbar),
toolbar_flags, tbis_flags);
+ if (gui.drawarea != NULL)
+ gtk_widget_queue_draw(gui.drawarea);
}
}
#endif
@@ -2339,10 +2342,10 @@ menubar_popover_closed_hook(GSignalInvocationHint *ihint UNUSED,
}
#endif

-#ifndef USE_GTK4_SNAPSHOT
static void
drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
{
+#ifndef USE_GTK4_SNAPSHOT
int w, h;

// Use formwin size since drawarea may not have its final size yet
@@ -2363,10 +2366,10 @@ drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
if (gui.surface != NULL)
cairo_surface_destroy(gui.surface);
gui.surface = create_backing_surface(w, h);
+#endif

gui_mch_new_colors();
}
-#endif

static void
drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
@@ -2384,42 +2387,8 @@ drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
}

#ifndef USE_GTK4_SNAPSHOT
-// Debounced resize: drawarea_resize_cb only resizes the backing surface
-// (preserving old content) and (re)arms a short timeout. The actual
-// gui_resize_shell() runs from drawarea_resize_apply_cb once the user has
-// stopped dragging for ~100 ms, by which time no input is pending and
-// update_screen() will not bail in screenclear()'s wake.
-static guint drawarea_resize_timeout_id = 0;
-static int drawarea_resize_pending_w = 0;
-static int drawarea_resize_pending_h = 0;
-
- static gboolean
-drawarea_resize_apply_cb(gpointer data UNUSED)
-{
- int width = drawarea_resize_pending_w;
- int height = drawarea_resize_pending_h;
-
- drawarea_resize_timeout_id = 0;
-
- if (width <= 0 || height <= 0)
- return G_SOURCE_REMOVE;
- if (updating_screen)
- {
- drawarea_resize_timeout_id = g_timeout_add(50,
- drawarea_resize_apply_cb, NULL);
- return G_SOURCE_REMOVE;
- }
-
- gui.force_redraw = TRUE;
- gui_resize_shell(width, height);
- if (gui.in_use)
- redraw_all_later(UPD_CLEAR);
- return G_SOURCE_REMOVE;
-}
-
- static void
-drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height,
- gpointer data UNUSED)
+ void
+gui_gtk4_resize(int width, int height)
{
cairo_t *cr;
cairo_surface_t *old_surface;
@@ -2428,9 +2397,6 @@ drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height,
if (width <= 0 || height <= 0)
return;

- drawarea_resize_pending_w = width;
- drawarea_resize_pending_h = height;
-
// Keep the backing surface in sync with the drawing area so GTK keeps
// showing the previous frame. Re-creating it preserves the old
// contents.
@@ -2465,13 +2431,6 @@ drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height,
cairo_destroy(cr);
}
}
-
- // Debounce: (re)arm the apply timeout, so gui_resize_shell() only
- // runs once the resize stream settles.
- if (drawarea_resize_timeout_id != 0)
- g_source_remove(drawarea_resize_timeout_id);
- drawarea_resize_timeout_id = g_timeout_add(100,
- drawarea_resize_apply_cb, NULL);
}

static void
@@ -4977,20 +4936,41 @@ gui_mch_update_scrollbar_size(void)
void
gui_mch_set_text_area_pos(int x, int y, int w, int h)
{
+ // "h" may be negative especially when draw area size is smaller than
+ // "gui.char_height".
+ if (w <= 0 || h <= 0)
+ return;
last_text_area_w = w;
last_text_area_h = h;
- // 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
- // 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
- // content whenever a sub-cell resize shifted the cell grid, and
- // update_screen() may bail (char_avail()) during a drag and leave
- // the fresh surface blank.
+
+ vim_form_move_resize(VIM_FORM(gui.formwin), gui.drawarea, x, y, w, h);
+}
+
+#ifdef USE_GTK4_SNAPSHOT
+/*
+ * Calculate the number of pixels to bleed background color to. Should be called
+ * after all UI elements are positioned and resized.
+ */
+ void
+gui_gtk_calculate_bleed(int width, int height)
+{
+ gui.bleed_right = width - last_text_area_w;
+ gui.bleed_bot = height - last_text_area_h;
+
+ if (gui.which_scrollbars[SBAR_LEFT])
+ gui.bleed_right -= gui.scrollbar_width;
+ if (gui.which_scrollbars[SBAR_RIGHT])
+ gui.bleed_right -= gui.scrollbar_width;
+ if (gui.which_scrollbars[SBAR_BOTTOM])
+ gui.bleed_bot -= gui.scrollbar_height;
+
+ // Not sure if this can happen, but be safe...
+ if (gui.bleed_right < 0)
+ gui.bleed_right = 0;
+ if (gui.bleed_bot < 0)
+ gui.bleed_bot = 0;
}
+#endif

/*
* ============================================================
diff --git a/src/gui_gtk4_da.c b/src/gui_gtk4_da.c
index 34dad93a6..11b746389 100644
--- a/src/gui_gtk4_da.c
+++ b/src/gui_gtk4_da.c
@@ -75,7 +75,7 @@ struct _VimDrawArea
int n_rows;
int n_cols;

- int resize_count;
+ int bleed_right;

// Used for hollow and part style cursors. For the block cursor, that is
// simply rendered as a cell using vim_draw_area_add_glyphs(). May be NULL.
@@ -107,7 +107,6 @@ G_DEFINE_TYPE(VimDrawArea, vim_draw_area, GTK_TYPE_WIDGET)
static void draw_image_free(DrawImage *dimg);
#endif
static void vim_draw_area_snapshot(GtkWidget *widget, GtkSnapshot *snapshot);
-static void vim_draw_area_size_allocate(GtkWidget *widget, int width, int height, int baseline);

static void
vim_draw_area_finalize(GObject *obj)
@@ -138,10 +137,10 @@ vim_draw_area_class_init(VimDrawAreaClass *class)
GObjectClass *obj_class = G_OBJECT_CLASS(class);

widget_class->snapshot = vim_draw_area_snapshot;
- widget_class->size_allocate = vim_draw_area_size_allocate;

obj_class->finalize = vim_draw_area_finalize;

+ gtk_widget_class_set_layout_manager_type(widget_class, GTK_TYPE_BIN_LAYOUT);
}

static void
@@ -178,7 +177,6 @@ vim_draw_area_set_size(VimDrawArea *self, int rows, int cols)
self->n_cols = cols;
self->cells = g_realloc_n(self->cells, rows * cols, sizeof(DrawCell));
memset(self->cells, 0, rows * (sizeof(DrawCell) * cols));
- self->resize_count++;
}

static void
@@ -574,14 +572,17 @@ draw_node_render(DrawNode *dnode, int row, VimDrawArea *da)
if (!(dnode->dnode_flags & DRAW_NODE_NOBG))
{
int width = dnode->n_cells * gui.char_width;
- int bleed = gtk_widget_get_width(GTK_WIDGET(da)) - FILL_X(da->n_cols);

// If this draw node touches the end of the draw area. Bleed its
// background to the right if the space the draw area covers is slightly
// bigger than its actual visible area (that all cells cover). This just
// makes things like status bars look a bit nicer
- if (END_COL(dnode) == da->n_cols - 1 && bleed > 0)
- width += bleed;
+ //
+ // Don't do this for the bottom, because that will make the cursor in
+ // the cmdline look weird. Instead only bleed downwards when drawing the
+ // global background color (see vim_draw_area_snapshot())
+ if (END_COL(dnode) == da->n_cols - 1)
+ width += gui.bleed_right;

nodes[n_nodes++] = gsk_color_node_new(&dnode->bg_color,
&GRAPHENE_RECT_INIT(FILL_X(dnode->start_col), FILL_Y(row),
@@ -1475,8 +1476,8 @@ vim_draw_area_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
garray_T invert_ga;

gui_mch_set_bg_color(gui.back_pixel);
- height = gtk_widget_get_height(widget);
- width = gtk_widget_get_width(widget);
+ height = gtk_widget_get_height(widget) + gui.bleed_bot;
+ width = gtk_widget_get_width(widget) + gui.bleed_right;

if (self->cells == NULL)
{
@@ -1485,6 +1486,23 @@ vim_draw_area_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
return;
}

+ // If number of pixels to bleed has changed, then dirty the nodes at the
+ // right edge of the draw area.
+ if (self->bleed_right != gui.bleed_right)
+ {
+ self->bleed_right = gui.bleed_right;
+ for (int r = 0; r < self->n_rows; r++)
+ {
+ DrawCell *dcell = &GET_ROW(self, r)[self->n_cols - 1];
+
+ if (dcell->dnode != NULL)
+ {
+ (void)draw_node_make_dirty(dcell->dnode);
+ draw_node_render(dcell->dnode, r, self);
+ }
+ }
+ }
+
// For inverted cells, we first build an array of bounds that represent
// blocks of inverted cells. Then we apply a white color to each of those
// bounds and then finish the blend.
@@ -1565,36 +1583,4 @@ vim_draw_area_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
#endif
}

- static void
-vim_draw_area_size_allocate(
- GtkWidget *widget,
- int width,
- int height,
- int baseline UNUSED)
-{
- VimDrawArea *self = VIM_DRAW_AREA(widget);
- int old_count = self->resize_count;
-
- gui_resize_shell(width, height);
-
- if (old_count == self->resize_count)
- {
- // Number of columns or rows hasn't changed. However still re render the
- // draw nodes at the right edge of the draw area, so that they can
- // update their background bleed (see draw_node_render()).
- for (int r = 0; r < self->n_rows; r++)
- {
- DrawCell *dcell = &GET_ROW(self, r)[self->n_cols - 1];
-
- if (dcell->dnode != NULL)
- {
- (void)draw_node_make_dirty(dcell->dnode);
- draw_node_render(dcell->dnode, r, self);
- }
- }
- }
-
- return;
-}
-
#endif // USE_GTK4_SNAPSHOT
diff --git a/src/gui_gtk4_f.c b/src/gui_gtk4_f.c
index 52941874f..29c17aa6d 100644
--- a/src/gui_gtk4_f.c
+++ b/src/gui_gtk4_f.c
@@ -209,18 +209,13 @@ vim_form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
static gboolean
vim_form_resize_idle_cb(VimForm *self)
{
- int w, h;
-
self->resize_idle_id = 0;

- // Use drawarea's actual allocation, not formwin's
if (gui.drawarea == NULL)
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);
+ if (self->last_width > 1 && self->last_height > 1)
+ gui_resize_shell(self->last_width, self->last_height);

exit:
g_object_unref(self);
diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro
index acfb13c9a..eaf8eb268 100644
--- a/src/proto/gui_gtk4.pro
+++ b/src/proto/gui_gtk4.pro
@@ -52,6 +52,7 @@ void gui_mch_draw_hollow_cursor(guicolor_T color);
void gui_mch_draw_part_cursor(int w, int h, guicolor_T color);
void gui_mch_flash(int msec);
void gui_mch_invert_rectangle(int r, int c, int nr, int nc);
+void gui_gtk4_resize(int width, int height);
void gui_mch_update(void);
int gui_mch_wait_for_chars(long wtime);
void gui_mch_flush(void);
@@ -103,6 +104,7 @@ 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);
+void gui_gtk_calculate_bleed(int width, int height);
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);
int gui_mch_dialog(int type, char_u *title, char_u *message, char_u *buttons, int def_but, char_u *textfield, int ex_cmd);
diff --git a/src/version.c b/src/version.c
index 3f52cbadf..7433593c7 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 */
+/**/
+ 710,
/**/
709,
/**/
Reply all
Reply to author
Forward
0 new messages