Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix virtual keyboard on (mobile) web #4855

Merged
merged 27 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4d9a0cf
eframe: fix not show keyboard on ios/android
micmonay Jul 13, 2024
b4cf232
fix virtual keyboard Android not move canvas
micmonay Jul 17, 2024
4f27a47
fix vkeyboard android/ios
micmonay Jul 20, 2024
e21eb58
clean fix vkeyboard
micmonay Jul 21, 2024
04c3344
fix vkeyboard fmt
micmonay Jul 21, 2024
b7711a6
fix vkeyboard comments
micmonay Jul 21, 2024
3607b11
fix composing keyboard
micmonay Jul 23, 2024
4f15368
fix vkeyboard IOS/Android
micmonay Jul 31, 2024
28822fb
fix vkeyboard flapping in IOS
micmonay Aug 1, 2024
1bc1aba
add is_mobile_safari function
micmonay Aug 6, 2024
c8d4226
remove old is_mobile function
micmonay Aug 21, 2024
2495324
eframe: fix not show keyboard on ios/android
micmonay Jul 13, 2024
e98afbd
fix virtual keyboard Android not move canvas
micmonay Jul 17, 2024
715384f
rebase fix vkeyboard android/ios
micmonay Jul 20, 2024
b04197b
clean fix vkeyboard
micmonay Jul 21, 2024
573a50f
rebase fix vkeyboard fmt
micmonay Jul 21, 2024
4b2ff9e
fix vkeyboard comments
micmonay Jul 21, 2024
4dc62e5
fix composing keyboard
micmonay Jul 23, 2024
2f7ab21
rebase fix vkeyboard IOS/Android
micmonay Jul 31, 2024
5b4b151
fix vkeyboard flapping in IOS
micmonay Aug 1, 2024
f420bd3
add is_mobile_safari function
micmonay Aug 6, 2024
f07472a
remove old is_mobile function
micmonay Aug 21, 2024
6a1e3e9
rebase fix-keyboard
micmonay Aug 26, 2024
5543ef3
remove is_mobile not use
micmonay Aug 26, 2024
502801c
fix rebase builder text_edit
micmonay Aug 26, 2024
ef6ff81
fix virtual keyboard on mobile
micmonay Aug 26, 2024
fdc6658
Try fix rebase
micmonay Aug 26, 2024
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
5 changes: 4 additions & 1 deletion crates/eframe/src/web/app_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,10 @@ impl AppRunner {
}
}

if let Err(err) = self.text_agent.move_to(ime, self.canvas()) {
if let Err(err) = self
.text_agent
.move_to(ime, self.canvas(), self.egui_ctx.zoom_factor())
{
log::error!(
"failed to update text agent position: {}",
super::string_from_js_value(&err)
Expand Down
7 changes: 7 additions & 0 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,13 @@ fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
runner.needs_repaint.repaint_asap();
event.stop_propagation();
event.prevent_default();

// Fix virtual keyboard IOS
// Need call focus at the same time of event
if runner.text_agent.has_focus() {
runner.text_agent.set_focus(false);
runner.text_agent.set_focus(true);
}
Comment on lines +579 to +582
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the intention to do that? If this is some workaround it should be documented explicitly, because it could introduce unintended behaviour in other places and if such behaviour would be found and traced down to this lines is should be obvious why they are here.

}
}
})
Expand Down
13 changes: 0 additions & 13 deletions crates/eframe/src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,3 @@ pub fn percent_decode(s: &str) -> String {
.decode_utf8_lossy()
.to_string()
}

/// Returns `true` if the app is likely running on a mobile device.
pub(crate) fn is_mobile() -> bool {
fn try_is_mobile() -> Option<bool> {
const MOBILE_DEVICE: [&str; 6] =
["Android", "iPhone", "iPad", "iPod", "webOS", "BlackBerry"];

let user_agent = web_sys::window()?.navigator().user_agent().ok()?;
let is_mobile = MOBILE_DEVICE.iter().any(|&name| user_agent.contains(name));
Some(is_mobile)
}
try_is_mobile().unwrap_or(false)
}
52 changes: 40 additions & 12 deletions crates/eframe/src/web/text_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::cell::Cell;

