fix(electrobun): wrap WebView in GtkScrolledWindow for resize-in
Codex review #2 found: set_size_request only controls minimum, not natural/preferred size. WebView still reports content size as natural, causing rubber-band effect during shrink. Fix: reparent WebKitWebView into GtkScrolledWindow via FFI with propagate-natural-width/height=FALSE and min-content=1x1. This decouples WebView content size from window size negotiation. Also reverted to native begin_resize_drag (WM handles everything).
This commit is contained in:
parent
d1583f8ce4
commit
300bd30ca3
2 changed files with 132 additions and 2 deletions
|
|
@ -66,6 +66,62 @@ function getGtk() {
|
|||
args: [FFIType.ptr],
|
||||
returns: FFIType.ptr, // GList*
|
||||
},
|
||||
// GtkScrolledWindow — wraps WebView to decouple natural size
|
||||
gtk_scrolled_window_new: {
|
||||
args: [FFIType.ptr, FFIType.ptr], // hadjustment, vadjustment (both null)
|
||||
returns: FFIType.ptr,
|
||||
},
|
||||
gtk_scrolled_window_set_policy: {
|
||||
args: [FFIType.ptr, FFIType.i32, FFIType.i32], // hscrollbar_policy, vscrollbar_policy
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_scrolled_window_set_propagate_natural_width: {
|
||||
args: [FFIType.ptr, FFIType.bool],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_scrolled_window_set_propagate_natural_height: {
|
||||
args: [FFIType.ptr, FFIType.bool],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_scrolled_window_set_min_content_width: {
|
||||
args: [FFIType.ptr, FFIType.i32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_scrolled_window_set_min_content_height: {
|
||||
args: [FFIType.ptr, FFIType.i32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// Reparenting
|
||||
gtk_container_remove: {
|
||||
args: [FFIType.ptr, FFIType.ptr], // container, widget
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_container_add: {
|
||||
args: [FFIType.ptr, FFIType.ptr], // container, widget
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_widget_show_all: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// Expand flags
|
||||
gtk_widget_set_hexpand: {
|
||||
args: [FFIType.ptr, FFIType.bool],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
gtk_widget_set_vexpand: {
|
||||
args: [FFIType.ptr, FFIType.bool],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// Reference counting (prevent widget destruction during reparent)
|
||||
g_object_ref: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.ptr,
|
||||
},
|
||||
g_object_unref: {
|
||||
args: [FFIType.ptr],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
// GList traversal
|
||||
g_list_length: {
|
||||
args: [FFIType.ptr],
|
||||
|
|
@ -233,6 +289,75 @@ export function gtkSetFrame(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the WebKitWebView inside a GtkScrolledWindow to decouple its
|
||||
* natural/preferred size from the window's size negotiation.
|
||||
* This prevents the "rubber band" effect during resize-in.
|
||||
*
|
||||
* Codex review: GtkScrolledWindow has propagate-natural-width/height = FALSE
|
||||
* by default, which stops the child's natural size from reaching the window.
|
||||
*/
|
||||
export function wrapWebViewInScrolledWindow(windowPtr: number | bigint): boolean {
|
||||
const lib = getGtk();
|
||||
if (!lib) return false;
|
||||
try {
|
||||
// Get the window's child (container holding the WebView)
|
||||
const container = lib.symbols.gtk_bin_get_child(windowPtr as any);
|
||||
if (!container) { console.log("[gtk-window] No child container found"); return false; }
|
||||
|
||||
// Get the WebView from the container
|
||||
const webview = lib.symbols.gtk_bin_get_child(container as any);
|
||||
if (!webview) { console.log("[gtk-window] No WebView found in container"); return false; }
|
||||
|
||||
console.log("[gtk-window] Wrapping WebView in GtkScrolledWindow");
|
||||
|
||||
// Ref the WebView so it's not destroyed when removed from container
|
||||
lib.symbols.g_object_ref(webview as any);
|
||||
|
||||
// Remove WebView from its current parent
|
||||
lib.symbols.gtk_container_remove(container as any, webview as any);
|
||||
|
||||
// Create a GtkScrolledWindow
|
||||
const scrolled = lib.symbols.gtk_scrolled_window_new(null, null);
|
||||
if (!scrolled) {
|
||||
// Put WebView back if scrolled window creation failed
|
||||
lib.symbols.gtk_container_add(container as any, webview as any);
|
||||
lib.symbols.g_object_unref(webview as any);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure: no scrollbar policy (automatic), don't propagate natural size
|
||||
const GTK_POLICY_AUTOMATIC = 1;
|
||||
const GTK_POLICY_NEVER = 2;
|
||||
lib.symbols.gtk_scrolled_window_set_policy(scrolled as any, GTK_POLICY_NEVER, GTK_POLICY_NEVER);
|
||||
lib.symbols.gtk_scrolled_window_set_propagate_natural_width(scrolled as any, false);
|
||||
lib.symbols.gtk_scrolled_window_set_propagate_natural_height(scrolled as any, false);
|
||||
lib.symbols.gtk_scrolled_window_set_min_content_width(scrolled as any, 1);
|
||||
lib.symbols.gtk_scrolled_window_set_min_content_height(scrolled as any, 1);
|
||||
|
||||
// Set expand flags
|
||||
lib.symbols.gtk_widget_set_hexpand(scrolled as any, true);
|
||||
lib.symbols.gtk_widget_set_vexpand(scrolled as any, true);
|
||||
lib.symbols.gtk_widget_set_size_request(scrolled as any, 1, 1);
|
||||
|
||||
// Add WebView to ScrolledWindow
|
||||
lib.symbols.gtk_container_add(scrolled as any, webview as any);
|
||||
lib.symbols.g_object_unref(webview as any); // balance the ref
|
||||
|
||||
// Add ScrolledWindow to the original container
|
||||
lib.symbols.gtk_container_add(container as any, scrolled as any);
|
||||
|
||||
// Show everything
|
||||
lib.symbols.gtk_widget_show_all(scrolled as any);
|
||||
|
||||
console.log("[gtk-window] WebView successfully wrapped in GtkScrolledWindow");
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("[gtk-window] wrapWebViewInScrolledWindow failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Edge string → GDK_EDGE mapping
|
||||
const EDGE_MAP: Record<string, GdkEdge> = {
|
||||
n: GDK_EDGE.N, s: GDK_EDGE.S, e: GDK_EDGE.E, w: GDK_EDGE.W,
|
||||
|
|
|
|||
|
|
@ -233,10 +233,15 @@ mainWindow = new BrowserWindow({
|
|||
},
|
||||
});
|
||||
|
||||
// Ensure GTK window is resizable (titleBarStyle:"hidden" may clear the flag)
|
||||
// Ensure GTK window is resizable and wrap WebView for proper resize-in
|
||||
{
|
||||
const { ensureResizable } = require("./gtk-window.ts");
|
||||
const { ensureResizable, wrapWebViewInScrolledWindow } = require("./gtk-window.ts");
|
||||
ensureResizable((mainWindow as any).ptr);
|
||||
// Wrap WebView in GtkScrolledWindow to decouple natural size from window size
|
||||
// (prevents rubber-band effect when shrinking)
|
||||
setTimeout(() => {
|
||||
wrapWebViewInScrolledWindow((mainWindow as any).ptr);
|
||||
}, 2000); // Delay to ensure WebView is fully initialized
|
||||
}
|
||||
|
||||
// Prevent GTK's false Ctrl+click detection from closing the window on initial load.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue