Skip to content

Commit e2fbf82

Browse files
committed
[Gtk4] Overhaul Shell sizing
Non-resizable shells (e.g. DIALOG_TRIM) appear too tall when setSize(computeSize()) is called before open() because window decoration heights were not correctly accounted for with GTK4 CSD. Maximized shells had their content cut off at the bottom because the shell content was not resized to fill the full maximized area. Use the layout signal, which fires after the the new dimensions are set, to correctly resize the shell content to fill the maximized window.
1 parent acfd831 commit e2fbf82

4 files changed

Lines changed: 102 additions & 46 deletions

File tree

bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ public static String getEnvironmentalVariable (String envVarName) {
468468
public static final byte[] notify_theme_change = ascii("notify::gtk-application-prefer-dark-theme");
469469
public static final byte[] response = ascii("response");
470470
public static final byte[] compute_size = ascii("compute-size");
471+
public static final byte[] layout = ascii("layout");
471472

472473
/** Properties */
473474
public static final byte[] active = ascii("active");

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ public class Display extends Device implements Executor {
134134
long eventProc, windowProc2, windowProc3, windowProc4, windowProc5, windowProc6;
135135
long changeValueProc;
136136
long snapshotDrawProc, keyPressReleaseProc, focusProc, windowActiveProc, enterMotionProc, leaveProc,
137-
scrollProc, resizeProc, activateProc, gesturePressReleaseProc;
137+
scrollProc, resizeProc, layoutProc, activateProc, gesturePressReleaseProc;
138138
long notifyProc;
139139
long computeSizeProc;
140140
Callback windowCallback2, windowCallback3, windowCallback4, windowCallback5, windowCallback6;
141141
Callback changeValue;
142142
Callback snapshotDraw, keyPressReleaseCallback, focusCallback, windowActiveCallback, enterMotionCallback, computeSizeCallback,
143-
scrollCallback, leaveCallback, resizeCallback, activateCallback, gesturePressReleaseCallback;
143+
scrollCallback, leaveCallback, resizeCallback, layoutCallback, activateCallback, gesturePressReleaseCallback;
144144
Callback notifyCallback;
145145
EventTable eventTable, filterTable;
146146
static String APP_NAME = "SWT"; //$NON-NLS-1$
@@ -3631,6 +3631,8 @@ void initializeCallbacks () {
36313631

36323632
resizeCallback = new Callback(this, "resizeProc", void.class, new Type[] {long.class, int.class, int.class}); //$NON-NLS-1$
36333633
resizeProc = resizeCallback.getAddress();
3634+
layoutCallback = new Callback(this, "layoutProc", void.class, new Type[] {long.class, int.class, int.class, long.class}); //$NON-NLS-1$
3635+
layoutProc = layoutCallback.getAddress();
36343636

36353637
activateCallback = new Callback(this, "activateProc", void.class, new Type[] {long.class, long.class, long.class}); //$NON-NLS-1$
36363638
activateProc = activateCallback.getAddress();
@@ -4671,6 +4673,9 @@ void releaseDisplay () {
46714673
resizeCallback.dispose();
46724674
resizeCallback = null;
46734675
resizeProc = 0;
4676+
layoutCallback.dispose();
4677+
layoutCallback = null;
4678+
layoutProc = 0;
46744679

46754680
activateCallback.dispose();
46764681
activateCallback = null;
@@ -6155,6 +6160,11 @@ void resizeProc(long handle, int width, int height) {
61556160
if (widget != null) widget.gtk_size_allocate(handle, 0);
61566161
}
61576162

6163+
void layoutProc(long surface, int width, int height, long user_data) {
6164+
Widget widget = getWidget(user_data);
6165+
if (widget != null) widget.gtk_layout(surface, width, height);
6166+
}
6167+
61586168
long notifyProc (long object, long param_spec, long user_data) {
61596169
Widget widget = getWidget (object);
61606170
if (widget == null) {

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java

Lines changed: 86 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -723,14 +723,6 @@ Rectangle computeTrimInPixels (int x, int y, int width, int height) {
723723
trim.y -= menuBarHeight;
724724
trim.height += menuBarHeight;
725725
}
726-
if (GTK.GTK4 && OS.isWayland()) {
727-
long titlebar = GTK4.gtk_window_get_titlebar(shellHandle);
728-
if (titlebar != 0) {
729-
int titleBarHeight = GTK4.gtk_widget_get_height (titlebar);
730-
trim.y -= titleBarHeight;
731-
trim.height += titleBarHeight;
732-
}
733-
}
734726
return trim;
735727
}
736728

@@ -971,6 +963,7 @@ void hookEvents () {
971963
long gdkSurface = gtk_widget_get_surface (shellHandle);
972964
OS.g_signal_connect (gdkSurface, OS.notify_state, display.notifyProc, shellHandle);
973965
OS.g_signal_connect (gdkSurface, OS.compute_size, display.computeSizeProc, shellHandle);
966+
OS.g_signal_connect (gdkSurface, OS.layout, display.layoutProc, shellHandle);
974967
OS.g_signal_connect(shellHandle, OS.notify_default_height, display.notifyProc, Widget.NOTIFY_DEFAULT_HEIGHT);
975968
OS.g_signal_connect(shellHandle, OS.notify_default_width, display.notifyProc, Widget.NOTIFY_DEFAULT_WIDTH);
976969
OS.g_signal_connect(shellHandle, OS.notify_maximized, display.notifyProc, Widget.NOTIFY_MAXIMIZED);
@@ -1812,7 +1805,6 @@ long gtk3_key_press_event (long widget, long event) {
18121805
@Override
18131806
long gtk_size_allocate (long widget, long allocation) {
18141807
int width, height;
1815-
GdkRectangle monitorSize = new GdkRectangle();
18161808
int[] widthA = new int [1];
18171809
int[] heightA = new int [1];
18181810

@@ -1840,11 +1832,14 @@ long gtk_size_allocate (long widget, long allocation) {
18401832
heightA[0] = Math.max(1, heightA[0] - headerNaturalHeight[0]);
18411833
}
18421834
} else {
1843-
long display = GDK.gdk_display_get_default();
1844-
long monitor = GDK.gdk_display_get_monitor_at_surface(display, paintSurface());
1845-
GDK.gdk_monitor_get_geometry(monitor, monitorSize);
1846-
widthA[0] = monitorSize.width;
1847-
heightA[0] = monitorSize.height - headerNaturalHeight[0];
1835+
/*
1836+
* For maximized windows, neither gtk_window_get_default_size() nor
1837+
* gdk_surface_get_width/height() have the new dimensions yet when
1838+
* notify::maximized fires. The GdkSurface::layout signal fires
1839+
* synchronously after the compositor commits the new allocation;
1840+
* gtk_layout() handles the resize from there.
1841+
*/
1842+
return 0;
18481843
}
18491844
} else {
18501845
GTK3.gtk_window_get_size(shellHandle, widthA, heightA);
@@ -1864,6 +1859,37 @@ long gtk_size_allocate (long widget, long allocation) {
18641859
return 0;
18651860
}
18661861

1862+
@Override
1863+
void gtk_layout (long surface, int surfaceWidth, int surfaceHeight) {
1864+
/*
1865+
* Used exclusively for the maximized case:
1866+
* notify::maximized fires before the compositor commits the new size, so
1867+
* gtk_size_allocate returns 0 early for maximized windows and defers here.
1868+
*
1869+
* For non-maximized resizes, sizing is already handled correctly via
1870+
* gtk_window_get_default_size, so those are skipped here to avoid overriding
1871+
* with different surface dimensions.
1872+
*
1873+
* Surface width and height - total GTK window size including the header bar.
1874+
*/
1875+
if (!GTK4.gtk_window_is_maximized(shellHandle)) return;
1876+
1877+
long header = GTK4.gtk_window_get_titlebar(shellHandle);
1878+
int headerH = 0;
1879+
if (header != 0) {
1880+
int[] headerNaturalHeight = new int[1];
1881+
GTK4.gtk_widget_measure(header, GTK.GTK_ORIENTATION_VERTICAL, -1, null, headerNaturalHeight, null, null);
1882+
headerH = headerNaturalHeight[0];
1883+
}
1884+
int newWidth = surfaceWidth;
1885+
int newHeight = Math.max(1, surfaceHeight - headerH);
1886+
if ((!resized || oldWidth != newWidth || oldHeight != newHeight) && newWidth > 0) {
1887+
oldWidth = newWidth;
1888+
oldHeight = newHeight;
1889+
resizeBounds(newWidth, newHeight, true);
1890+
}
1891+
}
1892+
18671893
private void updateDecorations(long gdkResource) {
18681894
if (OS.isX11()) {
18691895
/*
@@ -2345,25 +2371,21 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize
23452371
if (geometry.getMaxHeight() > 0) {
23462372
height = Math.min(height, geometry.getMaxHeight());
23472373
}
2348-
/*
2349-
* If the shell is created without a RESIZE style bit, and the
2350-
* minWidth/minHeight/maxWidth/maxHeight have been set, allow the resize.
2351-
*/
2352-
if ((style & SWT.RESIZE) != 0 || (geometry.getMinHeight() != 0 || geometry.getMinWidth() != 0 || geometry.getMaxHeight() != 0 || geometry.getMaxWidth() != 0)) {
2353-
if (GTK.GTK4) {
2354-
/*
2355-
* On GTK4, GtkWindow size includes the header bar. In order to keep window size allocation of the client area
2356-
* consistent with previous versions of SWT, we need to include the header bar height in addition to the given height value.
2357-
*/
2358-
long header = GTK4.gtk_window_get_titlebar(shellHandle);
2359-
int[] headerNaturalHeight = new int[1];
2360-
if (header != 0) {
2361-
GTK4.gtk_widget_measure(header, GTK.GTK_ORIENTATION_VERTICAL, -1, null, headerNaturalHeight, null, null);
2362-
}
2363-
GTK.gtk_window_set_default_size(shellHandle, width, height + headerNaturalHeight[0]);
2364-
} else {
2365-
GTK3.gtk_window_resize (shellHandle, width, height);
2374+
if (GTK.GTK4) {
2375+
/*
2376+
* GtkWindow size includes the header bar. To stay consistent with previous
2377+
* versions of SWT, header bar height has to be added to the given height value.
2378+
* This applies to all shell styles that have a title bar (e.g. SHELL_TRIM,
2379+
* DIALOG_TRIM).
2380+
*/
2381+
long header = GTK4.gtk_window_get_titlebar(shellHandle);
2382+
int[] headerNaturalHeight = new int[1];
2383+
if (header != 0) {
2384+
GTK4.gtk_widget_measure(header, GTK.GTK_ORIENTATION_VERTICAL, -1, null, headerNaturalHeight, null, null);
23662385
}
2386+
GTK.gtk_window_set_default_size(shellHandle, width, height + headerNaturalHeight[0]);
2387+
} else if ((style & SWT.RESIZE) != 0 || (geometry.getMinHeight() != 0 || geometry.getMinWidth() != 0 || geometry.getMaxHeight() != 0 || geometry.getMaxWidth() != 0)) {
2388+
GTK3.gtk_window_resize (shellHandle, width, height);
23672389
}
23682390
boolean changed = width != oldWidth || height != oldHeight;
23692391
if (changed) {
@@ -2520,19 +2542,17 @@ void setInitialBounds() {
25202542
height = (int) (dest.height * SHELL_TO_MONITOR_RATIO);
25212543
}
25222544

2523-
if ((style & SWT.RESIZE) != 0) {
2524-
/*
2525-
* On GTK4, GtkWindow size includes the header bar. In order to keep window size allocation of the client area
2526-
* consistent with previous versions of SWT, we need to include the header bar height in addition to the given height value.
2527-
*/
2528-
long header = GTK4.gtk_window_get_titlebar(shellHandle);
2529-
int[] headerNaturalHeight = new int[1];
2530-
if (header != 0) {
2531-
GTK4.gtk_widget_measure(header, GTK.GTK_ORIENTATION_VERTICAL, -1, null, headerNaturalHeight, null, null);
2532-
}
2533-
2534-
GTK.gtk_window_set_default_size(shellHandle, width, height + headerNaturalHeight[0]);
2545+
/*
2546+
* GtkWindow size includes the header bar. To stay
2547+
* consistent with previous versions of SWT the header bar height is added the given height value.
2548+
* This applies to all shell styles.
2549+
*/
2550+
long header = GTK4.gtk_window_get_titlebar(shellHandle);
2551+
int[] headerNaturalHeight = new int[1];
2552+
if (header != 0) {
2553+
GTK4.gtk_widget_measure(header, GTK.GTK_ORIENTATION_VERTICAL, -1, null, headerNaturalHeight, null, null);
25352554
}
2555+
GTK.gtk_window_set_default_size(shellHandle, width, height + headerNaturalHeight[0]);
25362556
} else {
25372557
long display = GDK.gdk_display_get_default();
25382558
if (display != 0) {
@@ -3158,6 +3178,24 @@ int trimHeight () {
31583178
// Shells with both ON_TOP and RESIZE set only use border, not trim.
31593179
// See bug 319612.
31603180
if (isCustomResize()) return 0;
3181+
if (GTK.GTK4 && OS.isWayland()) {
3182+
/*
3183+
* On GTK4 Wayland, window decorations are implemented as GTK CSD widgets. The
3184+
* title bar (GtkHeaderBar) is part of the GtkWindow and its height must be
3185+
* queried dynamically. Use gtk_widget_get_height() if already allocated,
3186+
* otherwise gtk_widget_measure() which works even before the window is
3187+
* realized/shown.
3188+
*/
3189+
long titlebar = GTK4.gtk_window_get_titlebar(shellHandle);
3190+
if (titlebar != 0) {
3191+
int height = GTK4.gtk_widget_get_height(titlebar);
3192+
if (height > 0) return height;
3193+
int[] naturalHeight = new int[1];
3194+
GTK4.gtk_widget_measure(titlebar, GTK.GTK_ORIENTATION_VERTICAL, -1, null, naturalHeight, null, null);
3195+
return naturalHeight[0];
3196+
}
3197+
return 0;
3198+
}
31613199
boolean hasTitle = false, hasResize = false, hasBorder = false;
31623200
hasTitle = (style & (SWT.MIN | SWT.MAX | SWT.TITLE | SWT.MENU)) != 0;
31633201
hasResize = (style & SWT.RESIZE) != 0;
@@ -3178,6 +3216,10 @@ int trimWidth () {
31783216
// Shells with both ON_TOP and RESIZE set only use border, not trim.
31793217
// See bug 319612.
31803218
if (isCustomResize()) return 0;
3219+
if (GTK.GTK4 && OS.isWayland()) {
3220+
// On GTK4 Wayland CSD, the title bar adds height only.
3221+
return 0;
3222+
}
31813223
boolean hasTitle = false, hasResize = false, hasBorder = false;
31823224
hasTitle = (style & (SWT.MIN | SWT.MAX | SWT.TITLE | SWT.MENU)) != 0;
31833225
hasResize = (style & SWT.RESIZE) != 0;

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,9 @@ long gtk_size_allocate (long widget, long allocation) {
11121112
return 0;
11131113
}
11141114

1115+
void gtk_layout (long surface, int width, int height) {
1116+
}
1117+
11151118
long gtk_status_icon_popup_menu (long handle, long button, long activate_time) {
11161119
return 0;
11171120
}

0 commit comments

Comments
 (0)