Skip to content

Commit fd95924

Browse files
committed
chore: working, text_input
1 parent a107885 commit fd95924

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

layershellev/src/events.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,38 @@ pub struct AxisScroll {
165165
pub stop: bool,
166166
}
167167

168+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
169+
pub enum Ime {
170+
/// Notifies when the IME was enabled.
171+
///
172+
/// After getting this event you could receive [`Preedit`][Self::Preedit] and
173+
/// [`Commit`][Self::Commit] events. You should also start performing IME related requests
174+
/// like [`Window::set_ime_cursor_area`].
175+
Enabled,
176+
177+
/// Notifies when a new composing text should be set at the cursor position.
178+
///
179+
/// The value represents a pair of the preedit string and the cursor begin position and end
180+
/// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
181+
/// this indicates that preedit was cleared.
182+
///
183+
/// The cursor position is byte-wise indexed.
184+
Preedit(String, Option<(usize, usize)>),
185+
186+
/// Notifies when text should be inserted into the editor widget.
187+
///
188+
/// Right before this event winit will send empty [`Self::Preedit`] event.
189+
Commit(String),
190+
191+
/// Notifies when the IME was disabled.
192+
///
193+
/// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
194+
/// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event. You should
195+
/// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear
196+
/// pending preedit text.
197+
Disabled,
198+
}
199+
168200
#[allow(unused)]
169201
#[derive(Debug, Clone)]
170202
pub(crate) enum DispatchMessageInner {
@@ -252,6 +284,7 @@ pub(crate) enum DispatchMessageInner {
252284
scale_float: f64,
253285
},
254286
XdgInfoChanged(XdgInfoChangedType),
287+
Ime(Ime),
255288
}
256289

257290
/// This tell the DispatchMessage by dispatch
@@ -348,6 +381,7 @@ pub enum DispatchMessage {
348381
scale_u32: u32,
349382
scale_float: f64,
350383
},
384+
Ime(Ime),
351385
}
352386

353387
impl From<DispatchMessageInner> for DispatchMessage {
@@ -461,6 +495,7 @@ impl From<DispatchMessageInner> for DispatchMessage {
461495
scale_u32,
462496
scale_float,
463497
},
498+
DispatchMessageInner::Ime(ime) => DispatchMessage::Ime(ime),
464499
DispatchMessageInner::RefreshSurface { .. } => unimplemented!(),
465500
DispatchMessageInner::XdgInfoChanged(_) => unimplemented!(),
466501
}

layershellev/src/lib.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
188188
zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
189189
};
190190

191+
use wayland_protocols::wp::text_input::zv3::client::{
192+
zwp_text_input_manager_v3::ZwpTextInputManagerV3,
193+
zwp_text_input_v3::{self, ContentHint, ContentPurpose, ZwpTextInputV3},
194+
};
195+
191196
pub use calloop;
192197

193198
use calloop::{
@@ -571,6 +576,18 @@ impl<T> WindowStateUnit<T> {
571576
}
572577
}
573578

579+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
580+
pub enum ImePurpose {
581+
/// No special hints for the IME (default).
582+
Normal,
583+
/// The IME is used for password input.
584+
Password,
585+
/// The IME is used to input into a terminal.
586+
///
587+
/// For example, that could alter OSK on Wayland to show extra buttons.
588+
Terminal,
589+
}
590+
574591
/// main state, store the main information
575592
#[derive(Debug)]
576593
pub struct WindowState<T> {
@@ -627,6 +644,12 @@ pub struct WindowState<T> {
627644
start_mode: StartMode,
628645
init_finished: bool,
629646
events_transparent: bool,
647+
648+
text_input_manager: Option<ZwpTextInputManagerV3>,
649+
text_input: Option<ZwpTextInputV3>,
650+
text_inputs: Vec<ZwpTextInputV3>,
651+
ime_purpose: ImePurpose,
652+
ime_allowed: bool,
630653
}
631654

632655
impl<T> WindowState<T> {
@@ -776,6 +799,44 @@ impl<T> WindowState<T> {
776799
pub fn is_with_target(&self) -> bool {
777800
self.start_mode.is_with_target()
778801
}
802+
pub fn ime_allowed(&self) -> bool {
803+
self.ime_allowed
804+
}
805+
pub fn set_ime_allowed(&mut self, ime_allowed: bool) {
806+
self.ime_allowed = ime_allowed
807+
}
808+
809+
#[inline]
810+
pub fn text_input_entered(&mut self, text_input: &ZwpTextInputV3) {
811+
if !self.text_inputs.iter().any(|t| t == text_input) {
812+
self.text_inputs.push(text_input.clone());
813+
}
814+
}
815+
#[inline]
816+
pub fn text_input_left(&mut self, text_input: &ZwpTextInputV3) {
817+
if let Some(position) = self.text_inputs.iter().position(|t| t == text_input) {
818+
self.text_inputs.remove(position);
819+
}
820+
}
821+
822+
fn ime_purpose(&self) -> ImePurpose {
823+
self.ime_purpose
824+
}
825+
}
826+
827+
pub trait ZwpTextInputV3Ext {
828+
fn set_content_type_by_purpose(&self, purpose: ImePurpose);
829+
}
830+
831+
impl ZwpTextInputV3Ext for ZwpTextInputV3 {
832+
fn set_content_type_by_purpose(&self, purpose: ImePurpose) {
833+
let (hint, purpose) = match purpose {
834+
ImePurpose::Normal => (ContentHint::None, ContentPurpose::Normal),
835+
ImePurpose::Password => (ContentHint::SensitiveData, ContentPurpose::Password),
836+
ImePurpose::Terminal => (ContentHint::None, ContentPurpose::Terminal),
837+
};
838+
self.set_content_type(hint, purpose);
839+
}
779840
}
780841

781842
impl WindowWrapper {
@@ -1007,6 +1068,12 @@ impl<T> Default for WindowState<T> {
10071068
start_mode: StartMode::Active,
10081069
init_finished: false,
10091070
events_transparent: false,
1071+
1072+
text_input_manager: None,
1073+
text_input: None,
1074+
text_inputs: Vec::new(),
1075+
ime_purpose: ImePurpose::Normal,
1076+
ime_allowed: false,
10101077
}
10111078
}
10121079
}
@@ -1771,6 +1838,55 @@ impl<T> Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ()> for WindowStat
17711838
}
17721839
}
17731840

