Skip to content

Commit b5dd09b

Browse files
committed
feat(detach): hand birth_content the window's real size
Now that geometry restores a detached window's remembered size inside open_detached's build(), DetachSpec's width/height describe only the never-popped-before case — but all three consumers computed their docked content's birth rect from exactly those constants, so a re-popped tab's content sat visibly the wrong size until detach.html reported the true hole. Pass the built window's real logical inner size into birth_content rather than documenting the trap: a caller can no longer use the stale value without reaching past the one it was handed. A failed geometry query falls back to spec's size — a stale birth rect costs one frame, refusing to dock costs the pop-out.
1 parent edf8d6a commit b5dd09b

2 files changed

Lines changed: 31 additions & 9 deletions

File tree

‎CLAUDE.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,15 @@ regardless of what it hosts. It is NOT a place to abstract things that merely *l
218218
launch and orphan an entry each time. Every consumer already derives it from a stable
219219
identity (warden hashes `origin_label:tab_key`; curator and lector hash the tab's own label),
220220
and each pins that determinism with its own test.
221-
- **`DetachSpec`'s `width`/`height` are the first-pop-out default only.** Restore overwrites
222-
them inside `open_detached`'s `build()` for any tab popped out before, so a consumer sizing
223-
its docked content must read the built window's real `inner_size()` inside `birth_content`,
224-
never the constants it passed in. warden hit exactly this: its reparented native surface was
225-
born at the default rect and only snapped right once `detach.html` reported the true hole.
221+
- **`DetachSpec`'s `width`/`height` are the first-pop-out default only** — restore overwrites
222+
them inside `open_detached`'s `build()` for any tab popped out before. So `birth_content` is
223+
handed the window's **real** logical inner size as its second argument, and a consumer sizes
224+
its docked content from that. All three consumers computed a birth rect from the constants
225+
before this, and all three were wrong the moment geometry started being remembered; handing
226+
the fact over beats documenting the trap, since the stale value is now the one you'd have to
227+
reach past. (The cost of getting it wrong is bounded — one frame, until `detach.html`'s
228+
`set_hole_rect` reports the true hole — which is also why a failed geometry query falls back
229+
to `spec`'s size rather than failing the pop-out.)
226230
- **A banner-only shell page** (`detach.html` — title + origin accent, no sidebar), served over
227231
its own custom protocol `DETACH_SCHEME`, registered on the `Builder` by
228232
`register_detach_protocol` (chained into `register_plugins` alongside `home`'s). Same reasoning

‎src/detach.rs‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ const DETACH_HTML: &str = include_str!("detach.html");
6565
///
6666
/// `width`/`height` are the **first-pop-out default only**. Once this tab has been popped out and
6767
/// resized, [`crate::geometry`] restores its remembered size and position over them during
68-
/// [`open_detached`]'s `build()` — so a caller sizing its own content off these constants rather
69-
/// than off the built window's real `inner_size()` will get it wrong for every subsequent pop-out.
68+
/// [`open_detached`]'s `build()`. That is why `birth_content` is handed the window's real size —
69+
/// size docked content from *that*, never from these fields.
7070
pub struct DetachSpec {
7171
pub title: String,
7272
pub colour: Option<String>,
@@ -125,6 +125,15 @@ pub(crate) fn register_detach_protocol<R: tauri::Runtime>(
125125
/// rect) — on `Err`, the freshly-built window is closed and the error propagated, so a failed dock
126126
/// never leaves an empty banner-only window behind. On success, returns the window's label so the
127127
/// caller can look it up again (e.g. to call [`wire_return`]).
128+
///
129+
/// **`birth_content` is handed the window's real logical inner size, and must size its content
130+
/// from that rather than from the `spec` it passed in.** By the time the closure runs,
131+
/// [`crate::geometry`] has already restored this tab's remembered size over `spec`'s — its
132+
/// `on_window_ready` hook runs inside `build()` — so `spec.width`/`spec.height` describe only the
133+
/// never-popped-before case. Every consumer computes a birth rect for its docked content, and all
134+
/// three got this wrong from the constants before the size was passed in: handing over the fact
135+
/// beats documenting the trap, since a caller cannot use the stale value without reaching past the
136+
/// one it was given. Falls back to `spec`'s size if the window's geometry can't be queried.
128137
pub fn open_detached<R, F>(
129138
app: &tauri::AppHandle<R>,
130139
token: &str,
@@ -134,7 +143,7 @@ pub fn open_detached<R, F>(
134143
) -> tauri::Result<String>
135144
where
136145
R: tauri::Runtime,
137-
F: FnOnce(&tauri::WebviewWindow<R>) -> tauri::Result<()>,
146+
F: FnOnce(&tauri::WebviewWindow<R>, tauri::LogicalSize<f64>) -> tauri::Result<()>,
138147
{
139148
let label = detached_label(token);
140149
let payload = detach_payload_json(spec, app_name);
@@ -158,7 +167,16 @@ where
158167
))
159168
.build()?;
160169

161-
if let Err(e) = birth_content(&window) {
170+
// The size the window ACTUALLY has, which is `spec`'s only when this tab has no remembered
171+
// geometry — see this function's doc. A failed query falls back to `spec`'s size rather than
172+
// erroring: a birth rect that is merely stale costs one frame (`detach.html`'s `set_hole_rect`
173+
// corrects it), where refusing to dock would cost the whole pop-out.
174+
let real_size = window
175+
.scale_factor()
176+
.and_then(|scale| window.inner_size().map(|s| s.to_logical::<f64>(scale)))
177+
.unwrap_or_else(|_| tauri::LogicalSize::new(spec.width, spec.height));
178+
179+
if let Err(e) = birth_content(&window, real_size) {
162180
let _ = window.close();
163181
return Err(e);
164182
}

0 commit comments

Comments
 (0)