Say what size the window actually is, in both coordinate systems

Every figure in D-N7's table was computed at a guessed scale factor. The
tablet's panel is 3000 by 1920 physical and nothing in this repository
has ever recorded the density Android reports for it, so the dock's
width is either 900 or 1037 and its available height is 200px either
way. N6 asks for the measurement; this is the line that carries it.

Beside the existing `apply_layout_class` call, because that is where the
window is already being asked for its size and its scale, and again on
every resize, so turning the tablet over records the other orientation
in the same logcat. Both coordinate systems on one line: a logical size
cannot be checked when the scale is the thing in doubt, and a physical
size that does not divide by the scale printed next to it says the
reading is of something other than the panel.

The level is not fixed, because none of the three obvious choices works.
`android_main` caps the facade at info, so debug never leaves the
device and a debug-only line answers nothing. A drag emits a resize per
frame and each accepted record is also appended to the on-disk log, so
info on every resize is not a diagnostic. And the first reading is not
the settled one: on X11 the window reports 0x0, then 360x320 at scale
1.0, then 1100x720 at scale 2.0, so reporting only the first would put a
number in logcat that is not the window's.

So the pair that decides is the scale factor and the orientation --
exactly what N6 is asking for, and exactly what a drag leaves alone. A
window with no area is not a reading and records nothing. Every later
change to either half, the scale resolving or the tablet turning over,
is a new answer and goes out at info; everything else is debug.
This commit is contained in:
2026-09-06 20:20:50 +02:00
parent 23a061fd80
commit ed061b8012
2 changed files with 118 additions and 25 deletions
+22 -25
View File
File diff suppressed because one or more lines are too long
+96
View File
@@ -3414,18 +3414,30 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
// Slint because a property that both derives from and feeds the layout is
// a binding loop.
let panels = std::rc::Rc::new(PanelChoices::default());
// N6: what the last window line that carried a measurement reported.
// Shared with the resize callback below, which is where every reading
// after the first one arrives. See `window_metrics_level`.
let reported_geometry = std::rc::Rc::new(std::cell::Cell::new(None::<(bool, u32)>));
{
let weak = window.as_weak();
let panels = panels.clone();
let reported_geometry = reported_geometry.clone();
window.on_window_resized(move |width| {
let Some(window) = weak.upgrade() else { return };
apply_layout_class(&window, width, &panels);
log_window_metrics(&window, window_metrics_level(&window, &reported_geometry));
});
}
{
let size = window.window().size();
let scale = window.window().scale_factor().max(0.01);
apply_layout_class(&window, size.width as f32 / scale, &panels);
// N6: the reading beside the one the layout class is derived from,
// which on a desktop is taken before the window has been mapped and so
// reads zero. `window_metrics_level` is what decides whether that is
// worth `info`; here it will not be, and the first resize carries the
// measurement instead.
log_window_metrics(&window, window_metrics_level(&window, &reported_geometry));
}
// FR-UI-2: the two collapsible columns, opened and closed by hand.
@@ -3606,6 +3618,90 @@ fn groups_in_rail(settings: &settings_ui::SettingsController) -> bool {
.groups_in_rail(dr_plat::is_touch_first())
}
/// TRACES: FR-UI-1
/// The window's own measurements, named plainly enough to be read out of
/// `adb logcat`.
///
/// `ui-navigation.md` N6. Every figure in D-N7's table was computed at a
/// *guessed* scale factor, because nothing in this repository has ever
/// recorded the density Android reports: the tablet's panel is 3000 x 1920
/// physical, and the two plausible densities put the dock's width at 900 or
/// 1037 and its available height 200px apart. The only way to settle it is to
/// ask the window on the device and read the answer off the wire.
///
/// Both coordinate systems, on one line, so they can be checked against each
/// other. Logical alone cannot be trusted when the scale is the thing in
/// doubt, and a physical size that does not divide into the logical one by the
/// scale reported beside it says the reading is of something other than the
/// panel — an emulator, a scaled-down desktop window, a display that changed
/// under the application.
/// TRACES: FR-UI-1
/// How loudly to say the window's size, given what has already been said.
///
/// Three constraints meet here and only one arrangement satisfies all of them.
///
/// *It has to reach the device.* `android_main` caps the `log` facade at
/// `info` (`darkroom-android`), so anything at `debug` is discarded before it
/// reaches liblog and `adb logcat` never sees it. N6's whole deliverable is a
/// number read off a tablet, so a reading worth having must be `info`.
///
/// *It must not be the shape of a drag.* Dragging a desktop window emits a
/// resize per frame, and every accepted record is also appended to the on-disk
/// log (NFR-OPS-1). Hundreds of `info` lines saying the window is a pixel
/// wider is not a diagnostic, so a resize that only moves the size is `debug`.
///
/// *A window has no size until it has been mapped, and the size it reports
/// first is not the one it settles on.* The startup call runs before the event
/// loop, where `size()` is 0 x 0 and `scale_factor()` is 1.0; the first resize
/// after it arrives at whatever the platform opened with and at scale 1.0,
/// because the display's real scale is applied a beat later. Watched on X11
/// the sequence is 0 x 0, then 360 x 320 at 1.0, then 1100 x 720 at 2.0 — and
/// a rule that reports only the first of those puts a number in logcat that
/// is not the window's.
///
/// So the pair that decides is **the scale factor and the orientation**, which
/// is exactly the pair N6 is asking for and exactly the pair a drag leaves
/// alone. A window with no area is not a reading at all and records nothing.
/// Every later change to either half — the display's scale resolving, the
/// window moving to a monitor with a different density, the tablet being
/// turned over — is a new answer to N6's question and goes out at `info`; the
/// rest is `debug`. Scale is compared as the line prints it, to two decimals,
/// so two records that would read identically cannot both claim `info`.
fn window_metrics_level(
window: &AppWindow,
reported: &std::cell::Cell<Option<(bool, u32)>>,
) -> log::Level {
let size = window.window().size();
if size.width == 0 || size.height == 0 {
return log::Level::Debug;
}
let geometry = (
size.height >= size.width,
(window.window().scale_factor().max(0.01) * 100.0).round() as u32,
);
if reported.replace(Some(geometry)) == Some(geometry) {
log::Level::Debug
} else {
log::Level::Info
}
}
fn log_window_metrics(window: &AppWindow, level: log::Level) {
let physical = window.window().size();
// The same floor `apply_layout_class`'s caller uses: a scale of zero is
// not a number this can divide by, and a window that reports one is
// reporting nothing useful anyway.
let scale = window.window().scale_factor().max(0.01);
log::log!(
level,
"window: {:.0}x{:.0} logical at scale {scale:.2} ({}x{} physical)",
physical.width as f32 / scale,
physical.height as f32 / scale,
physical.width,
physical.height
);
}
fn apply_layout_class(window: &AppWindow, width: f32, panels: &PanelChoices) {
let expanded = width >= EXPANDED_MIN_WIDTH;
window.set_expanded(expanded);