diff --git a/api/sixtyfps-rs/lib.rs b/api/sixtyfps-rs/lib.rs index 9f6f5efb6..67d53556b 100644 --- a/api/sixtyfps-rs/lib.rs +++ b/api/sixtyfps-rs/lib.rs @@ -198,9 +198,9 @@ pub mod re_exports { PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size, }; pub use sixtyfps_corelib::input::{ - FocusEvent, InputEventResult, KeyCode, KeyEvent, KeyEventResult, KeyboardModifiers, - MouseEvent, ALT_MODIFIER, CONTROL_MODIFIER, COPY_PASTE_MODIFIER, LOGO_MODIFIER, - NO_MODIFIER, SHIFT_MODIFIER, + FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent, + ALT_MODIFIER, CONTROL_MODIFIER, COPY_PASTE_MODIFIER, LOGO_MODIFIER, NO_MODIFIER, + SHIFT_MODIFIER, }; pub use sixtyfps_corelib::item_tree::{ item_offset, visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, @@ -322,22 +322,6 @@ pub mod testing { KEYBOARD_MODIFIERS.with(|x| x.set(modifiers)) } - /// Simulate a series of key press and release event - pub fn send_key_clicks< - X: vtable::HasStaticVTable + HasWindow, - Component: Into> + Clone, - >( - component: &Component, - key_codes: &[crate::re_exports::KeyCode], - ) { - let component = component.clone().into(); - sixtyfps_corelib::tests::sixtyfps_send_key_clicks( - &crate::re_exports::Slice::from_slice(key_codes), - KEYBOARD_MODIFIERS.with(|x| x.get()), - component.component_window(), - ) - } - /// Simulate entering a sequence of ascii characters key by key. pub fn send_keyboard_string_sequence< X: vtable::HasStaticVTable + HasWindow, diff --git a/sixtyfps_runtime/corelib/input.rs b/sixtyfps_runtime/corelib/input.rs index ea1bf3916..60ee0b965 100644 --- a/sixtyfps_runtime/corelib/input.rs +++ b/sixtyfps_runtime/corelib/input.rs @@ -11,15 +11,13 @@ LICENSE END */ */ #![warn(missing_docs)] -use crate::component::ComponentRc; use crate::graphics::Point; use crate::item_tree::ItemVisitorResult; use crate::items::{ItemRc, ItemRef, ItemWeak}; use crate::Property; +use crate::{component::ComponentRc, SharedString}; use const_field_offset::FieldOffsets; use euclid::default::Vector2D; -use sixtyfps_corelib_macros::*; -use std::convert::TryFrom; use std::pin::Pin; use std::rc::Rc; @@ -81,220 +79,68 @@ impl Default for InputEventResult { } } -/// A key code is a symbolic name for a key on a keyboard. Depending on the -/// key mappings, different keys may produce different key codes. -/// Key codes are typically produced when pressing or releasing a key. -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, MappedKeyCode)] -#[allow(missing_docs)] -pub enum KeyCode { - Key1, - Key2, - Key3, - Key4, - Key5, - Key6, - Key7, - Key8, - Key9, - Key0, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - Escape, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - Snapshot, - Scroll, - Pause, - Insert, - Home, - Delete, - End, - PageDown, - PageUp, +/// InternalKeyCode is used to certain keys to unicode characters, since our +/// public key event only exposes a string. This enum captures this mapping. +#[derive(Debug, PartialEq, Clone)] +pub enum InternalKeyCode { + /// Code corresponding to the left cursor key - encoded as 0xE ASCII (shift out) Left, - Up, + /// Code corresponding to the right cursor key -- encoded as 0xF ASCII (shift in) Right, - Down, + /// Code corresponding to the home key -- encoded as 0x2 ASCII (start of text) + Home, + /// Code corresponding to the end key -- encoded as 0x3 ASCII (end of text) + End, + /// Code corresponding to the backspace key -- encoded as 0x7 ASCII (backspace) Back, + /// Code corresponding to the delete key -- encoded as 0x7F ASCII (delete) + Delete, + /// Code corresponding to the return key -- encoded as 0xA ASCII (newline) Return, - Space, - Compose, - Caret, - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - AbntC1, - AbntC2, - NumpadAdd, - Apostrophe, - Apps, - Asterisk, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - NumpadDecimal, - NumpadDivide, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - NumpadMultiply, - Mute, - MyComputer, - NavigateForward, - NavigateBackward, - NextTrack, - NoConvert, - NumpadComma, - NumpadEnter, - NumpadEquals, - OEM102, - Period, - PlayPause, - Plus, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - NumpadSubtract, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Copy, - Paste, - Cut, } -impl TryFrom for KeyCode { - type Error = (); +const LEFT_CODE: char = '\u{000E}'; // shift out +const RIGHT_CODE: char = '\u{000F}'; // shift in +const HOME_CODE: char = '\u{0002}'; // start of text +const END_CODE: char = '\u{0003}'; // end of text +const BACK_CODE: char = '\u{0007}'; // backspace \b +const DELETE_CODE: char = '\u{007F}'; // cancel +const RETURN_CODE: char = '\u{000A}'; // \n - fn try_from(value: char) -> Result { - Ok(match value { - 'a' => Self::A, - 'b' => Self::B, - 'c' => Self::C, - 'd' => Self::D, - 'e' => Self::E, - 'f' => Self::F, - 'g' => Self::G, - 'h' => Self::H, - 'i' => Self::I, - 'j' => Self::J, - 'k' => Self::K, - 'l' => Self::L, - 'm' => Self::M, - 'n' => Self::N, - 'o' => Self::O, - 'p' => Self::P, - 'q' => Self::Q, - 'r' => Self::R, - 's' => Self::S, - 't' => Self::T, - 'u' => Self::U, - 'v' => Self::V, - 'w' => Self::W, - 'x' => Self::X, - 'y' => Self::Y, - 'z' => Self::Z, - '1' => Self::Key1, - '2' => Self::Key2, - '3' => Self::Key3, - '4' => Self::Key4, - '5' => Self::Key5, - '6' => Self::Key6, - '7' => Self::Key7, - '8' => Self::Key8, - '9' => Self::Key9, - _ => return Err(()), - }) +impl InternalKeyCode { + /// Encodes the internal key code as string + pub fn encode_to_string(&self) -> SharedString { + match self { + InternalKeyCode::Left => LEFT_CODE, + InternalKeyCode::Right => RIGHT_CODE, + InternalKeyCode::Home => HOME_CODE, + InternalKeyCode::End => END_CODE, + InternalKeyCode::Back => BACK_CODE, + InternalKeyCode::Delete => DELETE_CODE, + InternalKeyCode::Return => RETURN_CODE, + } + .to_string() + .into() + } + /// Tries to see if the provided string corresponds to a single special + /// encoded key. + pub fn try_decode_from_string(str: &SharedString) -> Option { + let mut chars = str.chars(); + let ch = chars.next(); + if ch.is_some() && chars.next().is_none() { + Some(match ch.unwrap() { + LEFT_CODE => Self::Left, + RIGHT_CODE => Self::Right, + HOME_CODE => Self::Home, + END_CODE => Self::End, + BACK_CODE => Self::Back, + DELETE_CODE => Self::Delete, + RETURN_CODE => Self::Return, + _ => return None, + }) + } else { + None + } } } @@ -408,47 +254,18 @@ impl core::ops::BitOrAssign for KeyboardModifiers { pub enum KeyEvent { /// A key on a keyboard was pressed. KeyPressed { - /// The key code of the pressed key. - code: KeyCode, + /// The unicode representation of the key pressed. + string: SharedString, /// The keyboard modifiers active at the time of the key press event. modifiers: KeyboardModifiers, }, /// A key on a keyboard was released. KeyReleased { - /// The key code of the released key. - code: KeyCode, + /// The unicode representation of the key released. + string: SharedString, /// The keyboard modifiers active at the time of the key release event. modifiers: KeyboardModifiers, }, - /// A key on a keyboard was released that results in - /// a character that's suitable for text input. - CharacterInput { - /// The u32 is a unicode scalar value that is safe to convert to char. - unicode_scalar: u32, - /// The keyboard modifiers active at the time of the char input event. - modifiers: KeyboardModifiers, - }, -} - -impl TryFrom<(&winit::event::KeyboardInput, KeyboardModifiers)> for KeyEvent { - type Error = (); - - fn try_from( - input: (&winit::event::KeyboardInput, KeyboardModifiers), - ) -> Result { - let key_code = match input.0.virtual_keycode { - Some(code) => code.into(), - None => return Err(()), - }; - Ok(match input.0.state { - winit::event::ElementState::Pressed => { - KeyEvent::KeyPressed { code: key_code, modifiers: input.1 } - } - winit::event::ElementState::Released => { - KeyEvent::KeyReleased { code: key_code, modifiers: input.1 } - } - }) - } } /// Represents how an item's key_event handler dealt with a key event. diff --git a/sixtyfps_runtime/corelib/items.rs b/sixtyfps_runtime/corelib/items.rs index d07c7c671..ee67a417f 100644 --- a/sixtyfps_runtime/corelib/items.rs +++ b/sixtyfps_runtime/corelib/items.rs @@ -426,15 +426,11 @@ impl Item for FocusScope { fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult { match event { - KeyEvent::KeyPressed { .. } => {} - KeyEvent::KeyReleased { .. } => {} - KeyEvent::CharacterInput { unicode_scalar, .. } => { - if let Some(char) = std::char::from_u32(*unicode_scalar) { - let key = SharedString::from(char.to_string().as_str()); - // FIXME: handle pressed and release in their event - Self::FIELD_OFFSETS.key_pressed.apply_pin(self).emit(&(key.clone(),)); - Self::FIELD_OFFSETS.key_released.apply_pin(self).emit(&(key,)); - } + KeyEvent::KeyPressed { string, .. } => { + Self::FIELD_OFFSETS.key_pressed.apply_pin(self).emit(&(string.clone(),)); + } + KeyEvent::KeyReleased { string, .. } => { + Self::FIELD_OFFSETS.key_released.apply_pin(self).emit(&(string.clone(),)); } }; KeyEventResult::EventAccepted diff --git a/sixtyfps_runtime/corelib/items/text.rs b/sixtyfps_runtime/corelib/items/text.rs index 2a8335e38..6cb2f2f77 100644 --- a/sixtyfps_runtime/corelib/items/text.rs +++ b/sixtyfps_runtime/corelib/items/text.rs @@ -22,6 +22,7 @@ When adding an item or a property, it needs to be kept in sync with different pl use super::{Item, ItemConsts, ItemRc, VoidArg}; use crate::graphics::{Color, Point, Rect, Size}; +use crate::input::InternalKeyCode; use crate::input::{ FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent, MouseEventType, @@ -269,18 +270,53 @@ impl Item for TextInput { } match event { - KeyEvent::CharacterInput { unicode_scalar, .. } => { + KeyEvent::KeyPressed { string, modifiers } => { + if let Some(keycode) = InternalKeyCode::try_decode_from_string(string) { + if let Ok(text_cursor_movement) = TextCursorDirection::try_from(keycode.clone()) + { + TextInput::move_cursor( + self, + text_cursor_movement, + (*modifiers).into(), + window, + ); + return KeyEventResult::EventAccepted; + } else if keycode == InternalKeyCode::Back { + TextInput::delete_previous(self, window); + return KeyEventResult::EventAccepted; + } else if keycode == InternalKeyCode::Delete { + TextInput::delete_char(self, window); + return KeyEventResult::EventAccepted; + } else if keycode == InternalKeyCode::Return { + Self::FIELD_OFFSETS.accepted.apply_pin(self).emit(&()); + return KeyEventResult::EventAccepted; + } + } + KeyEventResult::EventIgnored + } + KeyEvent::KeyReleased { string, modifiers } + // Only insert/interpreter non-control character strings + if !string.is_empty() && string.as_str().chars().all(|ch| !ch.is_control()) => + { + if modifiers.test_exclusive(crate::input::COPY_PASTE_MODIFIER) { + if string == "c" { + self.copy(); + return KeyEventResult::EventAccepted; + } else if string == "v" { + self.paste(); + return KeyEventResult::EventAccepted; + } + } self.delete_selection(); let mut text: String = self.text().into(); // FIXME: respect grapheme boundaries let insert_pos = self.cursor_position() as usize; - let ch = char::try_from(*unicode_scalar).unwrap().to_string(); - text.insert_str(insert_pos, &ch); + text.insert_str(insert_pos, &string); self.as_ref().text.set(text.into()); - let new_cursor_pos = (insert_pos + ch.len()) as i32; + let new_cursor_pos = (insert_pos + string.len()) as i32; self.as_ref().cursor_position.set(new_cursor_pos); self.as_ref().anchor_position.set(new_cursor_pos); @@ -292,68 +328,6 @@ impl Item for TextInput { KeyEventResult::EventAccepted } - KeyEvent::KeyPressed { code, modifiers } if *code == crate::input::KeyCode::Right => { - TextInput::move_cursor( - self, - TextCursorDirection::Forward, - (*modifiers).into(), - window, - ); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, modifiers } if *code == crate::input::KeyCode::Left => { - TextInput::move_cursor( - self, - TextCursorDirection::Backward, - (*modifiers).into(), - window, - ); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, modifiers } if *code == crate::input::KeyCode::Home => { - TextInput::move_cursor( - self, - TextCursorDirection::StartOfLine, - (*modifiers).into(), - window, - ); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, modifiers } if *code == crate::input::KeyCode::End => { - TextInput::move_cursor( - self, - TextCursorDirection::EndOfLine, - (*modifiers).into(), - window, - ); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, .. } if *code == crate::input::KeyCode::Back => { - TextInput::delete_previous(self, window); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, .. } if *code == crate::input::KeyCode::Delete => { - TextInput::delete_char(self, window); - KeyEventResult::EventAccepted - } - KeyEvent::KeyPressed { code, .. } if *code == crate::input::KeyCode::Return => { - Self::FIELD_OFFSETS.accepted.apply_pin(self).emit(&()); - KeyEventResult::EventAccepted - } - KeyEvent::KeyReleased { code, modifiers } - if modifiers.test_exclusive(crate::input::COPY_PASTE_MODIFIER) - && *code == crate::input::KeyCode::C => - { - self.copy(); - KeyEventResult::EventAccepted - } - KeyEvent::KeyReleased { code, modifiers } - if modifiers.test_exclusive(crate::input::COPY_PASTE_MODIFIER) - && *code == crate::input::KeyCode::V => - { - self.paste(); - KeyEventResult::EventAccepted - } _ => KeyEventResult::EventIgnored, } } @@ -390,6 +364,20 @@ enum TextCursorDirection { EndOfLine, } +impl std::convert::TryFrom for TextCursorDirection { + type Error = (); + + fn try_from(value: InternalKeyCode) -> Result { + Ok(match value { + InternalKeyCode::Left => Self::Backward, + InternalKeyCode::Right => Self::Forward, + InternalKeyCode::Home => Self::StartOfLine, + InternalKeyCode::End => Self::EndOfLine, + _ => return Err(()), + }) + } +} + enum AnchorMode { KeepAnchor, MoveAnchor, diff --git a/sixtyfps_runtime/corelib/tests.rs b/sixtyfps_runtime/corelib/tests.rs index cae175d19..fe1717d2e 100644 --- a/sixtyfps_runtime/corelib/tests.rs +++ b/sixtyfps_runtime/corelib/tests.rs @@ -10,8 +10,9 @@ LICENSE END */ //! Functions usefull for testing #![warn(missing_docs)] -use crate::input::{MouseEvent, MouseEventType}; +use crate::input::{KeyEvent, KeyboardModifiers, MouseEvent, MouseEventType, SHIFT_MODIFIER}; use crate::window::ComponentWindow; +use crate::SharedString; /// SixtyFPS animations do not use real time, but use a mocked time. /// Normally, the event loop update the time of the animation using @@ -60,60 +61,21 @@ pub extern "C" fn sixtyfps_send_mouse_click( ); } -/// Simulate a key down event. -#[no_mangle] -pub extern "C" fn sixtyfps_send_key_clicks( - key_codes: &crate::slice::Slice, - modifiers: crate::input::KeyboardModifiers, - window: &ComponentWindow, -) { - for key_code in key_codes.iter() { - window - .process_key_input(&crate::input::KeyEvent::KeyPressed { code: *key_code, modifiers }); - window - .process_key_input(&crate::input::KeyEvent::KeyReleased { code: *key_code, modifiers }); - } -} - /// Simulate a character input event. #[no_mangle] pub extern "C" fn send_keyboard_string_sequence( sequence: &crate::SharedString, - modifiers: crate::input::KeyboardModifiers, + modifiers: KeyboardModifiers, window: &ComponentWindow, ) { - use std::convert::TryInto; - - let key_down = |maybe_code: &Option| { - maybe_code.clone().map(|code| { - window.process_key_input(&crate::input::KeyEvent::KeyPressed { code: code, modifiers }); - }); - }; - - let key_up = |maybe_code: &Option| { - maybe_code.clone().map(|code| { - window - .process_key_input(&crate::input::KeyEvent::KeyReleased { code: code, modifiers }); - }); - }; - for ch in sequence.chars() { let mut modifiers = modifiers; - let maybe_key_code = if ch.is_ascii_uppercase() { - modifiers |= crate::input::SHIFT_MODIFIER; - ch.to_ascii_lowercase().try_into() - } else { - ch.try_into() + if ch.is_ascii_uppercase() { + modifiers |= SHIFT_MODIFIER; } - .ok(); + let string: SharedString = ch.to_string().into(); - key_down(&maybe_key_code); - - window.process_key_input(&crate::input::KeyEvent::CharacterInput { - unicode_scalar: ch.into(), - modifiers, - }); - - key_up(&maybe_key_code); + window.process_key_input(&KeyEvent::KeyPressed { string: string.clone(), modifiers }); + window.process_key_input(&KeyEvent::KeyReleased { string, modifiers }); } } diff --git a/sixtyfps_runtime/corelib_macros/lib.rs b/sixtyfps_runtime/corelib_macros/lib.rs index 9800c9c7e..a7cc3ddcc 100644 --- a/sixtyfps_runtime/corelib_macros/lib.rs +++ b/sixtyfps_runtime/corelib_macros/lib.rs @@ -175,30 +175,3 @@ fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> { } None } - -#[proc_macro_derive(MappedKeyCode)] -pub fn keycode_mapping(input: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); - - let variants = match &input.data { - syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, - _ => { - return syn::Error::new(input.ident.span(), "Only `enum` types are supported") - .to_compile_error() - .into() - } - } - .iter() - .collect::>(); - - quote!( - impl From for KeyCode { - fn from(code: winit::event::VirtualKeyCode) -> Self { - match code { - #(winit::event::VirtualKeyCode::#variants => Self::#variants),* - } - } - } - ) - .into() -} diff --git a/sixtyfps_runtime/rendering_backends/gl/eventloop.rs b/sixtyfps_runtime/rendering_backends/gl/eventloop.rs index e769df50b..b5390bc4e 100644 --- a/sixtyfps_runtime/rendering_backends/gl/eventloop.rs +++ b/sixtyfps_runtime/rendering_backends/gl/eventloop.rs @@ -16,10 +16,9 @@ LICENSE END */ use sixtyfps_corelib as corelib; use corelib::graphics::Point; -use corelib::input::{KeyEvent, MouseEventType}; +use corelib::input::{InternalKeyCode, KeyEvent, MouseEventType}; use corelib::window::*; use std::cell::RefCell; -use std::convert::TryInto; use std::rc::{Rc, Weak}; #[cfg(not(target_arch = "wasm32"))] @@ -292,19 +291,57 @@ pub fn run() { if let Some(Some(window)) = windows.borrow().get(&window_id).map(|weakref| weakref.upgrade()) { - if let Some(ref key_event) = - (input, window.current_keyboard_modifiers()).try_into().ok() + if let Some(key_code) = + input.virtual_keycode.and_then(|virtual_keycode| { + match virtual_keycode { + winit::event::VirtualKeyCode::Left => { + Some(InternalKeyCode::Left) + } + winit::event::VirtualKeyCode::Right => { + Some(InternalKeyCode::Right) + } + winit::event::VirtualKeyCode::Home => { + Some(InternalKeyCode::Home) + } + winit::event::VirtualKeyCode::End => { + Some(InternalKeyCode::End) + } + winit::event::VirtualKeyCode::Back => { + Some(InternalKeyCode::Back) + } + winit::event::VirtualKeyCode::Delete => { + Some(InternalKeyCode::Delete) + } + winit::event::VirtualKeyCode::Return => { + Some(InternalKeyCode::Return) + } + _ => None, + } + }) { + let string = key_code.encode_to_string(); + let event = match input.state { + glutin::event::ElementState::Pressed => KeyEvent::KeyPressed { + string, + modifiers: window.current_keyboard_modifiers(), + }, + glutin::event::ElementState::Released => { + KeyEvent::KeyReleased { + string, + modifiers: window.current_keyboard_modifiers(), + } + } + }; window .self_weak .get() .unwrap() .upgrade() .unwrap() - .process_key_input(key_event); + .process_key_input(&event); // FIXME: remove this, it should be based on actual changes rather than this window.request_redraw(); - } + }; } }); } @@ -321,8 +358,8 @@ pub fn run() { let modifiers = window.current_keyboard_modifiers(); if !modifiers.control() && !modifiers.alt() && !modifiers.logo() { - let key_event = KeyEvent::CharacterInput { - unicode_scalar: ch.into(), + let key_event = KeyEvent::KeyReleased { + string: ch.to_string().into(), modifiers, }; window diff --git a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs index b5816327e..9f3d715d5 100644 --- a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs +++ b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs @@ -12,7 +12,7 @@ use cpp::*; use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment}; use sixtyfps_corelib::component::ComponentRc; use sixtyfps_corelib::graphics::{FontRequest, Point, RenderingCache}; -use sixtyfps_corelib::input::{KeyCode, KeyEvent, MouseEventType}; +use sixtyfps_corelib::input::{InternalKeyCode, KeyEvent, MouseEventType}; use sixtyfps_corelib::item_rendering::{CachedRenderingData, ItemRenderer}; use sixtyfps_corelib::items::{self, ItemRef}; use sixtyfps_corelib::properties::PropertyTracker; @@ -21,7 +21,6 @@ use sixtyfps_corelib::window::PlatformWindow; use sixtyfps_corelib::{PathData, Property, Resource}; use std::cell::RefCell; -use std::convert::TryFrom; use std::pin::Pin; use std::ptr::NonNull; use std::rc::{Rc, Weak}; @@ -661,35 +660,26 @@ impl QtWindow { if modif & key_generated::Qt_KeyboardModifier_MetaModifier != 0 { modifiers |= sixtyfps_corelib::input::LOGO_MODIFIER } - let code = match key as key_generated::Qt_Key { - key_generated::Qt_Key_Key_Left => Some(KeyCode::Left), - key_generated::Qt_Key_Key_Right => Some(KeyCode::Right), - key_generated::Qt_Key_Key_Up => Some(KeyCode::Up), - key_generated::Qt_Key_Key_Down => Some(KeyCode::Down), - key_generated::Qt_Key_Key_Insert => Some(KeyCode::Insert), - key_generated::Qt_Key_Key_Backspace => Some(KeyCode::Back), - key_generated::Qt_Key_Key_Delete => Some(KeyCode::Delete), - key_generated::Qt_Key_Key_End => Some(KeyCode::End), - key_generated::Qt_Key_Key_Home => Some(KeyCode::Home), - key_generated::Qt_Key_Key_Return => Some(KeyCode::Return), - key_generated::Qt_Key_Key_Enter => Some(KeyCode::NumpadEnter), - _ => text.chars().next().and_then(|x| KeyCode::try_from(x).ok()), - }; - if let Some(code) = code { - let event = if released { - KeyEvent::KeyReleased { code, modifiers } - } else { - KeyEvent::KeyPressed { code, modifiers } - }; - self.self_weak.get().unwrap().upgrade().unwrap().process_key_input(&event); - } - if released && !text.is_empty() { - for x in text.chars() { - let event = KeyEvent::CharacterInput { unicode_scalar: x as _, modifiers }; - self.self_weak.get().unwrap().upgrade().unwrap().process_key_input(&event); - } + let string = match key as key_generated::Qt_Key { + key_generated::Qt_Key_Key_Left => Some(InternalKeyCode::Left), + key_generated::Qt_Key_Key_Right => Some(InternalKeyCode::Right), + key_generated::Qt_Key_Key_Backspace => Some(InternalKeyCode::Back), + key_generated::Qt_Key_Key_Delete => Some(InternalKeyCode::Delete), + key_generated::Qt_Key_Key_End => Some(InternalKeyCode::End), + key_generated::Qt_Key_Key_Home => Some(InternalKeyCode::Home), + key_generated::Qt_Key_Key_Return => Some(InternalKeyCode::Return), + _ => None, } + .map_or_else(|| text.into(), |code| code.encode_to_string()); + + let event = if released { + KeyEvent::KeyReleased { string, modifiers } + } else { + KeyEvent::KeyPressed { string, modifiers } + }; + self.self_weak.get().unwrap().upgrade().unwrap().process_key_input(&event); + timer_event(); } diff --git a/tests/cases/text/cursor_move.60 b/tests/cases/text/cursor_move.60 index 50c3b363c..3498db067 100644 --- a/tests/cases/text/cursor_move.60 +++ b/tests/cases/text/cursor_move.60 @@ -19,6 +19,14 @@ TestCase := TextInput { /* ```rust + +// from input.rs +const LEFT_CODE: char = '\u{000E}'; // shift out +const RIGHT_CODE: char = '\u{000F}'; // shift in +const HOME_CODE: char = '\u{0002}'; // start of text +const END_CODE: char = '\u{0003}'; // end of text +const BACK_CODE: char = '\u{0007}'; // backspace \b + let instance = TestCase::new(); sixtyfps::testing::send_mouse_click(&instance, 50., 50.); assert!(instance.get_input_focused()); @@ -28,39 +36,39 @@ assert_eq!(instance.get_test_text(), "Test"); assert!(!instance.get_has_selection()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into()); assert!(instance.get_has_selection()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Back]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string()); assert!(!instance.get_has_selection()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Back]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string()); assert_eq!(instance.get_test_text(), "Te"); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string()); assert_eq!(instance.get_test_cursor_pos(), 2); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); assert_eq!(instance.get_test_cursor_pos(), 0); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::End]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &END_CODE.to_string()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into()); assert!(instance.get_has_selection()); assert_eq!(instance.get_test_cursor_pos(), 2); assert_eq!(instance.get_test_anchor_pos(), 0); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); assert!(!instance.get_has_selection()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Home]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &HOME_CODE.to_string()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into()); assert!(instance.get_has_selection()); assert_eq!(instance.get_test_cursor_pos(), 0); diff --git a/tests/cases/text/surrogate_cursor.60 b/tests/cases/text/surrogate_cursor.60 index 3c65e1013..e22f64d9a 100644 --- a/tests/cases/text/surrogate_cursor.60 +++ b/tests/cases/text/surrogate_cursor.60 @@ -19,6 +19,11 @@ TestCase := TextInput { /* ```rust + +// from input.rs +const LEFT_CODE: char = '\u{000E}'; // shift out +const BACK_CODE: char = '\u{0007}'; // backspace \b + let instance = TestCase::new(); sixtyfps::testing::send_mouse_click(&instance, 50., 50.); assert!(instance.get_input_focused()); @@ -28,11 +33,11 @@ assert_eq!(instance.get_test_text(), "😍"); assert!(!instance.get_has_selection()); sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into()); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string()); assert!(instance.get_has_selection()); assert_eq!(instance.get_test_cursor_pos(), 0); assert_eq!(instance.get_test_anchor_pos(), 4); -sixtyfps::testing::send_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Back]); +sixtyfps::testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string()); assert!(!instance.get_has_selection()); assert_eq!(instance.get_test_text(), "");