Skip to content

Commit 1f94478

Browse files
committed
chore: ime base
1 parent fe40851 commit 1f94478

File tree

4 files changed

+153
-16
lines changed

4 files changed

+153
-16
lines changed

iced_examples/counter/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl Application for Counter {
7979

8080
fn update(&mut self, message: Message) -> Command<Message> {
8181
match message {
82-
Message::IcedEvent(event) => {
83-
println!("hello {event:?}");
82+
Message::IcedEvent(_event) => {
83+
//println!("hello {event:?}");
8484
Command::none()
8585
}
8686
Message::IncrementPressed => {

iced_layershell/src/application.rs

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ use crate::{
77
clipboard::LayerShellClipboard,
88
conversion,
99
error::Error,
10+
ime_preedit::Preedit,
1011
settings::VirtualKeyboardSettings,
1112
};
1213

1314
use super::{Appearance, DefaultStyle};
1415
use iced_graphics::{Compositor, compositor};
1516
use state::State;
1617

17-
use iced_core::{input_method, time::Instant, window as IcedCoreWindow, Event as IcedCoreEvent, Size};
18+
use iced_core::{
19+
Event as IcedCoreEvent, InputMethod, Size, input_method, time::Instant,
20+
window as IcedCoreWindow,
21+
};
1822

1923
use iced_runtime::{Action, Debug, Program, UserInterface, task::Task, user_interface};
2024

@@ -337,8 +341,11 @@ where
337341
iced_core::InputMethod::Disabled => {
338342
ev.set_ime_allowed(false);
339343
}
340-
iced_core::InputMethod::Enabled { position, .. } => {
344+
iced_core::InputMethod::Enabled {
345+
position, purpose, ..
346+
} => {
341347
ev.set_ime_allowed(true);
348+
ev.set_ime_purpose(conversion::ime_purpose(purpose));
342349
ev.set_ime_cursor_area(
343350
layershellev::dpi::LogicalPosition::new(position.x, position.y),
344351
layershellev::dpi::LogicalSize {
@@ -357,6 +364,94 @@ where
357364
Ok(())
358365
}
359366

367+
struct IMDrawer<A>
368+
where
369+
A: Application,
370+
A::Theme: iced_core::theme::Base,
371+
{
372+
preedit: Option<Preedit<A::Renderer>>,
373+
ime_state: Option<(iced_core::Point, input_method::Purpose)>,
374+
}
375+
376+
impl<A> IMDrawer<A>
377+
where
378+
A: Application,
379+
A::Theme: iced_core::theme::Base,
380+
{
381+
fn new() -> Self {
382+
Self {
383+
preedit: None,
384+
ime_state: None,
385+
}
386+
}
387+
pub fn request_input_method(
388+
&mut self,
389+
background_color: iced_core::Color,
390+
input_method: InputMethod,
391+
renderer: &A::Renderer,
392+
) {
393+
match input_method {
394+
InputMethod::Disabled => {
395+
self.disable_ime();
396+
}
397+
InputMethod::Enabled {
398+
position,
399+
purpose,
400+
preedit,
401+
} => {
402+
self.enable_ime(position, purpose);
403+
404+
if let Some(preedit) = preedit {
405+
if preedit.content.is_empty() {
406+
self.preedit = None;
407+
} else {
408+
let mut overlay = self.preedit.take().unwrap_or_else(Preedit::new);
409+
410+
overlay.update(position, &preedit, background_color, renderer);
411+
412+
self.preedit = Some(overlay);
413+
}
414+
} else {
415+
self.preedit = None;
416+
}
417+
}
418+
}
419+
}
420+
421+
pub fn draw_preedit(
422+
&mut self,
423+
renderer: &mut A::Renderer,
424+
text_color: iced_core::Color,
425+
background_color: iced_core::Color,
426+
logical_size: iced_core::Size,
427+
) {
428+
use iced_core::Point;
429+
use iced_core::Rectangle;
430+
if let Some(preedit) = &self.preedit {
431+
preedit.draw(
432+
renderer,
433+
text_color,
434+
background_color,
435+
&Rectangle::new(Point::ORIGIN, logical_size),
436+
);
437+
}
438+
}
439+
440+
fn enable_ime(&mut self, position: iced_core::Point, purpose: input_method::Purpose) {
441+
if self.ime_state != Some((position, purpose)) {
442+
self.ime_state = Some((position, purpose));
443+
}
444+
}
445+
446+
fn disable_ime(&mut self) {
447+
if self.ime_state.is_some() {
448+
self.ime_state = None;
449+
}
450+
451+
self.preedit = None;
452+
}
453+
}
454+
360455
#[allow(clippy::too_many_arguments)]
361456
async fn run_instance<A, E, C>(
362457
mut application: A,
@@ -386,6 +481,7 @@ async fn run_instance<A, E, C>(
386481
}
387482

388483
let mut renderer = compositor.create_renderer();
484+
let mut im_drawer: IMDrawer<A> = IMDrawer::new();
389485

390486
let cache = user_interface::Cache::default();
391487

@@ -464,9 +560,19 @@ async fn run_instance<A, E, C>(
464560
} = ui_state
465561
{
466562
events.push(redraw_event.clone());
467-
println!("ime: ?{input_method:?}");
468-
// window.request_input_method(input_method);
563+
custom_actions.push(LayerShellAction::Ime(input_method.clone()));
564+
im_drawer.request_input_method(
565+
state.background_color(),
566+
input_method,
567+
&renderer,
568+
);
469569
}
570+
im_drawer.draw_preedit(
571+
&mut renderer,
572+
state.text_color(),
573+
state.background_color(),
574+
state.viewport().logical_size(),
575+
);
470576

471577
runtime.broadcast(iced_futures::subscription::Event::Interaction {
472578
window: main_id,

iced_layershell/src/ime_preedit.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ impl<Renderer> Preedit<Renderer>
1515
where
1616
Renderer: text::Renderer,
1717
{
18-
fn new() -> Self {
18+
pub fn new() -> Self {
1919
Self {
2020
position: Point::ORIGIN,
2121
spans: Vec::new(),
2222
content: Renderer::Paragraph::default(),
2323
}
2424
}
2525

26-
fn update(
26+
pub fn update(
2727
&mut self,
2828
position: Point,
2929
preedit: &input_method::Preedit,
@@ -65,7 +65,13 @@ where
6565
}
6666
}
6767

68-
fn draw(&self, renderer: &mut Renderer, color: Color, background: Color, viewport: &Rectangle) {
68+
pub fn draw(
69+
&self,
70+
renderer: &mut Renderer,
71+
color: Color,
72+
background: Color,
73+
viewport: &Rectangle,
74+
) {
6975
use text::Paragraph as _;
7076

7177
if self.content.min_width() < 1.0 {

layershellev/src/lib.rs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -788,23 +788,38 @@ impl<T> WindowState<T> {
788788
pub fn gen_mainwindow_wrapper(&self) -> WindowWrapper {
789789
self.main_window().gen_wrapper()
790790
}
791+
791792
pub fn is_active(&self) -> bool {
792793
self.start_mode.is_active()
793794
}
795+
794796
pub fn is_background(&self) -> bool {
795797
self.start_mode.is_background()
796798
}
799+
797800
pub fn is_allscreens(&self) -> bool {
798801
self.start_mode.is_allscreens()
799802
}
803+
800804
pub fn is_with_target(&self) -> bool {
801805
self.start_mode.is_with_target()
802806
}
807+
803808
pub fn ime_allowed(&self) -> bool {
804809
self.ime_allowed
805810
}
811+
806812
pub fn set_ime_allowed(&mut self, ime_allowed: bool) {
807-
self.ime_allowed = ime_allowed
813+
self.ime_allowed = ime_allowed;
814+
for text_input in &self.text_inputs {
815+
if ime_allowed {
816+
text_input.enable();
817+
text_input.set_content_type_by_purpose(self.ime_purpose);
818+
} else {
819+
text_input.disable();
820+
}
821+
text_input.commit();
822+
}
808823
}
809824

810825
// TODO: maybe I should put text_inputs to unit
@@ -833,12 +848,21 @@ impl<T> WindowState<T> {
833848
}
834849
}
835850

851+
pub fn set_ime_purpose(&mut self, purpose: ImePurpose) {
852+
self.ime_purpose = purpose;
853+
self.text_input.iter().for_each(|text_input| {
854+
text_input.set_content_type_by_purpose(purpose);
855+
text_input.commit();
856+
});
857+
}
858+
836859
#[inline]
837860
pub fn text_input_entered(&mut self, text_input: &ZwpTextInputV3) {
838861
if !self.text_inputs.iter().any(|t| t == text_input) {
839862
self.text_inputs.push(text_input.clone());
840863
}
841864
}
865+
842866
#[inline]
843867
pub fn text_input_left(&mut self, text_input: &ZwpTextInputV3) {
844868
if let Some(position) = self.text_inputs.iter().position(|t| t == text_input) {
@@ -1234,6 +1258,11 @@ impl<T: 'static> Dispatch<wl_seat::WlSeat, ()> for WindowState<T> {
12341258
if capabilities.contains(wl_seat::Capability::Touch) {
12351259
state.touch = Some(seat.get_touch(qh, ()));
12361260
}
1261+
let text_input = state
1262+
.text_input_manager
1263+
.as_ref()
1264+
.map(|manager| manager.get_text_input(seat, qh, TextInputData::default()));
1265+
state.text_input = text_input;
12371266
}
12381267
}
12391268
}
@@ -1887,6 +1916,7 @@ struct Preedit {
18871916
cursor_begin: Option<usize>,
18881917
cursor_end: Option<usize>,
18891918
}
1919+
18901920
impl<T> Dispatch<zwp_text_input_v3::ZwpTextInputV3, TextInputData> for WindowState<T> {
18911921
fn event(
18921922
state: &mut Self,
@@ -2069,10 +2099,8 @@ impl<T: 'static> WindowState<T> {
20692099
let text_input_manager = globals
20702100
.bind::<ZwpTextInputManagerV3, _, _>(&qh, 1..=1, ())
20712101
.ok();
2072-
let text_input = text_input_manager
2073-
.as_ref()
2074-
.map(|manager| manager.get_text_input(self.get_seat(), &qh, TextInputData::default()));
20752102

2103+
self.text_input_manager = text_input_manager;
20762104
event_queue.blocking_dispatch(&mut self)?; // then make a dispatch
20772105

20782106
// do the step before, you get empty list
@@ -2255,9 +2283,6 @@ impl<T: 'static> WindowState<T> {
22552283
self.xdg_output_manager = Some(xdg_output_manager);
22562284
self.connection = Some(connection);
22572285

2258-
self.text_input = text_input;
2259-
self.text_input_manager = text_input_manager;
2260-
22612286
Ok(self)
22622287
}
22632288
/// main event loop, every time dispatch, it will store the messages, and do callback. it will

0 commit comments

Comments
 (0)