From: Foxe Chen Date: Tue, 23 Jun 2026 20:03:31 +0000 (+0000) Subject: patch 9.2.0710: GTK4 GUI resize handling can be improved X-Git-Tag: v9.2.0710^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7908164c9d6a5ac7bda34d45681d6f298e32419d;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/gui.c b/src/gui.c index 0cd586114a..0ca09d8eb1 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 674601e0f7..78ad3a09bb 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 5126f99198..7084516d87 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 34dad93a61..11b7463892 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 52941874fb..29c17aa6d5 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 acfb13c9a5..eaf8eb2686 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 3f52cbadff..7433593c77 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, /**/