fix(electrobun): enable window resize via recursive GTK min-size override
Root cause: WebKitWebView requests min_size = content_size (5120x1387 on ultrawide), which GTK propagates to WM_NORMAL_HINTS, blocking all resize. Fix: recursively walk the GTK widget tree (GtkWindow → GtkBin → WebView) and call gtk_widget_set_size_request(-1, -1) on every widget. Then set gtk_window_set_geometry_hints with min=400x300. Result: WM_NORMAL_HINTS now shows min=10x10, window is fully resizable.
This commit is contained in:
parent
9da9d96ebd
commit
d84feb6c67
3 changed files with 134 additions and 10 deletions
|
|
@ -31,6 +31,46 @@ function getGtk() {
|
|||
args: [FFIType.ptr, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.u32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_window_get_resizable: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.bool,
|
||||
},
|
||||
gtk_window_set_resizable: {
|
||||
args: [FFIType.ptr, FFIType.bool],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// GtkWidget — controls minimum size request
|
||||
gtk_widget_set_size_request: {
|
||||
args: [FFIType.ptr, FFIType.i32, FFIType.i32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// GtkWindow — override geometry hints (min/max size sent to WM)
|
||||
gtk_window_set_geometry_hints: {
|
||||
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.i32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// Container traversal
|
||||
gtk_bin_get_child: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.ptr,
|
||||
},
|
||||
gtk_container_get_children: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.ptr, // GList*
|
||||
},
|
||||
// GList traversal
|
||||
g_list_length: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.u32,
|
||||
},
|
||||
g_list_nth_data: {
|
||||
args: [FFIType.ptr, FFIType.u32],
|
||||
returns: FFIType.ptr,
|
||||
},
|
||||
g_list_free: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
});
|
||||
return gtk3;
|
||||
} catch (err) {
|
||||
|
|
@ -39,6 +79,82 @@ function getGtk() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure window is resizable at GTK level. Must be called after window creation.
|
||||
*/
|
||||
export function ensureResizable(windowPtr: number | bigint): boolean {
|
||||
const lib = getGtk();
|
||||
if (!lib) return false;
|
||||
try {
|
||||
const isResizable = lib.symbols.gtk_window_get_resizable(windowPtr as any);
|
||||
console.log(`[gtk-window] gtk_window_get_resizable = ${isResizable}`);
|
||||
if (!isResizable) {
|
||||
console.log("[gtk-window] Window NOT resizable — forcing set_resizable(true)");
|
||||
lib.symbols.gtk_window_set_resizable(windowPtr as any, true);
|
||||
const nowResizable = lib.symbols.gtk_window_get_resizable(windowPtr as any);
|
||||
console.log(`[gtk-window] After set_resizable(true): ${nowResizable}`);
|
||||
}
|
||||
// Override minimum size on the entire widget tree.
|
||||
// WebKitWebView requests min_size = content_size, which GTK propagates
|
||||
// to WM_NORMAL_HINTS, blocking resize. Fix: set -1 (no minimum) recursively.
|
||||
console.log("[gtk-window] Resetting minimum size constraints on widget tree");
|
||||
|
||||
function clearMinSize(widget: any, depth: number) {
|
||||
if (!widget || depth > 10) return;
|
||||
lib!.symbols.gtk_widget_set_size_request(widget, -1, -1);
|
||||
// Try as GtkBin (single child)
|
||||
try {
|
||||
const child = lib!.symbols.gtk_bin_get_child(widget);
|
||||
if (child) {
|
||||
console.log(`[gtk-window] ${" ".repeat(depth)}child (bin)`);
|
||||
clearMinSize(child, depth + 1);
|
||||
}
|
||||
} catch { /* not a GtkBin */ }
|
||||
// Try as GtkContainer (multiple children)
|
||||
try {
|
||||
const list = lib!.symbols.gtk_container_get_children(widget);
|
||||
if (list) {
|
||||
const len = lib!.symbols.g_list_length(list);
|
||||
console.log(`[gtk-window] ${" ".repeat(depth)}container (${len} children)`);
|
||||
for (let i = 0; i < len && i < 20; i++) {
|
||||
const child = lib!.symbols.g_list_nth_data(list, i);
|
||||
if (child) clearMinSize(child, depth + 1);
|
||||
}
|
||||
lib!.symbols.g_list_free(list);
|
||||
}
|
||||
} catch { /* not a GtkContainer */ }
|
||||
}
|
||||
clearMinSize(windowPtr as any, 0);
|
||||
// Set geometry hints with small minimum via GdkGeometry struct
|
||||
// GdkGeometry: min_width(i32), min_height(i32), ... (rest are padding)
|
||||
// GdkWindowHints: GDK_HINT_MIN_SIZE = 1<<1 = 2
|
||||
try {
|
||||
// Allocate GdkGeometry struct (18 ints = 72 bytes)
|
||||
const buf = new ArrayBuffer(72);
|
||||
const view = new Int32Array(buf);
|
||||
view[0] = 400; // min_width
|
||||
view[1] = 300; // min_height
|
||||
view[2] = 32767; // max_width
|
||||
view[3] = 32767; // max_height
|
||||
const GDK_HINT_MIN_SIZE = 2;
|
||||
const GDK_HINT_MAX_SIZE = 4;
|
||||
lib.symbols.gtk_window_set_geometry_hints(
|
||||
windowPtr as any,
|
||||
null, // widget = null → applies to window itself
|
||||
ptr(buf) as any, // GdkGeometry*
|
||||
GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE,
|
||||
);
|
||||
console.log("[gtk-window] Set geometry hints: min=400×300 max=32767×32767");
|
||||
} catch (err) {
|
||||
console.error("[gtk-window] geometry hints failed:", err);
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("[gtk-window] ensureResizable failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate resize to the window manager.
|
||||
* The WM handles cursor, animation, constraints — zero CPU from us.
|
||||
|
|
|
|||
|
|
@ -114,7 +114,9 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
|
|||
const { beginResizeDrag, edgeStringToGdk } = require("./gtk-window.ts");
|
||||
const gdkEdge = edgeStringToGdk(edge);
|
||||
if (gdkEdge === null) return { ok: false, error: `Unknown edge: ${edge}` };
|
||||
console.log(`[resize] edge=${edge} gdkEdge=${gdkEdge} btn=${button} rootX=${rootX} rootY=${rootY} ptr=${(mainWindow as any).ptr}`);
|
||||
const ok = beginResizeDrag((mainWindow as any).ptr, gdkEdge, button, rootX, rootY);
|
||||
console.log(`[resize] result: ${ok}`);
|
||||
return { ok };
|
||||
} catch (err) { console.error("[window.beginResize]", err); return { ok: false }; }
|
||||
},
|
||||
|
|
@ -219,6 +221,12 @@ mainWindow = new BrowserWindow({
|
|||
},
|
||||
});
|
||||
|
||||
// Ensure GTK window is resizable (titleBarStyle:"hidden" may clear the flag)
|
||||
{
|
||||
const { ensureResizable } = require("./gtk-window.ts");
|
||||
ensureResizable((mainWindow as any).ptr);
|
||||
}
|
||||
|
||||
// Prevent GTK's false Ctrl+click detection from closing the window on initial load.
|
||||
// WebKitGTK reports stale modifier state (0x14 = Ctrl+Alt) after SIGTERM of previous instance,
|
||||
// which Electrobun interprets as a Cmd+click → "open in new window" → closes the main window.
|
||||
|
|
|
|||
|
|
@ -618,16 +618,16 @@
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Resize handles ─────────────────────────────────── */
|
||||
.rz { position: fixed; z-index: 9999; }
|
||||
.rz-n { top: 0; left: 4px; right: 4px; height: 4px; cursor: n-resize; }
|
||||
.rz-s { bottom: 0; left: 4px; right: 4px; height: 4px; cursor: s-resize; }
|
||||
.rz-e { right: 0; top: 4px; bottom: 4px; width: 4px; cursor: e-resize; }
|
||||
.rz-w { left: 0; top: 4px; bottom: 4px; width: 4px; cursor: w-resize; }
|
||||
.rz-ne { top: 0; right: 0; width: 8px; height: 8px; cursor: ne-resize; }
|
||||
.rz-nw { top: 0; left: 0; width: 8px; height: 8px; cursor: nw-resize; }
|
||||
.rz-se { bottom: 0; right: 0; width: 8px; height: 8px; cursor: se-resize; }
|
||||
.rz-sw { bottom: 0; left: 0; width: 8px; height: 8px; cursor: sw-resize; }
|
||||
/* ── Resize handles — 6px edges, 12px corners, fixed on viewport ── */
|
||||
.rz { position: fixed; z-index: 9999; background: rgba(255,0,0,0.15); }
|
||||
.rz-n { top: 0; left: 12px; right: 12px; height: 6px; cursor: n-resize; }
|
||||
.rz-s { bottom: 0; left: 12px; right: 12px; height: 6px; cursor: s-resize; }
|
||||
.rz-e { right: 0; top: 12px; bottom: 12px; width: 6px; cursor: e-resize; }
|
||||
.rz-w { left: 0; top: 12px; bottom: 12px; width: 6px; cursor: w-resize; }
|
||||
.rz-ne { top: 0; right: 0; width: 12px; height: 12px; cursor: ne-resize; }
|
||||
.rz-nw { top: 0; left: 0; width: 12px; height: 12px; cursor: nw-resize; }
|
||||
.rz-se { bottom: 0; right: 0; width: 12px; height: 12px; cursor: se-resize; }
|
||||
.rz-sw { bottom: 0; left: 0; width: 12px; height: 12px; cursor: sw-resize; }
|
||||
|
||||
.app-shell {
|
||||
flex: 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue