winit-core/window: add Window::request_ime_update
Allow updating IME state atomically to make it easier for platforms where it's atomic by its nature, like Wayland. The old API is marked as deprecated and is routed to the new atomic API. Co-authored-by: dcz <gilapfco.dcz@porcupinefactory.org>
This commit is contained in:
parent
fa0795a50c
commit
08907148ec
19 changed files with 866 additions and 222 deletions
|
|
@ -20,8 +20,9 @@ use winit_core::event::{Ime, WindowEvent};
|
|||
use winit_core::event_loop::AsyncRequestSerial;
|
||||
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
|
||||
use winit_core::window::{
|
||||
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
|
||||
WindowAttributes, WindowButtons, WindowId, WindowLevel,
|
||||
CursorGrabMode, ImeCapabilities, ImeRequest, ImeRequestError, ResizeDirection, Theme,
|
||||
UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId,
|
||||
WindowLevel,
|
||||
};
|
||||
|
||||
use super::event_loop::sink::EventSink;
|
||||
|
|
@ -508,30 +509,21 @@ impl CoreWindow for Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_cursor_area(&self, position: Position, size: Size) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
if window_state.ime_allowed() {
|
||||
let scale_factor = window_state.scale_factor();
|
||||
let position = position.to_logical(scale_factor);
|
||||
let size = size.to_logical(scale_factor);
|
||||
window_state.set_ime_cursor_area(position, size);
|
||||
}
|
||||
}
|
||||
fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
|
||||
let state_changed = self.window_state.lock().unwrap().request_ime_update(request)?;
|
||||
|
||||
#[inline]
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
|
||||
if let Some(allowed) = state_changed {
|
||||
let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
|
||||
self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
|
||||
self.event_loop_awakener.ping();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_purpose(&self, purpose: ImePurpose) {
|
||||
self.window_state.lock().unwrap().set_ime_purpose(purpose);
|
||||
fn ime_capabilities(&self) -> Option<ImeCapabilities> {
|
||||
self.window_state.lock().unwrap().ime_allowed()
|
||||
}
|
||||
|
||||
fn focus_window(&self) {}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,15 @@ use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_manager
|
|||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
use winit_core::cursor::{CursorIcon, CustomCursor as CoreCustomCursor};
|
||||
use winit_core::error::{NotSupportedError, RequestError};
|
||||
use winit_core::window::{CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowId};
|
||||
use winit_core::window::{
|
||||
CursorGrabMode, ImeCapabilities, ImeRequest, ImeRequestError, ResizeDirection, Theme, WindowId,
|
||||
};
|
||||
|
||||
use crate::event_loop::OwnedDisplayHandle;
|
||||
use crate::logical_to_physical_rounded;
|
||||
use crate::seat::{
|
||||
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
|
||||
PointerConstraintsState, TextInputClientState, WinitPointerData, WinitPointerDataExt,
|
||||
ZwpTextInputV3Ext,
|
||||
};
|
||||
use crate::state::{WindowCompositorUpdate, WinitState};
|
||||
use crate::types::cursor::{CustomCursor, SelectedCursor, WaylandCustomCursor};
|
||||
|
|
@ -113,11 +116,11 @@ pub struct WindowState {
|
|||
/// The current cursor grabbing mode.
|
||||
cursor_grab_mode: GrabState,
|
||||
|
||||
/// Whether the IME input is allowed for that window.
|
||||
ime_allowed: bool,
|
||||
|
||||
/// The current IME purpose.
|
||||
ime_purpose: ImePurpose,
|
||||
/// The input method properties provided by the application to the IME.
|
||||
///
|
||||
/// This state is cached here so that the window can automatically send the state to the IME as
|
||||
/// soon as it becomes available without application involvement.
|
||||
text_input_state: Option<TextInputClientState>,
|
||||
|
||||
/// The text inputs observed on the window.
|
||||
text_inputs: Vec<ZwpTextInputV3>,
|
||||
|
|
@ -211,8 +214,7 @@ impl WindowState {
|
|||
frame_callback_state: FrameCallbackState::None,
|
||||
seat_focus: Default::default(),
|
||||
has_pending_move: None,
|
||||
ime_allowed: false,
|
||||
ime_purpose: ImePurpose::Normal,
|
||||
text_input_state: None,
|
||||
last_configure: None,
|
||||
max_surface_size: None,
|
||||
min_surface_size: MIN_WINDOW_SIZE,
|
||||
|
|
@ -544,8 +546,12 @@ impl WindowState {
|
|||
|
||||
/// Whether the IME is allowed.
|
||||
#[inline]
|
||||
pub fn ime_allowed(&self) -> bool {
|
||||
self.ime_allowed
|
||||
pub fn ime_allowed(&self) -> Option<ImeCapabilities> {
|
||||
self.text_input_state.as_ref().map(|state| state.capabilities())
|
||||
}
|
||||
|
||||
pub(crate) fn text_input_state(&self) -> Option<&TextInputClientState> {
|
||||
self.text_input_state.as_ref()
|
||||
}
|
||||
|
||||
/// Get the size of the window.
|
||||
|
|
@ -983,53 +989,61 @@ impl WindowState {
|
|||
self.seat_focus.remove(seat);
|
||||
}
|
||||
|
||||
/// Returns `true` if the requested state was applied.
|
||||
pub fn set_ime_allowed(&mut self, allowed: bool) -> bool {
|
||||
self.ime_allowed = allowed;
|
||||
/// Atomically update input method state.
|
||||
///
|
||||
/// Returns `None` if an input method state haven't changed. Alternatively `Some(true)` and
|
||||
/// `Some(false)` is returned respectfully.
|
||||
pub fn request_ime_update(
|
||||
&mut self,
|
||||
request: ImeRequest,
|
||||
) -> Result<Option<bool>, ImeRequestError> {
|
||||
let state_change = match request {
|
||||
ImeRequest::Enable(enable) => {
|
||||
let (capabilities, request_data) = enable.into_raw();
|
||||
|
||||
let mut applied = false;
|
||||
if self.text_input_state.is_some() {
|
||||
return Err(ImeRequestError::AlreadyEnabled);
|
||||
}
|
||||
|
||||
self.text_input_state = Some(TextInputClientState::new(
|
||||
capabilities,
|
||||
request_data,
|
||||
self.scale_factor(),
|
||||
));
|
||||
true
|
||||
},
|
||||
ImeRequest::Update(request_data) => {
|
||||
let scale_factor = self.scale_factor();
|
||||
if let Some(text_input_state) = self.text_input_state.as_mut() {
|
||||
text_input_state.update(request_data, scale_factor);
|
||||
} else {
|
||||
return Err(ImeRequestError::NotEnabled);
|
||||
}
|
||||
false
|
||||
},
|
||||
ImeRequest::Disable => {
|
||||
self.text_input_state = None;
|
||||
true
|
||||
},
|
||||
};
|
||||
|
||||
// Only one input method may be active per (seat, surface),
|
||||
// but there may be multiple seats focused on a surface,
|
||||
// resulting in multiple text input objects.
|
||||
//
|
||||
// WARNING: this doesn't actually handle different seats with independent cursors. There's
|
||||
// no API to set a per-seat input method state, so they all share a single state.
|
||||
for text_input in &self.text_inputs {
|
||||
applied = true;
|
||||
if allowed {
|
||||
text_input.enable();
|
||||
text_input.set_content_type_by_purpose(self.ime_purpose);
|
||||
} else {
|
||||
text_input.disable();
|
||||
}
|
||||
text_input.commit();
|
||||
text_input.set_state(self.text_input_state.as_ref(), state_change);
|
||||
}
|
||||
|
||||
applied
|
||||
}
|
||||
|
||||
/// Set the IME position.
|
||||
pub fn set_ime_cursor_area(&self, position: LogicalPosition<u32>, size: LogicalSize<u32>) {
|
||||
// FIXME: This won't fly unless user will have a way to request IME window per seat, since
|
||||
// the ime windows will be overlapping, but winit doesn't expose API to specify for
|
||||
// which seat we're setting IME position.
|
||||
let (x, y) = (position.x as i32, position.y as i32);
|
||||
let (width, height) = (size.width as i32, size.height as i32);
|
||||
for text_input in self.text_inputs.iter() {
|
||||
text_input.set_cursor_rectangle(x, y, width, height);
|
||||
text_input.commit();
|
||||
if state_change {
|
||||
Ok(Some(self.text_input_state.is_some()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IME purpose.
|
||||
pub fn set_ime_purpose(&mut self, purpose: ImePurpose) {
|
||||
self.ime_purpose = purpose;
|
||||
|
||||
for text_input in &self.text_inputs {
|
||||
text_input.set_content_type_by_purpose(purpose);
|
||||
text_input.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the IME purpose.
|
||||
pub fn ime_purpose(&self) -> ImePurpose {
|
||||
self.ime_purpose
|
||||
}
|
||||
|
||||
/// Set the scale factor for the given window.
|
||||
#[inline]
|
||||
pub fn set_scale_factor(&mut self, scale_factor: f64) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue