Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting the IME purpose #2650

Merged
merged 1 commit into from
Jan 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Wayland, support fractional scaling via the wp-fractional-scale protocol.
- On web, fix removal of mouse event listeners from the global object upon window distruction.
- Add WindowAttributes getter to WindowBuilder to allow introspection of default values.
- Added `Window::set_ime_purpose` for setting the IME purpose, currently implemented on Wayland only.

# 0.27.5

Expand Down
17 changes: 15 additions & 2 deletions examples/ime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use winit::{
dpi::PhysicalPosition,
event::{ElementState, Event, Ime, VirtualKeyCode, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
window::{ImePurpose, WindowBuilder},
};

fn main() {
Expand All @@ -18,6 +18,7 @@ fn main() {
println!("IME position will system default");
println!("Click to set IME position to cursor's");
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
println!("Press F3 to cycle through IME purposes.");

let event_loop = EventLoop::new();

Expand All @@ -26,6 +27,7 @@ fn main() {
.build(&event_loop)
.unwrap();

let mut ime_purpose = ImePurpose::Normal;
let mut ime_allowed = true;
window.set_ime_allowed(ime_allowed);

Expand Down Expand Up @@ -90,7 +92,18 @@ fn main() {
{
ime_allowed = !ime_allowed;
window.set_ime_allowed(ime_allowed);
println!("\nIME: {ime_allowed}\n");
println!("\nIME allowed: {ime_allowed}\n");
}
if input.state == ElementState::Pressed
&& input.virtual_keycode == Some(VirtualKeyCode::F3)
{
ime_purpose = match ime_purpose {
ImePurpose::Normal => ImePurpose::Password,
ImePurpose::Password => ImePurpose::Terminal,
_ => ImePurpose::Normal,
};
window.set_ime_purpose(ime_purpose);
println!("\nIME purpose: {ime_purpose:?}\n");
}
}
_ => (),
Expand Down
6 changes: 5 additions & 1 deletion src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use crate::{
error,
event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
window::{self, CursorGrabMode, ResizeDirection, Theme, WindowButtons, WindowLevel},
window::{
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
},
};

static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
Expand Down Expand Up @@ -1006,6 +1008,8 @@ impl Window {

pub fn set_ime_allowed(&self, _allowed: bool) {}

pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}

pub fn focus_window(&self) {}

pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
Expand Down
8 changes: 6 additions & 2 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use crate::{
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
},
window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWindowId, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};

Expand Down Expand Up @@ -303,6 +303,10 @@ impl Inner {
warn!("`Window::set_ime_allowed` is ignored on iOS")
}

pub fn set_ime_purpose(&self, _purpose: ImePurpose) {
warn!("`Window::set_ime_allowed` is ignored on iOS")
}

pub fn focus_window(&self) {
warn!("`Window::set_focus` is ignored on iOS")
}
Expand Down
9 changes: 7 additions & 2 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use crate::{
},
icon::Icon,
window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel,
},
};

Expand Down Expand Up @@ -519,6 +519,11 @@ impl Window {
x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
}

#[inline]
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
x11_or_wayland!(match self; Window(w) => w.set_ime_purpose(purpose))
}

#[inline]
pub fn focus_window(&self) {
match self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::event::{Ime, WindowEvent};
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::event_loop::WinitState;

use super::{Preedit, TextInputHandler, TextInputInner};
use super::{Preedit, TextInputHandler, TextInputInner, ZwpTextInputV3Ext};

#[inline]
pub(super) fn handle_text_input(
Expand All @@ -32,6 +32,7 @@ pub(super) fn handle_text_input(
// Enable text input on that surface.
if window_handle.ime_allowed.get() {
text_input.enable();
text_input.set_content_type_by_purpose(window_handle.ime_purpose.get());
text_input.commit();
event_sink.push_window_event(WindowEvent::Ime(Ime::Enabled), window_id);
}
Expand Down
31 changes: 28 additions & 3 deletions src/platform_impl/linux/wayland/seat/text_input/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::Attached;
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3;
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_v3::ZwpTextInputV3;
use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input_v3::{
ContentHint, ContentPurpose, ZwpTextInputV3,
};

use crate::platform_impl::wayland::event_loop::WinitState;
use crate::platform_impl::wayland::WindowId;
use crate::window::ImePurpose;

mod handlers;

Expand All @@ -14,6 +17,21 @@ pub struct TextInputHandler {
text_input: ZwpTextInputV3,
}

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::Normal => (ContentHint::None, ContentPurpose::Normal),
ImePurpose::Password => (ContentHint::SensitiveData, ContentPurpose::Password),
ImePurpose::Terminal => (ContentHint::None, ContentPurpose::Terminal),
};
self.set_content_type(hint, purpose);
}
}

