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

Improve IME support with new Event::Ime #4358

Merged
merged 46 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f0b7737
Update memory.rs
rustbasic Mar 23, 2024
3b98d7f
Update epi_integration.rs
rustbasic Mar 23, 2024
af5a7bb
Update memory.rs
rustbasic Mar 23, 2024
7f66b56
Update memory.rs
rustbasic Mar 23, 2024
6396c4a
Update epi_integration.rs
rustbasic Mar 23, 2024
9fe42ff
Merge branch 'emilk:master' into master
rustbasic Mar 25, 2024
d7673fe
Merge branch 'emilk:master' into master
rustbasic Mar 25, 2024
a7edc53
Merge branch 'emilk:master' into master
rustbasic Mar 26, 2024
2432784
Merge branch 'emilk:master' into master
rustbasic Mar 27, 2024
9b6209b
Merge branch 'emilk:master' into master
rustbasic Mar 27, 2024
f3687f6
Merge branch 'emilk:master' into master
rustbasic Mar 28, 2024
78880de
Merge branch 'emilk:master' into master
rustbasic Mar 29, 2024
01ba2ec
Merge branch 'emilk:master' into master
rustbasic Mar 29, 2024
0ae2451
Merge branch 'emilk:master' into master
rustbasic Mar 29, 2024
e6c84ce
Merge branch 'emilk:master' into master
rustbasic Mar 30, 2024
821dff0
Update epi_integration.rs
rustbasic Mar 30, 2024
eecfafd
Merge branch 'emilk:master' into master
rustbasic Mar 30, 2024
cbb5ac7
Merge branch 'emilk:master' into master
rustbasic Mar 30, 2024
07b5143
Merge branch 'emilk:master' into master
rustbasic Mar 31, 2024
3313633
Update epi_integration.rs
rustbasic Mar 31, 2024
90b968c
Merge branch 'emilk:master' into master
rustbasic Apr 1, 2024
13af44f
Merge branch 'emilk:master' into master
rustbasic Apr 1, 2024
5d95f09
Merge branch 'emilk:master' into master
rustbasic Apr 1, 2024
4be440a
Merge branch 'emilk:master' into master
rustbasic Apr 1, 2024
1ef0e10
Merge branch 'emilk:master' into master
rustbasic Apr 2, 2024
6541324
Merge branch 'emilk:master' into master
rustbasic Apr 2, 2024
8abc161
Merge branch 'emilk:master' into master
rustbasic Apr 3, 2024
1b21742
Merge branch 'emilk:master' into master
rustbasic Apr 4, 2024
052bb68
Merge branch 'emilk:master' into master
rustbasic Apr 5, 2024
e71d4a2
Update lib.rs
rustbasic Apr 13, 2024
9009c91
Update input.rs
rustbasic Apr 13, 2024
03abdee
Update builder.rs
rustbasic Apr 13, 2024
96982e7
Update lib.rs
rustbasic Apr 13, 2024
c74c264
Update builder.rs
rustbasic Apr 14, 2024
e271cac
Update builder.rs
rustbasic Apr 14, 2024
b57bcbc
Update builder.rs
rustbasic Apr 14, 2024
81147d8
Update lib.rs
rustbasic Apr 14, 2024
9bab18a
Update lib.rs
rustbasic Apr 19, 2024
b34686f
Merge branch 'emilk:master' into patch41
rustbasic Apr 19, 2024
a2f8e63
Merge branch 'emilk:master' into patch41
rustbasic Apr 21, 2024
17a552e
Add `ImeEvent` to mirror the one in winit
emilk Apr 21, 2024
8a4d9b1
Merge branch 'emilk:master' into patch41
rustbasic Apr 22, 2024
580dfac
Update text_agent.rs
rustbasic Apr 22, 2024
4b450ad
Fix web build
emilk Apr 22, 2024
5ecccbb
`has_sent_ime_enabled`
emilk Apr 22, 2024
dda72ab
Code cleanup
emilk Apr 22, 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
45 changes: 31 additions & 14 deletions crates/egui-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub struct State {
pointer_touch_id: Option<u64>,

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

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

input_method_editor_started: false,
has_sent_ime_enable: false,

#[cfg(feature = "accesskit")]
accesskit: None,
Expand Down Expand Up @@ -342,23 +342,39 @@ impl State {
// We use input_method_editor_started to manually insert CompositionStart
// between Commits.
match ime {
winit::event::Ime::Enabled | winit::event::Ime::Disabled => (),
winit::event::Ime::Commit(text) => {
self.input_method_editor_started = false;
winit::event::Ime::Enabled => {
self.egui_input
.events
.push(egui::Event::CompositionEnd(text.clone()));
.push(egui::Event::Ime(egui::IMEEvent::Enabled));
self.has_sent_ime_enable = true;
}
winit::event::Ime::Preedit(text, Some(_)) => {
if !self.input_method_editor_started {
self.input_method_editor_started = true;
self.egui_input.events.push(egui::Event::CompositionStart);
winit::event::Ime::Preedit(_, None) => {}
winit::event::Ime::Preedit(text, Some(_cursor)) => {
if !self.has_sent_ime_enable {
self.egui_input
.events
.push(egui::Event::Ime(egui::IMEEvent::Enabled));
self.has_sent_ime_enable = true;
}
self.egui_input
.events
.push(egui::Event::CompositionUpdate(text.clone()));
.push(egui::Event::Ime(egui::IMEEvent::Preedit(text.clone())));
}
winit::event::Ime::Commit(text) => {
self.egui_input
.events
.push(egui::Event::Ime(egui::IMEEvent::Commit(text.clone())));
self.egui_input
.events
.push(egui::Event::Ime(egui::IMEEvent::Disabled));
self.has_sent_ime_enable = false;
}
winit::event::Ime::Disabled => {
self.egui_input
.events
.push(egui::Event::Ime(egui::IMEEvent::Disabled));
self.has_sent_ime_enable = false;
}
winit::event::Ime::Preedit(_, None) => {}
};

EventResponse {
Expand Down Expand Up @@ -601,7 +617,8 @@ impl State {
});
// If we're not yet translating a touch or we're translating this very
// touch …
if self.pointer_touch_id.is_none() || self.pointer_touch_id.unwrap() == touch.id {
if self.pointer_touch_id.is_none() || self.pointer_touch_id.unwrap_or_default() == touch.id
{
// … emit PointerButton resp. PointerMoved events to emulate mouse
match touch.phase {
winit::event::TouchPhase::Started => {
Expand Down Expand Up @@ -1531,7 +1548,7 @@ pub fn create_winit_window_builder<T>(
// We set sizes and positions in egui:s own ui points, which depends on the egui
// zoom_factor and the native pixels per point, so we need to know that here.
// We don't know what monitor the window will appear on though, but
// we'll try to fix that after the window is created in the vall to `apply_viewport_builder_to_window`.
// we'll try to fix that after the window is created in the call to `apply_viewport_builder_to_window`.
let native_pixels_per_point = event_loop
.primary_monitor()
.or_else(|| event_loop.available_monitors().next())
Expand Down
29 changes: 21 additions & 8 deletions crates/egui/src/data/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,8 @@ pub enum Event {
/// * `zoom > 1`: pinch spread
Zoom(f32),

/// IME composition start.
CompositionStart,

/// A new IME candidate is being suggested.
CompositionUpdate(String),

/// IME composition ended with this final result.
CompositionEnd(String),
/// IME Event
Ime(IMEEvent),

/// On touch screens, report this *in addition to*
/// [`Self::PointerMoved`], [`Self::PointerButton`], [`Self::PointerGone`]
Expand Down Expand Up @@ -507,6 +501,25 @@ pub enum Event {
},
}

/// IME event.
///
/// See <https://docs.rs/winit/latest/winit/event/enum.Ime.html>
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum IMEEvent {
/// Notifies when the IME was enabled.
Enabled,

/// A new IME candidate is being suggested.
Preedit(String),

/// IME composition ended with this final result.
Commit(String),

/// Notifies when the IME was disabled.
Disabled,
}

/// Mouse button (or similar for touch input)
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down
71 changes: 37 additions & 34 deletions crates/egui/src/widgets/text_edit/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,47 +950,50 @@ fn events(
..
} => check_for_mutating_key_press(os, &mut cursor_range, text, galley, modifiers, *key),

Event::CompositionStart => {
state.has_ime = true;
None
}

Event::CompositionUpdate(text_mark) => {
// empty prediction can be produced when user press backspace
// or escape during ime. We should clear current text.
if text_mark != "\n" && text_mark != "\r" && state.has_ime {
let mut ccursor = text.delete_selected(&cursor_range);
let start_cursor = ccursor;
if !text_mark.is_empty() {
text.insert_text_at(&mut ccursor, text_mark, char_limit);
}
Event::Ime(ime_event) => match ime_event {
IMEEvent::Enabled => {
state.ime_enabled = true;
state.ime_cursor_range = cursor_range;
Some(CCursorRange::two(start_cursor, ccursor))
} else {
None
}
}

Event::CompositionEnd(prediction) => {
// CompositionEnd only characters may be typed into TextEdit without trigger CompositionStart first,
// so do not check `state.has_ime = true` in the following statement.
if prediction != "\n" && prediction != "\r" {
state.has_ime = false;
let mut ccursor;
if !prediction.is_empty()
&& cursor_range.secondary.ccursor.index
== state.ime_cursor_range.secondary.ccursor.index
{
ccursor = text.delete_selected(&cursor_range);
text.insert_text_at(&mut ccursor, prediction, char_limit);
IMEEvent::Preedit(text_mark) => {
if text_mark == "\n" || text_mark == "\r" {
None
} else {
// Empty prediction can be produced when user press backspace
// or escape during IME, so we clear current text.
let mut ccursor = text.delete_selected(&cursor_range);
let start_cursor = ccursor;
if !text_mark.is_empty() {
text.insert_text_at(&mut ccursor, text_mark, char_limit);
}
state.ime_cursor_range = cursor_range;
Some(CCursorRange::two(start_cursor, ccursor))
}
}
IMEEvent::Commit(prediction) => {
if prediction == "\n" || prediction == "\r" {
None
} else {
ccursor = cursor_range.primary.ccursor;
state.ime_enabled = false;
let mut ccursor;
if !prediction.is_empty()
&& cursor_range.secondary.ccursor.index
== state.ime_cursor_range.secondary.ccursor.index
{
ccursor = text.delete_selected(&cursor_range);
text.insert_text_at(&mut ccursor, prediction, char_limit);
} else {
ccursor = cursor_range.primary.ccursor;
}
Some(CCursorRange::one(ccursor))
}
Some(CCursorRange::one(ccursor))
} else {
}
IMEEvent::Disabled => {
state.ime_enabled = false;
None
}
}
},

_ => None,
};
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/widgets/text_edit/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct TextEditState {

// If IME candidate window is shown on this text edit.
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) has_ime: bool,
pub(crate) ime_enabled: bool,

// cursor range for IME candidate.
#[cfg_attr(feature = "serde", serde(skip))]
Expand Down
Loading