Skip to content
Draft
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
3 changes: 3 additions & 0 deletions winit-appkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ rust-version.workspace = true
version.workspace = true

[features]
#TODO remove from default before merging
default = ["experimental_ime_rewrite"]
experimental_ime_rewrite = []
serde = ["dep:serde", "bitflags/serde", "smol_str/serde", "dpi/serde"]

[dependencies]
Expand Down
148 changes: 129 additions & 19 deletions winit-appkit/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use winit_core::event::{
PointerKind, PointerSource, TouchPhase, WindowEvent,
};
use winit_core::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use winit_core::window::ImeCapabilities;
use winit_core::window::{ImeCapabilities, ImeSurroundingText};

use super::app_state::AppState;
use super::cursor::{default_cursor, invisible_cursor};
Expand All @@ -44,7 +44,7 @@ impl Default for CursorState {
Self { visible: true, cursor: default_cursor() }
}
}

#[cfg(not(feature = "experimental_ime_rewrite"))]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
enum ImeState {
#[default]
Expand Down Expand Up @@ -120,18 +120,19 @@ pub struct ViewState {
modifiers: Cell<Modifiers>,
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
tracking_rect: Cell<Option<NSTrackingRectTag>>,
ime_state: Cell<ImeState>,
input_source: RefCell<String>,

/// True iff the application wants IME events.
///
/// Can be set using `set_ime_allowed`
/// Some(caps) iff the application wants IME events.
ime_capabilities: Cell<Option<ImeCapabilities>>,

#[cfg(not(feature = "experimental_ime_rewrite"))]
ime_state: Cell<ImeState>,
ime_current_surroundings: RefCell<Option<ImeSurroundingText>>,
/// True if the current key event should be forwarded
/// to the application, even during IME
#[cfg(not(feature = "experimental_ime_rewrite"))]
forward_key_to_app: Cell<bool>,

#[cfg(not(feature = "experimental_ime_rewrite"))]
marked_text: RefCell<Retained<NSMutableAttributedString>>,
accepts_first_mouse: bool,

Expand Down Expand Up @@ -235,7 +236,90 @@ define_class!(
}
}
}
unsafe impl NSTextInputClient for WinitView {
#[unsafe(method(hasMarkedText))]
fn has_marked_text(&self) -> bool {
trace_scope!("hasMarkedText");
todo!()
}

#[unsafe(method(markedRange))]
fn marked_range(&self) -> NSRange {
trace_scope!("markedRange");
todo!()
}

#[unsafe(method(selectedRange))]
fn selected_range(&self) -> NSRange {
trace_scope!("selectedRange");
// Documented to return `{NSNotFound, 0}` if there is no selection.
NSRange::new(NSNotFound as NSUInteger, 0)
}

#[unsafe(method(setMarkedText:selectedRange:replacementRange:))]
fn set_marked_text(
&self,
string: &NSObject,
selected_range: NSRange,
_replacement_range: NSRange,
) {
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
trace_scope!("setMarkedText:selectedRange:replacementRange:");
todo!()
}

#[unsafe(method(unmarkText))]
fn unmark_text(&self) {
trace_scope!("unmarkText");
}

#[unsafe(method_id(validAttributesForMarkedText))]
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
trace_scope!("validAttributesForMarkedText");
NSArray::new()
}

#[unsafe(method_id(attributedSubstringForProposedRange:actualRange:))]
fn attributed_substring_for_proposed_range(
&self,
_range: NSRange,
_actual_range: *mut NSRange,
) -> Option<Retained<NSAttributedString>> {
trace_scope!("attributedSubstringForProposedRange:actualRange:");
None
}

#[unsafe(method(characterIndexForPoint:))]
fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger {
trace_scope!("characterIndexForPoint:");
0
}

#[unsafe(method(firstRectForCharacterRange:actualRange:))]
fn first_rect_for_character_range(
&self,
_range: NSRange,
_actual_range: *mut NSRange,
) -> NSRect {
trace_scope!("firstRectForCharacterRange:actualRange:");
todo!()
}

#[unsafe(method(insertText:replacementRange:))]
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
trace_scope!("insertText:replacementRange:");
}