impl TextInputHandler {
#[inline]
pub fn set_ime_position(&self, x: i32, y: i32) {
Expand All @@ -22,8 +40,15 @@ impl TextInputHandler {
}

#[inline]
pub fn set_input_allowed(&self, allowed: bool) {
if allowed {
pub fn set_content_type_by_purpose(&self, purpose: ImePurpose) {
self.text_input.set_content_type_by_purpose(purpose);
self.text_input.commit();
}

#[inline]
pub fn set_input_allowed(&self, allowed: Option<ImePurpose>) {
if let Some(purpose) = allowed {
self.text_input.set_content_type_by_purpose(purpose);
self.text_input.enable();
} else {
self.text_input.disable();
Expand Down
9 changes: 7 additions & 2 deletions src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use crate::platform_impl::{
OsError, PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
};
use crate::window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons,
};

use super::env::WindowingFeatures;
Expand Down Expand Up @@ -623,6 +623,11 @@ impl Window {
self.send_request(WindowRequest::AllowIme(allowed));
}

#[inline]
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
self.send_request(WindowRequest::ImePurpose(purpose));
}

#[inline]
pub fn display(&self) -> &Display {
&self.display
Expand Down
29 changes: 27 additions & 2 deletions src/platform_impl/linux/wayland/window/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::platform_impl::wayland::protocols::wp_fractional_scale_v1::WpFraction
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
use crate::platform_impl::wayland::WindowId;
use crate::window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType};
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, Theme, UserAttentionType};

use super::WinitFrame;

Expand Down Expand Up @@ -83,6 +83,9 @@ pub enum WindowRequest {
/// Enable IME on the given window.
AllowIme(bool),

/// Set the IME purpose.
ImePurpose(ImePurpose),

/// Mark the window as opaque.
Transparent(bool),

Expand Down Expand Up @@ -169,6 +172,9 @@ pub struct WindowHandle {
/// Allow IME events for that window.
pub ime_allowed: Cell<bool>,

/// IME purpose for that window.
pub ime_purpose: Cell<ImePurpose>,

/// Wether the window is transparent.
pub transparent: Cell<bool>,

Expand Down Expand Up @@ -226,6 +232,7 @@ impl WindowHandle {
attention_requested: Cell::new(false),
compositor,
ime_allowed: Cell::new(false),
ime_purpose: Cell::new(ImePurpose::default()),
has_focus,
}
}
Expand Down Expand Up @@ -399,8 +406,9 @@ impl WindowHandle {
self.ime_allowed.replace(allowed);
let window_id = wayland::make_wid(self.window.surface());

let purpose = allowed.then(|| self.ime_purpose.get());
for text_input in self.text_inputs.iter() {
text_input.set_input_allowed(allowed);
text_input.set_input_allowed(purpose);
}

let event = if allowed {
Expand All @@ -412,6 +420,20 @@ impl WindowHandle {
event_sink.push_window_event(event, window_id);
}

pub fn set_ime_purpose(&self, purpose: ImePurpose) {
if self.ime_purpose.get() == purpose {
return;
}

self.ime_purpose.replace(purpose);

if self.ime_allowed.get() {
for text_input in self.text_inputs.iter() {
text_input.set_content_type_by_purpose(purpose);
}
}
}

pub fn set_cursor_visible(&self, visible: bool) {
self.cursor_visible.replace(visible);
let cursor_icon = match visible {
Expand Down Expand Up @@ -475,6 +497,9 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
let event_sink = &mut winit_state.event_sink;
window_handle.set_ime_allowed(allow, event_sink);
}
WindowRequest::ImePurpose(purpose) => {
window_handle.set_ime_purpose(purpose);
}
WindowRequest::SetCursorGrabMode(mode) => {
window_handle.set_cursor_grab(mode);
}
Expand Down
5 changes: 4 additions & 1 deletion src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
},
window::{
CursorGrabMode, CursorIcon, Icon, ResizeDirection, Theme, UserAttentionType,
CursorGrabMode, CursorIcon, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel,
},
};
Expand Down Expand Up @@ -1532,6 +1532,9 @@ impl UnownedWindow {
.send(ImeRequest::Allow(self.xwindow, allowed));
}

#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}

#[inline]
pub fn focus_window(&self) {
let state_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_STATE\0") };
Expand Down
7 changes: 5 additions & 2 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use crate::{
Fullscreen, OsError,
},
window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWindowId, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};
use core_graphics::display::{CGDisplay, CGPoint};
Expand Down Expand Up @@ -1154,6 +1154,9 @@ impl WinitWindow {
unsafe { Id::from_shared(self.view()) }.set_ime_allowed(allowed);
}

#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}

#[inline]
pub fn focus_window(&self) {
let is_minimized = self.isMiniaturized();
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/orbital/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
error,
platform_impl::Fullscreen,
window,
window::ImePurpose,
};

use super::{
Expand Down Expand Up @@ -323,6 +324,9 @@ impl Window {
#[inline]
pub fn set_ime_allowed(&self, _allowed: bool) {}

#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}

#[inline]
pub fn focus_window(&self) {}

Expand Down
9 changes: 7 additions & 2 deletions src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::event;
use crate::icon::Icon;
use crate::window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWI, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowId as RootWI, WindowLevel,
};

use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
Expand Down Expand Up @@ -351,6 +351,11 @@ impl Window {
// Currently not implemented
}

#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {
// Currently not implemented
}

#[inline]
pub fn focus_window(&self) {
// Currently a no-op as it does not seem there is good support for this on web
Expand Down
7 changes: 5 additions & 2 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ use crate::{
Fullscreen, PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::{
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowLevel,
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel,
},
};

Expand Down Expand Up @@ -733,6 +733,9 @@ impl Window {
}
}

#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}

#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let window = self.window.clone();
Expand Down
Loading