1841+
impl<T> Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WindowState<T> {
1842+
fn event(
1843+
state: &mut Self,
1844+
text_input: &zwp_text_input_v3::ZwpTextInputV3,
1845+
event: <zwp_text_input_v3::ZwpTextInputV3 as Proxy>::Event,
1846+
data: &(),
1847+
conn: &Connection,
1848+
qhandle: &QueueHandle<Self>,
1849+
) {
1850+
use zwp_text_input_v3::Event;
1851+
match event {
1852+
Event::Enter { surface } => {
1853+
let Some(id) = state.get_id_from_surface(&surface) else {
1854+
return;
1855+
};
1856+
if state.ime_allowed() {
1857+
text_input.enable();
1858+
text_input.set_content_type_by_purpose(state.ime_purpose());
1859+
text_input.commit();
1860+
state
1861+
.message
1862+
.push((Some(id), DispatchMessageInner::Ime(events::Ime::Enabled)));
1863+
}
1864+
state.text_input_entered(text_input);
1865+
}
1866+
Event::Leave { surface } => {
1867+
text_input.disable();
1868+
text_input.commit();
1869+
let Some(id) = state.get_id_from_surface(&surface) else {
1870+
return;
1871+
};
1872+
state.text_input_left(text_input);
1873+
state
1874+
.message
1875+
.push((Some(id), DispatchMessageInner::Ime(events::Ime::Disabled)));
1876+
}
1877+
Event::CommitString { text } => {}
1878+
Event::DeleteSurroundingText { .. } => {}
1879+
Event::Done { serial } => {}
1880+
Event::PreeditString {
1881+
text,
1882+
cursor_begin,
1883+
cursor_end,
1884+
} => {}
1885+
_ => {}
1886+
}
1887+
}
1888+
}
1889+
17741890
delegate_noop!(@<T> WindowState<T>: ignore WlCompositor); // WlCompositor is need to create a surface
17751891
delegate_noop!(@<T> WindowState<T>: ignore WlSurface); // surface is the base needed to show buffer
17761892
delegate_noop!(@<T> WindowState<T>: ignore WlOutput); // output is need to place layer_shell, although here
@@ -1796,6 +1912,8 @@ delegate_noop!(@<T> WindowState<T>: ignore WpFractionalScaleManagerV1);
17961912
delegate_noop!(@<T> WindowState<T>: ignore XdgPositioner);
17971913
delegate_noop!(@<T> WindowState<T>: ignore XdgWmBase);
17981914

1915+
delegate_noop!(@<T> WindowState<T>: ignore ZwpTextInputManagerV3);
1916+
17991917
impl<T: 'static> WindowState<T> {
18001918
/// build a new WindowState
18011919
pub fn build(mut self) -> Result<Self, LayerEventError> {
@@ -1839,6 +1957,12 @@ impl<T: 'static> WindowState<T> {
18391957
let fractional_scale_manager = globals
18401958
.bind::<WpFractionalScaleManagerV1, _, _>(&qh, 1..=1, ())
18411959
.ok();
1960+
let text_input_manager = globals
1961+
.bind::<ZwpTextInputManagerV3, _, _>(&qh, 1..=1, ())
1962+
.ok();
1963+
let text_input = text_input_manager
1964+
.as_ref()
1965+
.map(|manager| manager.get_text_input(self.get_seat(), &qh, ()));
18421966

18431967
event_queue.blocking_dispatch(&mut self)?; // then make a dispatch
18441968

@@ -2022,6 +2146,9 @@ impl<T: 'static> WindowState<T> {
20222146
self.xdg_output_manager = Some(xdg_output_manager);
20232147
self.connection = Some(connection);
20242148

2149+
self.text_input = text_input;
2150+
self.text_input_manager = text_input_manager;
2151+
20252152
Ok(self)
20262153
}
20272154
/// main event loop, every time dispatch, it will store the messages, and do callback. it will

0 commit comments

Comments
 (0)