Provide a way to set cursor area for IME cursor

Rename `Window::set_ime_position` to `Window::set_ime_cursor_area`
adding a way to create cursor exclusive zone.

Fixes #2886.
This commit is contained in:
Kirill Chibisov 2023-06-22 19:12:14 +00:00 committed by GitHub
parent 66ff52b012
commit 05444628e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 72 additions and 43 deletions

View file

@ -872,7 +872,7 @@ impl Window {
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
pub fn set_ime_position(&self, _position: Position) {}
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
pub fn set_ime_allowed(&self, _allowed: bool) {}

View file

@ -295,8 +295,8 @@ impl Inner {
warn!("`Window::set_window_icon` is ignored on iOS")
}
pub fn set_ime_position(&self, _position: Position) {
warn!("`Window::set_ime_position` is ignored on iOS")
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {
warn!("`Window::set_ime_cursor_area` is ignored on iOS")
}
pub fn set_ime_allowed(&self, _allowed: bool) {

View file

@ -512,8 +512,8 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, position: Position) {
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
x11_or_wayland!(match self; Window(w) => w.set_ime_cursor_area(position, size))
}
#[inline]

View file

@ -534,12 +534,13 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, position: Position) {
pub 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);
window_state.set_ime_position(position);
let size = size.to_logical(scale_factor);
window_state.set_ime_cursor_area(position, size);
}
}

View file

@ -741,13 +741,14 @@ impl WindowState {
}
/// Set the IME position.
pub fn set_ime_position(&self, position: LogicalPosition<u32>) {
pub fn set_ime_cursor_area(&self, position: LogicalPosition<u32>, size: LogicalSize<u32>) {
// XXX 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, 0, 0);
text_input.set_cursor_rectangle(x, y, width, height);
text_input.commit();
}
}

View file

@ -1510,7 +1510,7 @@ impl UnownedWindow {
}
#[inline]
pub fn set_ime_position(&self, spot: Position) {
pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
let _ = self
.ime_sender

View file

@ -202,11 +202,15 @@ pub(crate) fn close_sync(window: &NSWindow) {
});
}
pub(crate) fn set_ime_position_sync(window: &WinitWindow, logical_spot: LogicalPosition<f64>) {
pub(crate) fn set_ime_cursor_area_sync(
window: &WinitWindow,
logical_spot: LogicalPosition<f64>,
size: LogicalSize<f64>,
) {
let window = MainThreadSafe(window);
run_on_main(move || {
// TODO(madsmtm): Remove the need for this
unsafe { Id::from_shared(window.view()) }.set_ime_position(logical_spot);
unsafe { Id::from_shared(window.view()) }.set_ime_cursor_area(logical_spot, size);
});
}

View file

@ -122,6 +122,7 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
pub(super) struct ViewState {
pub cursor_state: Mutex<CursorState>,
ime_position: LogicalPosition<f64>,
ime_size: LogicalSize<f64>,
pub(super) modifiers: Modifiers,
phys_modifiers: HashMap<Key, ModLocationMask>,
tracking_rect: Option<NSTrackingRectTag>,
@ -167,6 +168,7 @@ declare_class!(
let state = ViewState {
cursor_state: Default::default(),
ime_position: LogicalPosition::new(0.0, 0.0),
ime_size: Default::default(),
modifiers: Default::default(),
phys_modifiers: Default::default(),
tracking_rect: None,
@ -416,11 +418,8 @@ declare_class!(
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
let x = base_x + self.state.ime_position.x;
let y = base_y - self.state.ime_position.y;
// This is not ideal: We _should_ return a different position based on
// the currently selected character (which varies depending on the type
// and size of the character), but in the current `winit` API there is
// no way to express this. Same goes for the `NSSize`.
NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0))
let LogicalSize { width, height } = self.state.ime_size;
NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(width, height))
}
#[sel(insertText:replacementRange:)]
@ -871,8 +870,13 @@ impl WinitView {
}
}
pub(super) fn set_ime_position(&mut self, position: LogicalPosition<f64>) {
pub(super) fn set_ime_cursor_area(
&mut self,
position: LogicalPosition<f64>,
size: LogicalSize<f64>,
) {
self.state.ime_position = position;
self.state.ime_size = size;
let input_context = self.inputContext().expect("input context");
input_context.invalidateCharacterCoordinates();
}

View file

@ -1161,10 +1161,11 @@ impl WinitWindow {
}
#[inline]
pub fn set_ime_position(&self, spot: Position) {
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
let scale_factor = self.scale_factor();
let logical_spot = spot.to_logical(scale_factor);
util::set_ime_position_sync(self, logical_spot);
let size = size.to_logical(scale_factor);
util::set_ime_cursor_area_sync(self, logical_spot, size);
}
#[inline]

View file

@ -324,7 +324,7 @@ impl Window {
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
#[inline]
pub fn set_ime_position(&self, _position: Position) {}
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
#[inline]
pub fn set_ime_allowed(&self, _allowed: bool) {}

View file

@ -327,7 +327,7 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, _position: Position) {
pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) {
// Currently a no-op as it does not seem there is good support for this on web
}

View file

@ -1,12 +1,11 @@
use std::{
ffi::{c_void, OsString},
mem::zeroed,
os::windows::prelude::OsStringExt,
ptr::null_mut,
};
use windows_sys::Win32::{
Foundation::POINT,
Foundation::{POINT, RECT},
Globalization::HIMC,
UI::{
Input::Ime::{
@ -19,7 +18,10 @@ use windows_sys::Win32::{
},
};
use crate::{dpi::Position, platform::windows::HWND};
use crate::{
dpi::{Position, Size},
platform::windows::HWND,
};
pub struct ImeContext {
hwnd: HWND,
@ -109,17 +111,24 @@ impl ImeContext {
}
}
pub unsafe fn set_ime_position(&self, spot: Position, scale_factor: f64) {
pub unsafe fn set_ime_cursor_area(&self, spot: Position, size: Size, scale_factor: f64) {
if !ImeContext::system_has_ime() {
return;
}
let (x, y) = spot.to_physical::<i32>(scale_factor).into();
let (width, height): (i32, i32) = size.to_physical::<i32>(scale_factor).into();
let rc_area = RECT {
left: x,
top: y,
right: x + width,
bottom: y - height,
};
let candidate_form = CANDIDATEFORM {
dwIndex: 0,
dwStyle: CFS_EXCLUDE,
ptCurrentPos: POINT { x, y },
rcArea: zeroed(),
rcArea: rc_area,
};
ImmSetCandidateWindow(self.himc, &candidate_form);

View file

@ -722,9 +722,9 @@ impl Window {
}
#[inline]
pub fn set_ime_position(&self, spot: Position) {
pub fn set_ime_cursor_area(&self, spot: Position, size: Size) {
unsafe {
ImeContext::current(self.hwnd()).set_ime_position(spot, self.scale_factor());
ImeContext::current(self.hwnd()).set_ime_cursor_area(spot, size, self.scale_factor());
}
}