Move Wayland backend to winit-wayland (#4252)
This commit is contained in:
parent
927af44aa4
commit
1126e9ea2f
29 changed files with 181 additions and 138 deletions
199
winit-wayland/src/seat/text_input/mod.rs
Normal file
199
winit-wayland/src/seat/text_input/mod.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
|
||||
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::{
|
||||
ContentHint, ContentPurpose, Event as TextInputEvent, ZwpTextInputV3,
|
||||
};
|
||||
use winit_core::event::{Ime, WindowEvent};
|
||||
use winit_core::window::ImePurpose;
|
||||
|
||||
use crate::state::WinitState;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TextInputState {
|
||||
text_input_manager: ZwpTextInputManagerV3,
|
||||
}
|
||||
|
||||
impl TextInputState {
|
||||
pub fn new(
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<WinitState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let text_input_manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self { text_input_manager })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TextInputState {
|
||||
type Target = ZwpTextInputManagerV3;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.text_input_manager
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwpTextInputManagerV3, GlobalData, WinitState> for TextInputState {
|
||||
fn event(
|
||||
_state: &mut WinitState,
|
||||
_proxy: &ZwpTextInputManagerV3,
|
||||
_event: <ZwpTextInputManagerV3 as Proxy>::Event,
|
||||
_data: &GlobalData,
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<WinitState>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwpTextInputV3, TextInputData, WinitState> for TextInputState {
|
||||
fn event(
|
||||
state: &mut WinitState,
|
||||
text_input: &ZwpTextInputV3,
|
||||
event: <ZwpTextInputV3 as Proxy>::Event,
|
||||
data: &TextInputData,
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<WinitState>,
|
||||
) {
|
||||
let windows = state.windows.get_mut();
|
||||
let mut text_input_data = data.inner.lock().unwrap();
|
||||
match event {
|
||||
TextInputEvent::Enter { surface } => {
|
||||
let window_id = crate::make_wid(&surface);
|
||||
text_input_data.surface = Some(surface);
|
||||
|
||||
let mut window = match windows.get(&window_id) {
|
||||
Some(window) => window.lock().unwrap(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
if window.ime_allowed() {
|
||||
text_input.enable();
|
||||
text_input.set_content_type_by_purpose(window.ime_purpose());
|
||||
text_input.commit();
|
||||
state.events_sink.push_window_event(WindowEvent::Ime(Ime::Enabled), window_id);
|
||||
}
|
||||
|
||||
window.text_input_entered(text_input);
|
||||
},
|
||||
TextInputEvent::Leave { surface } => {
|
||||
text_input_data.surface = None;
|
||||
|
||||
// Always issue a disable.
|
||||
text_input.disable();
|
||||
text_input.commit();
|
||||
|
||||
let window_id = crate::make_wid(&surface);
|
||||
|
||||
// XXX this check is essential, because `leave` could have a
|
||||
// reference to nil surface...
|
||||
let mut window = match windows.get(&window_id) {
|
||||
Some(window) => window.lock().unwrap(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
window.text_input_left(text_input);
|
||||
|
||||
state.events_sink.push_window_event(WindowEvent::Ime(Ime::Disabled), window_id);
|
||||
},
|
||||
TextInputEvent::PreeditString { text, cursor_begin, cursor_end } => {
|
||||
let text = text.unwrap_or_default();
|
||||
let cursor_begin = usize::try_from(cursor_begin)
|
||||
.ok()
|
||||
.and_then(|idx| text.is_char_boundary(idx).then_some(idx));
|
||||
let cursor_end = usize::try_from(cursor_end)
|
||||
.ok()
|
||||
.and_then(|idx| text.is_char_boundary(idx).then_some(idx));
|
||||
|
||||
text_input_data.pending_preedit = Some(Preedit { text, cursor_begin, cursor_end })
|
||||
},
|
||||
TextInputEvent::CommitString { text } => {
|
||||
text_input_data.pending_preedit = None;
|
||||
text_input_data.pending_commit = text;
|
||||
},
|
||||
TextInputEvent::Done { .. } => {
|
||||
let window_id = match text_input_data.surface.as_ref() {
|
||||
Some(surface) => crate::make_wid(surface),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Clear preedit, unless all we'll be doing next is sending a new preedit.
|
||||
if text_input_data.pending_commit.is_some()
|
||||
|| text_input_data.pending_preedit.is_none()
|
||||
{
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Send `Commit`.
|
||||
if let Some(text) = text_input_data.pending_commit.take() {
|
||||
state
|
||||
.events_sink
|
||||
.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id);
|
||||
}
|
||||
|
||||
// Send preedit.
|
||||
if let Some(preedit) = text_input_data.pending_preedit.take() {
|
||||
let cursor_range =
|
||||
preedit.cursor_begin.map(|b| (b, preedit.cursor_end.unwrap_or(b)));
|
||||
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::Ime(Ime::Preedit(preedit.text, cursor_range)),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
},
|
||||
TextInputEvent::DeleteSurroundingText { .. } => {
|
||||
// Not handled.
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ZwpTextInputV3Ext {
|
||||
fn set_content_type_by_purpose(&self, purpose: ImePurpose);
|
||||
}
|
||||
|
||||
impl ZwpTextInputV3Ext for ZwpTextInputV3 {
|
||||
fn set_content_type_by_purpose(&self, purpose: ImePurpose) {
|
||||
let (hint, purpose) = match purpose {
|
||||
ImePurpose::Password => (ContentHint::SensitiveData, ContentPurpose::Password),
|
||||
ImePurpose::Terminal => (ContentHint::None, ContentPurpose::Terminal),
|
||||
_ => (ContentHint::None, ContentPurpose::Normal),
|
||||
};
|
||||
self.set_content_type(hint, purpose);
|
||||
}
|
||||
}
|
||||
|
||||
/// The Data associated with the text input.
|
||||
#[derive(Default)]
|
||||
pub struct TextInputData {
|
||||
inner: std::sync::Mutex<TextInputDataInner>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TextInputDataInner {
|
||||
/// The `WlSurface` we're performing input to.
|
||||
surface: Option<WlSurface>,
|
||||
|
||||
/// The commit to submit on `done`.
|
||||
pending_commit: Option<String>,
|
||||
|
||||
/// The preedit to submit on `done`.
|
||||
pending_preedit: Option<Preedit>,
|
||||
}
|
||||
|
||||
/// The state of the preedit.
|
||||
struct Preedit {
|
||||
text: String,
|
||||
cursor_begin: Option<usize>,
|
||||
cursor_end: Option<usize>,
|
||||
}
|
||||
|
||||
delegate_dispatch!(WinitState: [ZwpTextInputManagerV3: GlobalData] => TextInputState);
|
||||
delegate_dispatch!(WinitState: [ZwpTextInputV3: TextInputData] => TextInputState);
|
||||
Loading…
Add table
Add a link
Reference in a new issue