Skip to content

Commit

Permalink
Add Window::set_ime_purpose
Browse files Browse the repository at this point in the history
This adds a way to set the purpose for the IME input, implemented
only on Wayland for now.
  • Loading branch information
lukaslihotzki authored and kchibisov committed Jan 29, 2023
1 parent 8f8da0f commit 9404ffe
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 22 deletions.
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
3 changes: 2 additions & 1 deletion src/platform_impl/linux/wayland/seat/text_input/handlers.rs
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

0 comments on commit 9404ffe

Please sign in to comment.