From a360eeb6d300a1f61186744463c731f20b14c55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Thu, 7 May 2026 20:10:46 +0300 Subject: [PATCH] Fix PaintListener for GTK3 Table and Tree widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GTK3 emits two draw-related signals: * EXPOSE_EVENT_INVERSE (after=false) → before GTK draws items * EXPOSE_EVENT (after=true) → after GTK draws items SWT historically dispatched SWT.Paint during EXPOSE_EVENT_INVERSE. For widgets like Table and Tree, GTK draws all rows/cells after this point. Result: anything drawn in a PaintListener was immediately overwritten. --- .../gtk/org/eclipse/swt/widgets/Table.java | 41 ++++++++++++++++++- .../gtk/org/eclipse/swt/widgets/Tree.java | 41 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java index 2a39de05837..a72fb2f3a97 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java @@ -2310,7 +2310,12 @@ long gtk_draw (long widget, long cairo) { return 0; } drawInheritedBackground (cairo); - return super.gtk_draw (widget, cairo); + if (GTK.GTK4) { + return super.gtk_draw (widget, cairo); + } else { + // On GTK3 super.gtk_draw will be lost by items drawing thus handle explicitly in windowProc. + return 0; + } } @Override @@ -4116,6 +4121,13 @@ long windowProc (long handle, long arg0, long user_data) { } propagateDraw(handle, arg0); } + /* + * Ensure the paint listener's drawing appears on top of items rather than being + * overwritten by them. + */ + if (!GTK.GTK4) { + gtk3_paintEvent(arg0); + } break; } case EXPOSE_EVENT_INVERSE: { @@ -4194,6 +4206,33 @@ void checkSetDataInProcessBeforeRemoval(int start, int end) { } } +/** + * Fire the paint event explicitly, so the paint listener's drawing is not lost. + */ +private void gtk3_paintEvent(long cairo) { + if ((state & OBSCURED) != 0) return; + if (drawRegion) { + cairoClipRegion(cairo); + } + if (!hooksPaint()) return; + GdkRectangle rect = new GdkRectangle(); + GDK.gdk_cairo_get_clip_rectangle(cairo, rect); + Event event = new Event(); + event.count = 1; + Rectangle eventBounds = new Rectangle(rect.x, rect.y, rect.width, rect.height); + if ((style & SWT.MIRRORED) != 0) eventBounds.x = getClientWidth() - eventBounds.width - eventBounds.x; + event.setBounds(eventBounds); + GCData data = new GCData(); + if (drawRegion) data.regionSet = eventRegion; + data.cairo = cairo; + GC gc = event.gc = GC.gtk_new(this, data); + gc.setClipping(eventBounds.x, eventBounds.y, eventBounds.width, eventBounds.height); + drawWidget(gc); + sendEvent(SWT.Paint, event); + gc.dispose(); + event.gc = null; +} + @Override public void dispose() { super.dispose(); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java index c24b767bef7..aa25c3203c7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java @@ -2480,7 +2480,12 @@ long gtk_draw (long widget, long cairo) { return 0; } drawInheritedBackground (cairo); - return super.gtk_draw (widget, cairo); + if (GTK.GTK4) { + return super.gtk_draw (widget, cairo); + } else { + // On GTK3 super.gtk_draw will be lost by items drawing thus handle explicitly in windowProc. + return 0; + } } @Override @@ -4192,6 +4197,13 @@ long windowProc (long handle, long arg0, long user_data) { } propagateDraw(handle, arg0); } + /* + * Ensure the paint listener's drawing appears on top of items rather than being + * overwritten by them. + */ + if (!GTK.GTK4) { + gtk3_paintEvent(arg0); + } break; } case EXPOSE_EVENT_INVERSE: { @@ -4267,6 +4279,33 @@ void checkSetDataInProcessBeforeRemoval() { } } +/** + * Fire the paint event explicitly, so the paint listener's drawing is not lost. + */ +private void gtk3_paintEvent(long cairo) { + if ((state & OBSCURED) != 0) return; + if (drawRegion) { + cairoClipRegion(cairo); + } + if (!hooksPaint()) return; + GdkRectangle rect = new GdkRectangle(); + GDK.gdk_cairo_get_clip_rectangle(cairo, rect); + Event event = new Event(); + event.count = 1; + Rectangle eventBounds = new Rectangle(rect.x, rect.y, rect.width, rect.height); + if ((style & SWT.MIRRORED) != 0) eventBounds.x = getClientWidth() - eventBounds.width - eventBounds.x; + event.setBounds(eventBounds); + GCData data = new GCData(); + if (drawRegion) data.regionSet = eventRegion; + data.cairo = cairo; + GC gc = event.gc = GC.gtk_new(this, data); + gc.setClipping(eventBounds.x, eventBounds.y, eventBounds.width, eventBounds.height); + drawWidget(gc); + sendEvent(SWT.Paint, event); + gc.dispose(); + event.gc = null; +} + private void throwCannotRemoveItem(int i) { String message = "Cannot remove item with index " + i + "."; throw new SWTException(message);