iris/android: the composer's launch position was the bench report pane, plus surface/insets lifecycle logging

The empty benchmark-report TextEdit held .height(rest(1)) beside
content.height(rest(2)), so it reserved a third of the window at every
launch and pushed the composer two thirds down -- Iris's 11:39 phone
report. It is sized to its content now, capped and scrollable, and sits
above the transcript rather than under the composer.

New log::info! lines for one insets change, one surface_changed, one
renderer build and one surface_destroyed, each with the glyph/atlas
counts, so a phone's adb logcat can answer the app-switch text loss the
emulator cannot reproduce.
This commit is contained in:
iris committed 2026-09-06 13:26:34 -04:00
1 parent 2fed8b34b3
commit d9872989fa
3 files changed
+82 -3

No files matched your search

+27 -2
View File
@@ -92,6 +92,12 @@ const ANIM_STEP_MS: u64 = 16;
const FIXTURE_JSONL: &str = include_str!("../../../app/bench-fixture/assets/transcript.jsonl");
/// How much of the screen a *filled* benchmark report may take before it
/// scrolls instead of growing -- roughly a third of a phone screen, the
/// share the pane used to reserve unconditionally. An empty report takes
/// nothing at all; see `new`'s comment at the tree it is used in.
const REPORT_MAX_HEIGHT_DP: f32 = 260.0;
pub struct BenchClient {
ui_state: AndroidUiState,
content: WeakWidget<WidgetPtr>,
@@ -255,10 +261,29 @@ impl AndroidAppState for BenchClient {
let top_bar = WidgetPtr::new().add(rsc);
let controls = bench_controls(rsc, 0.0);
top_bar(rsc).set(controls);
// The report pane is sized to whatever report it is holding, not
// to a share of the window: `rest(1)` here reserved a third of
// the screen for an *empty* `TextEdit` at every launch, which is
// what Iris's 2026-09-06 11:39 phone report described as "the app
// does not start with keyboard spacing correct" -- the composer
// two thirds down with black below it, nothing to do with the IME
// inset (measured: `iris insets:` reports bottom=63 ime_bottom=0
// at launch, while the `Message` field's own box sat 789px above
// the bottom of a 2282px surface -- exactly this pane's third).
// Capped and scrollable so a long report cannot take the screen
// back over, the same idiom `composer.rs` uses for the field.
// Above the transcript, not below it: the report is what the
// header's own "Run benchmark" button produces (UI_RULES.md --
// results appear where the action was started), and a pane under
// the composer would eat the navigation-bar clearance
// `set_bottom_inset` gives it.
let tree = (
top_bar,
content.height(rest(2)),
report_display.height(rest(1)).pad(dp(8)),
report_display
.pad(dp(8))
.max_height(dp(REPORT_MAX_HEIGHT_DP))
.scrollable(),
content.height(rest(1)),
)
.span(Dir::DOWN)
.add_strong(rsc)
+34
View File
@@ -368,6 +368,21 @@ impl<State: AndroidAppState> IrisViewPeer<State> {
let current_insets = ui_state.insets();
if current_insets != ui_state.last_insets {
let physical = WindowInsets::from_physical(current_insets);
// One line per real insets change. Iris's phone is the only
// place several of these bugs reproduce and `adb logcat` is
// the only instrument there (this-machine-android: system
// tracing is broken on that device), so the numbers a layout
// is actually fed have to reach the log -- "the composer
// floats at launch" is unanswerable from a screenshot alone.
log::info!(
"iris insets: left={} top={} right={} bottom={} ime_bottom={} window={:?}",
physical.left,
physical.top,
physical.right,
physical.bottom,
physical.ime_bottom,
self.window_size(),
);
self.state.android_state_mut().last_insets = current_insets;
self.state.on_insets_changed(&mut self.rsc, physical);
}
@@ -627,6 +642,12 @@ impl<State: AndroidAppState> ViewPeer for IrisViewPeer<State> {
// backgrounding) still goes through `AndroidRenderer::new` below,
// since `renderer` is `None` in that case.
let already_live = self.state.android_state().renderer.is_some();
log::info!(
"iris surface: surface_changed {width}x{height} already_live={already_live} \
glyphs_cached={} atlas_pages={}",
self.rsc.ui.text.atlas.glyph_count(),
self.rsc.ui.text.atlas.page_count(),
);
if already_live {
let ui_state = self.state.android_state_mut();
ui_state
@@ -671,6 +692,13 @@ impl<State: AndroidAppState> ViewPeer for IrisViewPeer<State> {
// builds a new renderer, exactly where invalidation is
// needed, never on the reuse branch, where it would throw
// away perfectly valid GPU state for nothing.
log::info!(
"iris surface: new renderer built ({:?}), clearing glyph atlas: \
glyphs={} pages={}",
renderer.adapter_backend,
self.rsc.ui.text.atlas.glyph_count(),
self.rsc.ui.text.atlas.page_count(),
);
self.rsc.ui.text.atlas.clear();
self.rsc.ui.textures.reset();
self.state.android_state_mut().renderer = Some(renderer);
@@ -707,6 +735,12 @@ impl<State: AndroidAppState> ViewPeer for IrisViewPeer<State> {
_ctx: &mut CallbackCtx<'local>,
_holder: &android_view::SurfaceHolder<'local>,
) {
log::info!(
"iris surface: surface_destroyed, tearing the renderer down \
(glyphs_cached={} atlas_pages={})",
self.rsc.ui.text.atlas.glyph_count(),
self.rsc.ui.text.atlas.page_count(),
);
self.state.android_state_mut().renderer = None;
}