[GTK] Prototype SWT.VIRTUAL support for Composite (issue 624) - #15
[GTK] Prototype SWT.VIRTUAL support for Composite (issue 624)#15vogella wants to merge 6 commits into
Conversation
Add an experimental, GTK-only implementation of a virtual (handle-less) Composite. A composite created with SWT.VIRTUAL creates no native widget; its children are parented to the nearest non-virtual ancestor while the virtual composite continues to act as a layout container. This reduces the number of native controls and the nesting depth of the native widget hierarchy. Mechanism: - createHandle() skips native widget creation for SWT.VIRTUAL. - parentingHandle() walks up to the nearest non-virtual ancestor, so children attach to that ancestor's native container. - _getChildren() filters native children by logical parent and appends Java-tracked virtual children (which are invisible to native enumeration and to disposal/reskin walks otherwise). - Geometry is tracked in Java; child layout coordinates are translated by the cumulative virtual-ancestor offset (Control.parentingOffset()), and descendants are shifted/relaid-out manually since there is no native size-allocate signal. - Native lifecycle, focus and z-order paths are guarded for the handle-less case. checkStyle() clears H_SCROLL/V_SCROLL/BORDER for virtual composites: with no native surface a virtual composite cannot scroll, draw a border or background, paint, take focus, or receive its own mouse/keyboard events, and it does not clip its children. It is intended purely as a layout node. Win32 and Cocoa still ignore the style (the composite behaves as a normal Composite there); the new tests assert behaviour that holds on every platform. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
There was a problem hiding this comment.
Code Review
This pull request introduces experimental support for virtual (SWT.VIRTUAL) composites in SWT GTK. A virtual composite acts as a lightweight, handle-less layout container without creating its own native operating-system control, flattening its children onto the nearest non-virtual ancestor. The changes include tracking virtual geometry, managing virtual children, routing parenting handles, and adjusting coordinate calculations in Control. Additionally, unit tests are added to verify layout and disposal behavior. The reviewer highlighted a critical stability concern where calling native GTK/GDK functions on a virtual composite's null handle could cause JVM crashes or GTK warnings, suggesting overrides for several core methods (such as redraw, visibility, enablement, printing, and coordinate translation) to safely handle the virtual case.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| @Override | ||
| void setZOrder (Control sibling, boolean above, boolean fixRelations, boolean fixChildren) { | ||
| // A virtual composite has no native widget, so there is no native z-order to change. | ||
| if (isVirtual ()) return; | ||
| super.setZOrder (sibling, above, fixRelations, fixChildren); | ||
| } |
There was a problem hiding this comment.
Critical Stability and Correctness Fixes for Virtual Composites
Since a virtual (SWT.VIRTUAL) composite has no native widget (handle == 0), calling native GTK/GDK functions on its handle or window will trigger native GTK critical warnings or crash the JVM.
To ensure stability and correctness, we must override several core Control and Composite methods to safely handle the virtual case:
- Color & Font Settings (
setBackgroundGdkRGBA,setForegroundGdkRGBA,setFontDescription): Prevent passing0to GTK style context and CSS provider functions. - Redraw & Update (
redraw,update): Recursively delegate to the parent composite with translated coordinates instead of querying the non-existent native widget/window. - Visibility & Enablement (
setVisible,setEnabled): Safely update the internal states and send events without calling native GTK show/hide/realize functions on a null handle. - Printing (
print,printWidget): Prevent native crashes when printing a parent container that contains virtual composites by recursively printing children. - Coordinate Translation (
toControl,toDisplay): Safely translate coordinates using the parent's coordinate space instead of calling native window origin/translation functions on a null handle. - Layout & Mirroring (
getChildrenCount,getClientWidth): Return the correct logical child count and virtual width (crucial for RTL/mirrored coordinate calculations) instead of querying the native widget allocation.
@Override
void setZOrder (Control sibling, boolean above, boolean fixRelations, boolean fixChildren) {
// A virtual composite has no native widget, so there is no native z-order to change.
if (isVirtual ()) return;
super.setZOrder (sibling, above, fixRelations, fixChildren);
}
@Override
void setBackgroundGdkRGBA (GdkRGBA rgba) {
if (isVirtual ()) return;
super.setBackgroundGdkRGBA (rgba);
}
@Override
void setForegroundGdkRGBA (GdkRGBA rgba) {
if (isVirtual ()) return;
super.setForegroundGdkRGBA (rgba);
}
@Override
void setFontDescription (long font) {
if (isVirtual ()) return;
super.setFontDescription (font);
}
@Override
public void redraw (int x, int y, int width, int height, boolean all) {
checkWidget();
if (isVirtual ()) {
parent.redraw (x + virtualX, y + virtualY, width, height, all);
return;
}
super.redraw (x, y, width, height, all);
}
@Override
void redraw (boolean all) {
checkWidget();
if (isVirtual ()) {
parent.redraw (virtualX, virtualY, virtualWidth, virtualHeight, all);
return;
}
super.redraw (all);
}
@Override
void update (boolean all, boolean flush) {
checkWidget();
if (isVirtual ()) {
parent.update (all, flush);
return;
}
super.update (all, flush);
}
@Override
public void setVisible (boolean visible) {
checkWidget();
if (isVirtual ()) {
if (((state & HIDDEN) == 0) == visible) return;
if (visible) {
sendEvent (SWT.Show);
if (isDisposed ()) return;
state &= ~HIDDEN;
} else {
state |= HIDDEN;
sendEvent (SWT.Hide);
}
return;
}
super.setVisible (visible);
}
@Override
public void setEnabled (boolean enabled) {
checkWidget();
if (isVirtual ()) {
if (((state & DISABLED) == 0) == enabled) return;
if (enabled) {
state &= ~DISABLED;
} else {
state |= DISABLED;
}
return;
}
super.setEnabled (enabled);
}
@Override
public boolean print (GC gc) {
checkWidget ();
if (gc == null) error (SWT.ERROR_NULL_ARGUMENT);
if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
if (isVirtual ()) {
return false;
}
return super.print (gc);
}
@Override
void printWidget (GC gc, long drawable, int depth, int x, int y) {
if (isVirtual ()) {
Control [] children = _getChildren ();
for (int i=children.length-1; i>=0; --i) {
Control child = children [i];
if (child.getVisible ()) {
Point location = child.getLocation ();
child.printWidget (gc, drawable, depth, x + location.x, y + location.y);
}
}
return;
}
super.printWidget (gc, drawable, depth, x, y);
}
@Override
public Point toControl (int x, int y) {
checkWidget ();
if (isVirtual ()) {
Point parentPoint = parent.toControl (x, y);
return new Point (parentPoint.x - virtualX, parentPoint.y - virtualY);
}
return super.toControl (x, y);
}
@Override
public Point toDisplay (int x, int y) {
checkWidget ();
if (isVirtual ()) {
return parent.toDisplay (x + virtualX, y + virtualY);
}
return super.toDisplay (x, y);
}
@Override
int getChildrenCount () {
if (isVirtual ()) {
return _getChildren ().length;
}
return super.getChildrenCount ();
}
@Override
int getClientWidth () {
if (isVirtual ()) {
return virtualWidth;
}
return super.getClientWidth ();
}- Fully-qualify java.util.List for the virtualChildren field; the bare name List resolves to org.eclipse.swt.widgets.List in this package, which broke compilation of the gtk fragment (and therefore every build job, since the Tycho reactor compiles all fragments). - Mirror the SWT.VIRTUAL class Javadoc into the win32 and cocoa Composite so the JavaDoc-consistency check passes. The style is still only implemented on GTK; on win32/cocoa it currently degrades to a normal Composite. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
Address review feedback: a virtual (handle-less) Composite must not call native GTK/GDK functions on its null handle. Override the affected Control methods for the virtual case: - setBackgroundGdkRGBA / setForegroundGdkRGBA / setFontDescription: no-op. - redraw / update: delegate to the parent at translated coordinates. - toControl / toDisplay: translate through the parent's coordinate space. - print / printWidget: skip the native snapshot; printWidget recurses into children. - getClientWidth: return the tracked virtual width (used by mirrored layout). - setVisible / setEnabled: update logical state and fire events without touching the (absent) native widget. Note: descendant native controls are not yet cascaded; documented as a known prototype limitation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
Port the virtual (handle-less) Composite to Win32, where the payoff is largest: it avoids creating an HWND per layout container, reducing native control count and nesting depth (Windows has an undocumented ~49-level limit on nested controls). Composite (win32): - checkStyle clears H_SCROLL/V_SCROLL/BORDER for virtual composites. - createHandle skips CreateWindowEx; createWidget skips native setup and registers the composite with its parent in Java. - parentingHandle() returns the nearest non-virtual ancestor's HWND; children attach there. - _getChildren enumerates the parenting window's children filtered by logical parent and appends Java-tracked virtual children. - Geometry (getClientAreaInPixels/getBoundsInPixels/getLocationInPixels/ setVirtualBounds) is tracked in Java; children are shifted and relaid out manually since there is no native WM_SIZE. - register/deregister/forceFocus/isReparentable/print and the redraw/update/toControlInPixels/toDisplayInPixels/setVisible/setEnabled paths are guarded so no native call is made on a null handle. Control (win32): - widgetParent() routes through parent.parentingHandle(). - parentingOffset() / moveHandleBy() and offset handling in setBoundsInPixels/getBoundsInPixels/getLocationInPixels translate child coordinates between the logical (virtual) parent and the real ancestor. Behaviour for non-virtual composites is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
Test Results 159 files - 23 159 suites - 23 21m 57s ⏱️ - 3m 39s For more details on these errors, see this check. Results for commit 72a8be4. ± Comparison against base commit 7f9089e. This pull request removes 145 and adds 2 tests. Note that renamed tests count towards both.This pull request skips 198 tests.♻️ This comment has been updated with latest results. |
Table and Tree extend Composite and use SWT.VIRTUAL to enable lazy (virtual) items. The new Composite.isVirtual() was defined purely as (style & SWT.VIRTUAL) != 0, so it incorrectly returned true for virtual Tables/Trees, causing the handle-less Composite overrides to hijack them (wrong client area/bounds, skipped native setup) and corrupt the GTK widget table (g_object_get_qdata index -1). Override isVirtual() to return false in Table and Tree (GTK and Win32) so the handle-less behaviour applies only to plain Composites created with SWT.VIRTUAL. Fixes the regressions in Table/Tree test_Virtual and the cascading widget-table errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
The two SWT.VIRTUAL tests were added to Test_org_eclipse_swt_widgets_Composite, which is a base class for ~70 widget test classes (Browser, CCombo, Table, Tree, ...). As a result they were inherited and re-run for every subclass, inflating suite runtime by ~5 minutes and creating a large amount of extra widget churn that grew the GTK Display widget table to its 1024 threshold and intermittently tripped a latent index-reuse error (g_object_get_qdata index -1) in unrelated tests such as test_Constructor. Move the tests into a standalone leaf class (Test_org_eclipse_swt_widgets_Composite_Virtual) registered in AllWidgetTests so they run exactly once, in the proper context. No production code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
Summary
Experimental, GTK-only implementation of a virtual (handle-less)
Composite, addressing eclipse-platform/eclipse.platform.swt#624.A
Compositecreated withSWT.VIRTUALcreates no native widget. Its children are parented to the nearest non-virtual ancestor, while the virtual composite continues to act as a layout container. This reduces the number of native controls and the nesting depth of the native widget hierarchy — the motivation in the issue (resize flicker, double-buffering cost, and platform limits on deeply nested controls, e.g. the ~49-level HWND limit on Windows).Mechanism
createHandle()skips native widget creation forSWT.VIRTUAL.parentingHandle()walks up to the nearest non-virtual ancestor, so children attach to that ancestor's native container._getChildren()filters native children by logical parent and appends Java-tracked virtual children (handle-less children are otherwise invisible to native enumeration and to disposal/reskin walks).Control.parentingOffset()), and descendants are shifted / re-laid-out manually since there is no native size-allocate signal.checkStyle()clearsH_SCROLL/V_SCROLL/BORDERfor virtual composites.Scope & limitations
A virtual composite is intended purely as a layout/grouping node. With no native surface it cannot scroll, draw a border/background, paint, take focus, or receive its own mouse/keyboard events, and it does not clip its children. Reparenting is disabled (
isReparentable() == false). Interleaving virtual and non-virtual siblings under the same parent does not preserve strict creation order.Not yet implemented:
setVisible/setEnabledcascade, and the Win32 / Cocoa implementations (where the real HWND-nesting payoff lives). On those platforms the style is currently ignored and the composite behaves as a normalComposite.Tests
Two new cross-platform tests in
Test_org_eclipse_swt_widgets_Composite(test_VIRTUAL_actsAsLayoutContainer,test_VIRTUAL_nested) assert behaviour that holds on GTK (virtual path) and on Win32/Cocoa (style ignored → normal composite).Status
🤖 Generated with Claude Code
https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb
Generated by Claude Code