-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
IME enabled on web #247
Closed
Closed
IME enabled on web #247
Changes from 18 commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
759e17b
Text wrap correctly on Chinese chrarcter
n2 efd8027
Text wrap correctly on Chinese chraracter, checked by a stand-alone f…
n2 40ffef8
Merge remote-tracking branch 'upstream/master' into master
n2 d5bb79d
Merge remote-tracking branch 'upstream/master' into master
n2 d4e5fa5
Merge remote-tracking branch 'upstream/master' into master
n2 e8d9273
epaint: default_fonts replaced by custom_fonts
n2 5e93450
Merge remote-tracking branch 'upstream/master' into master
n2 9a88ecf
Fix " env" error on wasm-bindgen 0.2.72
n2 be93274
Create TexeElement, send text if needed.
n2 34c6a5f
Toggle keyboard if necessary
n2 2a33c49
Make agent following cursor.
n2 8bcf144
Remove text_cursor from Output & canvas panning
n2 db3be33
Handle Text event when agent is hidden & code cleaning.
n2 b82b1bd
Some minor changes.
n2 ce05842
Merge remote-tracking branch 'upstream/master' into master
n2 86d331d
Revert "epaint: default_fonts replaced by custom_fonts"
n2 41a3bb6
Fix the panning of the canvas on mobile
emilk db04d03
Some changes based on feedback.
n2 9dd0057
Merge remote-tracking branch 'upstream/master'
n2 ba41b22
Minor changes
n2 1bed5ca
bug fix: false id clash error for wrapping text
emilk 9540c3e
refactor: simplify CollapsingHeader enable/disable code
emilk 27eba1b
Add helper functions to Rect
emilk 5d3501f
emath: add any_nan to Vec2, Pos2 and Rect
emilk c912df2
Refactor layout (#241)
emilk 2347d64
Fix bug that would allocate the full width of non-centered layouts
emilk 4237811
Fix: centered horizontal layouts should never overflow upwards
emilk 9c2438e
Add Visuals::debug_widgets to debug layouting by hovering widgets
emilk deb5921
Bug-fixes related to recent layout rewrite
emilk e815d21
Add ui.set_row_height
emilk 5c908ff
Depcrecate ui.horizontal_for_text and ui.horizontal_wrapped_for_text
emilk 8aa497f
[EasyMark] Add support for small and raised text
emilk 0f1f317
Middle-click links to open in new tab
emilk ce4a68d
Add demo app instructions for Defora Rawhide
emilk 93e2385
Document the need for latest rustc
emilk c5c4a09
Add link to https://github.com/hakolao/egui_winit_vulkano
emilk 6b43c99
ColorPicker: always show hue slider at full saturation and lightness
emilk 9ede193
Improve misc documentation
emilk e9cc09b
Add instruction to not add .js/.wasm files in pull requests
emilk 01d858a
Add years and email in LICENSE-MIT
emilk c43ea67
Replace emath::clamp with f32::clamp (new in rustc 1.50)
emilk ffb66f8
layout: don't return negative availability rectangles
emilk 64ea7d2
Return InnerResponse from Frame, Grid and ui.group()
emilk bee737d
Revert "epaint: default_fonts replaced by custom_fonts"
n2 d006d10
Fix the panning of the canvas on mobile
emilk fc7221b
Some changes based on feedback.
n2 71e45e0
Add some clippy lints
emilk 897ddad
Render tab character (\t) as four spaces
emilk cc96ad9
Add checkbox in demo app to turn screen reader on/off
emilk 3a0f1d2
Upgrade wasm-bindgen 0.2.71 -> 0.2.72
emilk 7797c80
ci: install libspeechd-dev and fix new clippy lints (#252)
emilk fe44235
Minor changes
n2 f32d369
Merge branch 'master' of https://github.com/n2/egui into master
n2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,9 +23,13 @@ pub use wasm_bindgen; | |
pub use web_sys; | ||
|
||
pub use painter::Painter; | ||
use std::cell::Cell; | ||
use std::rc::Rc; | ||
use std::sync::Arc; | ||
use wasm_bindgen::prelude::*; | ||
|
||
static AGENT_ID: &str = "text_agent"; | ||
|
||
// ---------------------------------------------------------------------------- | ||
// Helpers to hide some of the verbosity of web_sys | ||
|
||
|
@@ -107,11 +111,13 @@ pub fn button_from_mouse_event(event: &web_sys::MouseEvent) -> Option<egui::Poin | |
} | ||
} | ||
|
||
pub fn pos_from_touch_event(event: &web_sys::TouchEvent) -> egui::Pos2 { | ||
pub fn pos_from_touch_event(canvas_id: &str, event: &web_sys::TouchEvent) -> egui::Pos2 { | ||
let canvas = canvas_element(canvas_id).unwrap(); | ||
let rect = canvas.get_bounding_client_rect(); | ||
let t = event.touches().get(0).unwrap(); | ||
egui::Pos2 { | ||
x: t.page_x() as f32, | ||
y: t.page_y() as f32, | ||
x: t.page_x() as f32 - rect.left() as f32, | ||
y: t.page_y() as f32 - rect.top() as f32, | ||
} | ||
} | ||
|
||
|
@@ -228,7 +234,7 @@ impl epi::Storage for LocalStorage { | |
|
||
// ---------------------------------------------------------------------------- | ||
|
||
pub fn handle_output(output: &egui::Output) { | ||
pub fn handle_output(output: &egui::Output, _canvas_id: &str) { | ||
let egui::Output { | ||
cursor_icon, | ||
open_url, | ||
|
@@ -458,6 +464,19 @@ fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> { | |
request_animation_frame(runner_ref) | ||
} | ||
|
||
fn text_agent_hiddeh() -> bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit typo: |
||
use wasm_bindgen::JsCast; | ||
web_sys::window() | ||
.unwrap() | ||
.document() | ||
.unwrap() | ||
.get_element_by_id(AGENT_ID) | ||
.unwrap() | ||
.dyn_into::<web_sys::HtmlInputElement>() | ||
.unwrap() | ||
.hidden() | ||
} | ||
|
||
fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | ||
use wasm_bindgen::JsCast; | ||
let window = web_sys::window().unwrap(); | ||
|
@@ -485,7 +504,12 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
modifiers, | ||
}); | ||
} | ||
if !modifiers.ctrl && !modifiers.command && !should_ignore_key(&key) { | ||
if !modifiers.ctrl | ||
&& !modifiers.command | ||
&& !should_ignore_key(&key) | ||
// When text agent is shown, it sends text event instead. | ||
&& text_agent_hiddeh() | ||
{ | ||
runner_lock.input.raw.events.push(egui::Event::Text(key)); | ||
} | ||
runner_lock.needs_repaint.set_true(); | ||
|
@@ -633,6 +657,97 @@ fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers { | |
} | ||
} | ||
|
||
/// | ||
/// Text event handler, | ||
fn install_text_agent(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | ||
use wasm_bindgen::JsCast; | ||
let window = web_sys::window().unwrap(); | ||
let document = window.document().unwrap(); | ||
let body = document.body().expect("document should have a body"); | ||
let input = document | ||
.create_element("input")? | ||
.dyn_into::<web_sys::HtmlInputElement>()?; | ||
let input = std::rc::Rc::new(input); | ||
input.set_id(AGENT_ID); | ||
let is_composing = Rc::new(Cell::new(false)); | ||
{ | ||
let style = input.style(); | ||
// Transparent | ||
style.set_property("opacity", "0").unwrap(); | ||
// Hide under canvas | ||
style.set_property("z-index", "-1").unwrap(); | ||
} | ||
// Set size as small as possible, in case user may click on it. | ||
input.set_size(1); | ||
input.set_autofocus(true); | ||
input.set_hidden(true); | ||
{ | ||
// When IME is off | ||
let input_clone = input.clone(); | ||
let runner_ref = runner_ref.clone(); | ||
let is_composing = is_composing.clone(); | ||
let on_input = Closure::wrap(Box::new(move |_event: web_sys::InputEvent| { | ||
let text = input_clone.value(); | ||
if !text.is_empty() && !is_composing.get() { | ||
input_clone.set_value(""); | ||
let mut runner_lock = runner_ref.0.lock(); | ||
runner_lock.input.raw.events.push(egui::Event::Text(text)); | ||
runner_lock.needs_repaint.set_true(); | ||
} | ||
}) as Box<dyn FnMut(_)>); | ||
input.add_event_listener_with_callback("input", on_input.as_ref().unchecked_ref())?; | ||
on_input.forget(); | ||
} | ||
{ | ||
// When IME is on, handle composition event | ||
let input_clone = input.clone(); | ||
let runner_ref = runner_ref.clone(); | ||
let on_compositionend = Closure::wrap(Box::new(move |event: web_sys::CompositionEvent| { | ||
// let event_type = event.type_(); | ||
match event.type_().as_ref() { | ||
"compositionstart" => { | ||
is_composing.set(true); | ||
input_clone.set_value(""); | ||
} | ||
"compositionend" => { | ||
is_composing.set(false); | ||
input_clone.set_value(""); | ||
if let Some(text) = event.data() { | ||
let mut runner_lock = runner_ref.0.lock(); | ||
runner_lock.input.raw.events.push(egui::Event::Text(text)); | ||
runner_lock.needs_repaint.set_true(); | ||
} | ||
} | ||
"compositionupdate" => {} | ||
_s => panic!("Unknown type"), | ||
} | ||
}) as Box<dyn FnMut(_)>); | ||
let f = on_compositionend.as_ref().unchecked_ref(); | ||
input.add_event_listener_with_callback("compositionstart", f)?; | ||
input.add_event_listener_with_callback("compositionupdate", f)?; | ||
input.add_event_listener_with_callback("compositionend", f)?; | ||
on_compositionend.forget(); | ||
} | ||
{ | ||
// When input lost focus, focus on it again. | ||
// It is useful when user click somewhere outside canvas. | ||
let on_focusout = Closure::wrap(Box::new(move |_event: web_sys::MouseEvent| { | ||
// Delay 10 ms, and focus again. | ||
let func = js_sys::Function::new_no_args(&format!( | ||
"document.getElementById('{}').focus()", | ||
AGENT_ID | ||
)); | ||
window | ||
.set_timeout_with_callback_and_timeout_and_arguments_0(&func, 10) | ||
.unwrap(); | ||
}) as Box<dyn FnMut(_)>); | ||
input.add_event_listener_with_callback("focusout", on_focusout.as_ref().unchecked_ref())?; | ||
on_focusout.forget(); | ||
} | ||
body.append_child(&input)?; | ||
Ok(()) | ||
} | ||
|
||
fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | ||
use wasm_bindgen::JsCast; | ||
let canvas = canvas_element(runner_ref.0.lock().canvas_id()).unwrap(); | ||
|
@@ -721,6 +836,7 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
event.stop_propagation(); | ||
event.prevent_default(); | ||
} | ||
manipulate_agent(runner_lock.canvas_id(), runner_lock.input.latest_touch_pos); | ||
} | ||
}) as Box<dyn FnMut(_)>); | ||
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?; | ||
|
@@ -747,8 +863,8 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
let event_name = "touchstart"; | ||
let runner_ref = runner_ref.clone(); | ||
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| { | ||
let pos = pos_from_touch_event(&event); | ||
let mut runner_lock = runner_ref.0.lock(); | ||
let pos = pos_from_touch_event(runner_lock.canvas_id(), &event); | ||
runner_lock.input.latest_touch_pos = Some(pos); | ||
runner_lock.input.is_touch = true; | ||
let modifiers = runner_lock.input.raw.modifiers; | ||
|
@@ -774,8 +890,8 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
let event_name = "touchmove"; | ||
let runner_ref = runner_ref.clone(); | ||
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| { | ||
let pos = pos_from_touch_event(&event); | ||
let mut runner_lock = runner_ref.0.lock(); | ||
let pos = pos_from_touch_event(runner_lock.canvas_id(), &event); | ||
runner_lock.input.latest_touch_pos = Some(pos); | ||
runner_lock.input.is_touch = true; | ||
runner_lock | ||
|
@@ -815,6 +931,9 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
runner_lock.needs_repaint.set_true(); | ||
event.stop_propagation(); | ||
event.prevent_default(); | ||
|
||
// Finally, focus or blur on agent to toggle keyboard | ||
manipulate_agent(runner_lock.canvas_id(), runner_lock.input.latest_touch_pos); | ||
} | ||
}) as Box<dyn FnMut(_)>); | ||
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?; | ||
|
@@ -838,3 +957,41 @@ fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { | |
|
||
Ok(()) | ||
} | ||
|
||
fn manipulate_agent(canvas_id: &str, latest_cursor: Option<egui::Pos2>) -> Option<()> { | ||
use wasm_bindgen::JsCast; | ||
use web_sys::HtmlInputElement; | ||
let window = web_sys::window()?; | ||
let document = window.document()?; | ||
let input: HtmlInputElement = document.get_element_by_id(AGENT_ID)?.dyn_into().unwrap(); | ||
let cutsor_txt = document.body()?.style().get_property_value("cursor").ok()?; | ||
let style = canvas_element(canvas_id)?.style(); | ||
if cutsor_txt == cursor_web_name(egui::CursorIcon::Text) { | ||
input.set_hidden(false); | ||
input.focus().ok()?; | ||
// Panning canvas so that text edit is shown at 30% | ||
// Only on touch screens, when keyboard popups | ||
if let Some(p) = latest_cursor { | ||
let inner_height = window.inner_height().ok()?.as_f64()? as f32; | ||
let current_rel = p.y / inner_height; | ||
|
||
if current_rel > 0.5 { | ||
// probably below the keyboard | ||
|
||
let target_rel = 0.3; | ||
|
||
let delta = target_rel - current_rel; | ||
let new_pos_percent = (delta * 100.0).round().to_string() + "%"; | ||
|
||
style.set_property("position", "absolute").ok()?; | ||
style.set_property("top", &new_pos_percent).ok()?; | ||
} | ||
} | ||
} else { | ||
input.blur().ok()?; | ||
input.set_hidden(true); | ||
style.set_property("position", "absolute").ok()?; | ||
style.set_property("top", "0%").ok()?; // move back to normal position | ||
} | ||
Some(()) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove unused argument