Skip to content

[GTK] Prototype SWT.VIRTUAL support for Composite (issue 624) - #15

Open
vogella wants to merge 6 commits into
masterfrom
claude/brave-cerf-mimjt1
Open

[GTK] Prototype SWT.VIRTUAL support for Composite (issue 624)#15
vogella wants to merge 6 commits into
masterfrom
claude/brave-cerf-mimjt1

Conversation

@vogella

@vogella vogella commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

Experimental, GTK-only implementation of a virtual (handle-less) Composite, addressing eclipse-platform/eclipse.platform.swt#624.

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 — 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 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 (handle-less children are otherwise invisible to native enumeration and to disposal/reskin walks).
  • Geometry is tracked in Java; child layout coordinates are translated by the cumulative virtual-ancestor offset (Control.parentingOffset()), and descendants are shifted / re-laid-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.

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/setEnabled cascade, 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 normal Composite.

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

⚠️ This was developed without a local GTK native build or display, so it has not been compiled or run. It is published to let CI compile it and run the suite. Treat it as a starting point / RFC rather than a finished feature.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SQvhWB835Yq5KUwvXaE4Zb


Generated by Claude Code

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +2158 to +2163
@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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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:

  1. Color & Font Settings (setBackgroundGdkRGBA, setForegroundGdkRGBA, setFontDescription): Prevent passing 0 to GTK style context and CSS provider functions.
  2. Redraw & Update (redraw, update): Recursively delegate to the parent composite with translated coordinates instead of querying the non-existent native widget/window.
  3. Visibility & Enablement (setVisible, setEnabled): Safely update the internal states and send events without calling native GTK show/hide/realize functions on a null handle.
  4. Printing (print, printWidget): Prevent native crashes when printing a parent container that contains virtual composites by recursively printing children.
  5. 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.
  6. 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 ();
}

claude added 3 commits June 22, 2026 18:22
- 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
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Test Results

  159 files   -    23    159 suites   - 23   21m 57s ⏱️ - 3m 39s
4 580 tests  -   143  4 360 ✅  -   340  217 💤 +194  0 ❌ ±0  3 🔥 +3 
5 512 runs   - 1 300  5 376 ✅  - 1 273  133 💤  -  30  0 ❌ ±0  3 🔥 +3 

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.
org.eclipse.swt.browser.EdgeTests ‑ handlingOfInvalidFileURIs
org.eclipse.swt.browser.EdgeTests ‑ handlingOfNonFileURIs
org.eclipse.swt.browser.EdgeTests ‑ handlingOfTempDirWithSpacesAndUnicodeCharacters
org.eclipse.swt.browser.EdgeTests ‑ handlingOfUpperLowerCase
org.eclipse.swt.graphics.CursorWin32Tests ‑ testDestroyHandlesExceptPreservesRetainedHandle
org.eclipse.swt.graphics.CursorWin32Tests ‑ testDisposedCursorReturnsZeroHandle
org.eclipse.swt.graphics.CursorWin32Tests ‑ testHandleIsCachedForSameZoomLevel
org.eclipse.swt.graphics.CursorWin32Tests ‑ testImageDataCursorProducesDifferentHandlesForDifferentZoomLevels
org.eclipse.swt.graphics.GCWin32Tests ‑ drawnElementsShouldScaleUpToTheRightZoomLevel
org.eclipse.swt.graphics.GCWin32Tests ‑ gcZoomLevelMustChangeOnShellZoomChange
…
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Composite_Virtual ‑ test_VIRTUAL_actsAsLayoutContainer
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Composite_Virtual ‑ test_VIRTUAL_nested
This pull request skips 198 tests.
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser ‑ test_BrowserFunction_availableBeforePageScripts_issue20
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser ‑ test_BrowserFunction_availableOnLoad_concurrentInstances_issue20
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser ‑ test_Constructor_multipleInstantiationsInDifferentThreads
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_afterPageReload
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_stackedCalls
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_with_String
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_with_boolean
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_with_integer
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_browser_Browser_IE ‑ test_BrowserFunction_callback_with_javaReturningInt
…

♻️ This comment has been updated with latest results.

claude added 2 commits June 22, 2026 18:50
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants