Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions apps/native/host/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,10 @@ pub fn view(s: &State, cx: &mut Cx<State>) -> El {
let [top, right, bottom, left] = cx.safe_area();
let keyboard = s.keyboard;
let inset = [top, right, if keyboard > 0.0 { 0.0 } else { bottom }, left];
// A paste with no field focused goes to the page, which hands it to the composer.
let mut page = col()
.w_full()
.h_full()
.on_layout(move |_: &mut State, rect| report_env(rect, inset))
.on_paste_content(|_: &mut State, content, _| send_paste(content));
.on_layout(move |_: &mut State, rect| report_env(rect, inset));
let bg = color(&s.page.bg);
if let Some(bg) = bg {
page = page.fill(bg)
Expand Down Expand Up @@ -476,10 +474,16 @@ pub fn view(s: &State, cx: &mut Cx<State>) -> El {
page = page.child(l);
}
}
if keyboard <= 0.0 {
return page;
}
let mut window = col().w_full().h_full().pb(keyboard);
// Always wrapped, the keyboard down or not: the tree keeps its shape, so the ids of the
// elements under it (the focused composer's among them) stay put as the keyboard moves.
// Wrapping only while the keyboard is up renamed every id, which dropped the composer's
// focus, which lowered the keyboard, which renamed them back: a loop.
// A paste with no field focused goes to the window's root, which hands it to the composer.
let mut window = col()
.w_full()
.h_full()
.pb(keyboard.max(0.0))
.on_paste_content(|_: &mut State, content, _| send_paste(content));
if let Some(bg) = bg {
window = window.fill(bg)
}
Expand Down
24 changes: 24 additions & 0 deletions apps/native/host/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3100,6 +3100,30 @@ fn the_page_ends_above_the_on_screen_keyboard() {
assert_eq!(rect(&rt, "r/1"), [0.0, 760.0, 400.0, 40.0]);
}

/// Focusing the composer raises the keyboard, and the keyboard must leave the composer focused:
/// a page whose element ids change under it would drop focus, which lowers the keyboard, which
/// changes them back, over and over.
#[test]
fn the_on_screen_keyboard_leaves_the_composer_focused() {
let mut rt = composer_page();
rt.update(|_, tx| tx.focus("r/0/field".to_string()));
rt.frame(0.1);
let focused = rt.focused_path().map(|p| p.to_string());
assert!(focused.is_some(), "the composer took focus");
for covered in [300.0, 0.0, 300.0] {
assert!(rt.state.set_keyboard(covered));
rt.invalidate();
rt.frame(0.1);
rt.settle_all();
rt.frame(0.1);
assert_eq!(
rt.focused_path().map(|p| p.to_string()),
focused,
"keyboard covering {covered}"
);
}
}

#[test]
fn a_lone_inline_span_in_a_block_wraps_its_text_to_the_block() {
// A message's markdown: `p > span > span > span > text` in the content column of a row.
Expand Down
Loading