Skip to content

Commit

Permalink
Ime support
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed Feb 23, 2024
1 parent eba2e3f commit e58b66f
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 9 deletions.
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/eframe/src/web/app_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ impl AppRunner {
mutable_text_under_cursor,
ime,
#[cfg(feature = "accesskit")]
accesskit_update: _, // not currently implemented
accesskit_update: _, // not currently implemented
text_input_state: _, // not currently implemented
} = platform_output;

super::set_cursor_icon(cursor_icon);
Expand Down
55 changes: 51 additions & 4 deletions crates/egui-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod window_settings;

pub use window_settings::WindowSettings;

use egui::{TextInputState, TextSpan};
use raw_window_handle::HasDisplayHandle;

#[allow(unused_imports)]
Expand Down Expand Up @@ -94,6 +95,7 @@ pub struct State {

/// track ime state
input_method_editor_started: bool,
text_input_last_frame: bool,

#[cfg(feature = "accesskit")]
accesskit: Option<accesskit_winit::Adapter>,
Expand Down Expand Up @@ -133,6 +135,7 @@ impl State {
simulate_touch_screen: false,
pointer_touch_id: None,

text_input_last_frame: false,
input_method_editor_started: false,

#[cfg(feature = "accesskit")]
Expand Down Expand Up @@ -362,6 +365,28 @@ impl State {
consumed: self.egui_ctx.wants_keyboard_input(),
}
}
WindowEvent::TextInputState(state) => {
self.egui_input
.events
.push(egui::Event::TextInputState(TextInputState {
text: state.text.clone(),
selection: TextSpan {
start: state.selection.start,
end: state.selection.end,
},
compose_region: state
.compose_region
.as_ref()
.map(|r| TextSpan {
start: r.start,
end: r.end,
}),
}));
EventResponse {
repaint: true,
consumed: self.egui_ctx.wants_keyboard_input(),
}
}
WindowEvent::KeyboardInput { event, .. } => {
self.on_keyboard_input(event);

Expand Down Expand Up @@ -793,6 +818,7 @@ impl State {
ime,
#[cfg(feature = "accesskit")]
accesskit_update,
text_input_state,
} = platform_output;

self.set_cursor_icon(window, cursor_icon);
Expand All @@ -805,11 +831,31 @@ impl State {
self.clipboard.set(copied_text);
}

let allow_ime = ime.is_some();
if self.allow_ime != allow_ime {
self.allow_ime = allow_ime;
window.set_ime_allowed(allow_ime);
if let Some(text_input_state) = text_input_state {
window.set_text_input_state(winit::event::TextInputState {
text: text_input_state.text,
selection: winit::event::TextSpan {
start: text_input_state.selection.start,
end: text_input_state.selection.end,
},
compose_region: text_input_state
.compose_region
.map(|r| winit::event::TextSpan {
start: r.start,
end: r.end,
}),
});
}

let text_input_this_frame = ime.is_some();
if self.text_input_last_frame != text_input_this_frame {
if text_input_this_frame {
window.begin_ime_input();
} else {
window.end_ime_input();
}
}
self.text_input_last_frame = text_input_this_frame;

if let Some(ime) = ime {
let rect = ime.rect;
Expand Down Expand Up @@ -1745,6 +1791,7 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'st
WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput",
WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged",
WindowEvent::Ime { .. } => "WindowEvent::Ime",
WindowEvent::TextInputState(..) => "WindowEvent::TextInputState",
WindowEvent::CursorMoved { .. } => "WindowEvent::CursorMoved",
WindowEvent::CursorEntered { .. } => "WindowEvent::CursorEntered",
WindowEvent::CursorLeft { .. } => "WindowEvent::CursorLeft",
Expand Down
26 changes: 26 additions & 0 deletions crates/egui/src/data/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ pub enum Event {
/// IME composition ended with this final result.
CompositionEnd(String),

TextInputState(TextInputState),

/// On touch screens, report this *in addition to*
/// [`Self::PointerMoved`], [`Self::PointerButton`], [`Self::PointerGone`]
Touch {
Expand Down Expand Up @@ -1089,6 +1091,30 @@ impl From<u32> for TouchId {
}
}

/// This struct holds a span within a region of text from `start` (inclusive) to
/// `end` (exclusive).
///
/// An empty span or cursor position is specified with `start == end`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TextSpan {
/// The start of the span (inclusive)
pub start: usize,

/// The end of the span (exclusive)
pub end: usize,
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TextInputState {
pub text: String,
/// A selection defined on the text.
pub selection: TextSpan,
/// A composing region defined on the text.
pub compose_region: Option<TextSpan>,
}

// ----------------------------------------------------------------------------

// TODO(emilk): generalize this to a proper event filter.
Expand Down
9 changes: 8 additions & 1 deletion crates/egui/src/data/output.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! All the data egui returns to the backend at the end of each frame.
use crate::{ViewportIdMap, ViewportOutput, WidgetType};
use crate::{TextInputState, ViewportIdMap, ViewportOutput, WidgetType};

/// What egui emits each frame from [`crate::Context::run`].
///
Expand Down Expand Up @@ -118,6 +118,8 @@ pub struct PlatformOutput {
/// Useful for IME.
pub ime: Option<IMEOutput>,

pub text_input_state: Option<TextInputState>,

/// The difference in the widget tree since last frame.
///
/// NOTE: this needs to be per-viewport.
Expand Down Expand Up @@ -155,6 +157,7 @@ impl PlatformOutput {
ime,
#[cfg(feature = "accesskit")]
accesskit_update,
text_input_state,
} = newer;

self.cursor_icon = cursor_icon;
Expand All @@ -168,6 +171,10 @@ impl PlatformOutput {
self.mutable_text_under_cursor = mutable_text_under_cursor;
self.ime = ime.or(self.ime);

if text_input_state.is_some() {
self.text_input_state = text_input_state;
}

#[cfg(feature = "accesskit")]
{
// egui produces a complete AccessKit tree for each frame,
Expand Down
11 changes: 11 additions & 0 deletions crates/egui/src/input_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub struct InputState {

/// Position and size of the egui area.
pub screen_rect: Rect,
previous_screen_rect: Rect,

/// Also known as device pixel ratio, > 1 for high resolution screens.
pub pixels_per_point: f32,
Expand Down Expand Up @@ -150,6 +151,7 @@ impl Default for InputState {
smooth_scroll_delta: Vec2::ZERO,
zoom_factor_delta: 1.0,
screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
previous_screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
pixels_per_point: 1.0,
max_texture_side: 2048,
time: 0.0,
Expand Down Expand Up @@ -185,6 +187,7 @@ impl InputState {
new.predicted_dt
};

let previous_screen_rect = self.screen_rect;
let screen_rect = new.screen_rect.unwrap_or(self.screen_rect);
self.create_touch_states_for_new_devices(&new.events);
for touch_state in self.touch_states.values_mut() {
Expand Down Expand Up @@ -258,6 +261,7 @@ impl InputState {
smooth_scroll_delta,
zoom_factor_delta,
screen_rect,
previous_screen_rect,
pixels_per_point,
max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
time,
Expand All @@ -283,6 +287,11 @@ impl InputState {
self.screen_rect
}

#[inline(always)]
pub fn screen_rect_changed(&self) -> bool {
self.screen_rect != self.previous_screen_rect
}

/// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
/// * `zoom = 1`: no change
/// * `zoom < 1`: pinch together
Expand Down Expand Up @@ -1065,6 +1074,7 @@ impl InputState {

zoom_factor_delta,
screen_rect,
previous_screen_rect,
pixels_per_point,
max_texture_side,
time,
Expand Down Expand Up @@ -1108,6 +1118,7 @@ impl InputState {
));
ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
ui.label(format!("screen_rect: {screen_rect:?} points"));
ui.label(format!("previous_screen_rect: {:?} points", previous_screen_rect));
ui.label(format!(
"{pixels_per_point} physical pixels for each logical point"
));
Expand Down
Loading

0 comments on commit e58b66f

Please sign in to comment.