// Basically, we're sent this message whenever a keyboard event that doesn't generate a
// "human readable" character happens, i.e. newlines, tabs, and Ctrl+C.
#[unsafe(method(doCommandBySelector:))]
fn do_command_by_selector(&self, command: Sel) {
trace_scope!("doCommandBySelector:");
todo!()
}
}
#[cfg(not(feature = "experimental_ime_rewrite"))]
unsafe impl NSTextInputClient for WinitView {
#[unsafe(method(hasMarkedText))]
fn has_marked_text(&self) -> bool {
Expand Down Expand Up @@ -272,16 +356,15 @@ define_class!(
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
trace_scope!("setMarkedText:selectedRange:replacementRange:");

let (marked_text, string) = if let Some(string) =
string.downcast_ref::<NSAttributedString>()
{
(NSMutableAttributedString::from_attributed_nsstring(string), string.string())
} else if let Some(string) = string.downcast_ref::<NSString>() {
(NSMutableAttributedString::from_nsstring(string), string.copy())
} else {
// This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
panic!("unexpected text {string:?}")
};
let (marked_text, string) =
if let Some(string) = string.downcast_ref::<NSAttributedString>() {
(NSMutableAttributedString::from_attributed_nsstring(string), string.string())
} else if let Some(string) = string.downcast_ref::<NSString>() {
(NSMutableAttributedString::from_nsstring(string), string.copy())
} else {
// This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
panic!("unexpected text {string:?}")
};

// Update marked text.
*self.ivars().marked_text.borrow_mut() = marked_text;
Expand Down Expand Up @@ -445,6 +528,7 @@ define_class!(

/// This documentation attribute makes rustfmt work for some reason?
impl WinitView {
#[cfg(not(feature = "experimental_ime_rewrite"))]
#[unsafe(method(keyDown:))]
fn key_down(&self, event: &NSEvent) {
trace_scope!("keyDown:");
Expand Down Expand Up @@ -503,7 +587,14 @@ define_class!(
});
}
}
#[cfg(feature = "experimental_ime_rewrite")]
#[unsafe(method(keyDown:))]
fn key_down(&self, event: &NSEvent) {
trace_scope!("keyDown:");
todo!()
}

#[cfg(not(feature = "experimental_ime_rewrite"))]
#[unsafe(method(keyUp:))]
fn key_up(&self, event: &NSEvent) {
trace_scope!("keyUp:");
Expand All @@ -520,7 +611,12 @@ define_class!(
});
}
}

#[cfg(feature = "experimental_ime_rewrite")]
#[unsafe(method(keyUp:))]
fn key_up(&self, event: &NSEvent) {
trace_scope!("keyUp:");
todo!()
}
#[unsafe(method(flagsChanged:))]
fn flags_changed(&self, event: &NSEvent) {
trace_scope!("flagsChanged:");
Expand Down Expand Up @@ -809,10 +905,13 @@ impl WinitView {
modifiers: Default::default(),
phys_modifiers: Default::default(),
tracking_rect: Default::default(),
#[cfg(not(feature = "experimental_ime_rewrite"))]
ime_state: Default::default(),
input_source: Default::default(),
ime_capabilities: Default::default(),
#[cfg(not(feature = "experimental_ime_rewrite"))]
forward_key_to_app: Default::default(),
#[cfg(not(feature = "experimental_ime_rewrite"))]
marked_text: Default::default(),
accepts_first_mouse,
option_as_alt: Cell::new(option_as_alt),
Expand All @@ -838,7 +937,7 @@ impl WinitView {
fn scale_factor(&self) -> f64 {
self.window().backingScaleFactor() as f64
}

#[cfg(not(feature = "experimental_ime_rewrite"))]
fn is_ime_enabled(&self) -> bool {
!matches!(self.ivars().ime_state.get(), ImeState::Disabled)
}
Expand Down Expand Up @@ -872,6 +971,7 @@ impl WinitView {
false
}
}
#[cfg(not(feature = "experimental_ime_rewrite"))]
pub(super) fn enable_ime(&self, capabilities: ImeCapabilities) {
// This seems reasonable but the prior behavior of `set_ime_allowed` doesn't do this
// (it was also broken but let's not break things worse)
Expand All @@ -889,6 +989,12 @@ impl WinitView {
self.ivars().ime_capabilities.set(Some(capabilities));
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
}
#[cfg(feature = "experimental_ime_rewrite")]
pub(super) fn enable_ime(&self, capabilities: ImeCapabilities) {
todo!();
}

#[cfg(not(feature = "experimental_ime_rewrite"))]
pub(super) fn disable_ime(&self) {
// see above
self.ivars().ime_capabilities.set(None);
Expand All @@ -900,6 +1006,10 @@ impl WinitView {
// `set_ime_allowed`
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
}
#[cfg(feature = "experimental_ime_rewrite")]
pub(super) fn disable_ime(&self) {
todo!()
}

pub(super) fn ime_capabilities(&self) -> Option<ImeCapabilities> {
self.ivars().ime_capabilities.get()
Expand Down
Loading