use wasm_bindgen::prelude::*;

use super::{is_mobile, AppRunner, WebRunner};
use super::{AppRunner, WebRunner};

pub struct TextAgent {
input: web_sys::HtmlInputElement,
Expand All @@ -22,12 +22,17 @@ impl TextAgent {
.create_element("input")?
.dyn_into::<web_sys::HtmlInputElement>()?;
input.set_type("text");
input.set_autofocus(true);
input.set_attribute("autocapitalize", "off")?;

// append it to `<body>` and hide it outside of the viewport
let style = input.style();
style.set_property("opacity", "0")?;
style.set_property("background-color", "transparent")?;
style.set_property("border", "none")?;
style.set_property("outline", "none")?;
style.set_property("width", "1px")?;
style.set_property("height", "1px")?;
style.set_property("caret-color", "transparent")?;
style.set_property("position", "absolute")?;
style.set_property("top", "0")?;
style.set_property("left", "0")?;
Expand All @@ -39,6 +44,12 @@ impl TextAgent {
let input = input.clone();
move |event: web_sys::InputEvent, runner: &mut AppRunner| {
let text = input.value();
// Fix android virtual keyboard Gboard
// This removes the virtual keyboard's suggestion.
if !event.is_composing() {
input.blur().ok();
input.focus().ok();
}
// if `is_composing` is true, then user is using IME, for example: emoji, pinyin, kanji, hangul, etc.
// In that case, the browser emits both `input` and `compositionupdate` events,
// and we need to ignore the `input` event.
Expand Down Expand Up @@ -103,14 +114,8 @@ impl TextAgent {
&self,
ime: Option<egui::output::IMEOutput>,
canvas: &web_sys::HtmlCanvasElement,
zoom_factor: f32,
) -> Result<(), JsValue> {
// Mobile keyboards don't follow the text input it's writing to,
// instead typically being fixed in place on the bottom of the screen,
// so don't bother moving the text agent on mobile.
if is_mobile() {
return Ok(());
}

// Don't move the text agent unless the position actually changed:
if self.prev_ime_output.get() == ime {
return Ok(());
Expand All @@ -119,14 +124,24 @@ impl TextAgent {

let Some(ime) = ime else { return Ok(()) };

let canvas_rect = super::canvas_content_rect(canvas);
let mut canvas_rect = super::canvas_content_rect(canvas);
// Fix for safari with virtual keyboard flapping position
if is_mobile_safari() {
canvas_rect.min.y = canvas.offset_top() as f32;
}
let cursor_rect = ime.cursor_rect.translate(canvas_rect.min.to_vec2());

let style = self.input.style();

// This is where the IME input will point to:
style.set_property("left", &format!("{}px", cursor_rect.center().x))?;
style.set_property("top", &format!("{}px", cursor_rect.center().y))?;
style.set_property(
"left",
&format!("{}px", cursor_rect.center().x * zoom_factor),
)?;
style.set_property(
"top",
&format!("{}px", cursor_rect.center().y * zoom_factor),
)?;

Ok(())
}
Expand Down Expand Up @@ -173,3 +188,16 @@ impl Drop for TextAgent {
self.input.remove();
}
}

/// Returns `true` if the app is likely running on a mobile device on navigator Safari.
fn is_mobile_safari() -> bool {
(|| {
let user_agent = web_sys::window()?.navigator().user_agent().ok()?;
let is_ios = user_agent.contains("iPhone")
|| user_agent.contains("iPad")
|| user_agent.contains("iPod");
let is_safari = user_agent.contains("Safari");
Some(is_ios && is_safari)
})()
.unwrap_or(false)
}
Loading