mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Simplify KeyEvent
Fold CharacterInput into KeyPressed/KeyReleased and store the "key" as a string. Also, instead of exposing the KeyCode we're encoding special characters into the string.
This commit is contained in:
parent
db740831ee
commit
9ca87ab312
10 changed files with 223 additions and 463 deletions
|
@ -198,9 +198,9 @@ pub mod re_exports {
|
||||||
PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size,
|
PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size,
|
||||||
};
|
};
|
||||||
pub use sixtyfps_corelib::input::{
|
pub use sixtyfps_corelib::input::{
|
||||||
FocusEvent, InputEventResult, KeyCode, KeyEvent, KeyEventResult, KeyboardModifiers,
|
FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent,
|
||||||
MouseEvent, ALT_MODIFIER, CONTROL_MODIFIER, COPY_PASTE_MODIFIER, LOGO_MODIFIER,
|
ALT_MODIFIER, CONTROL_MODIFIER, COPY_PASTE_MODIFIER, LOGO_MODIFIER, NO_MODIFIER,
|
||||||
NO_MODIFIER, SHIFT_MODIFIER,
|
SHIFT_MODIFIER,
|
||||||
};
|
};
|
||||||
pub use sixtyfps_corelib::item_tree::{
|
pub use sixtyfps_corelib::item_tree::{
|
||||||
item_offset, visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable,
|
item_offset, visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable,
|
||||||
|
@ -322,22 +322,6 @@ pub mod testing {
|
||||||
KEYBOARD_MODIFIERS.with(|x| x.set(modifiers))
|
KEYBOARD_MODIFIERS.with(|x| x.set(modifiers))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simulate a series of key press and release event
|
|
||||||
pub fn send_key_clicks<
|
|
||||||
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
|
|
||||||
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + 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.
|
/// Simulate entering a sequence of ascii characters key by key.
|
||||||
pub fn send_keyboard_string_sequence<
|
pub fn send_keyboard_string_sequence<
|
||||||
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
|
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
|
||||||
|
|
|
@ -11,15 +11,13 @@ LICENSE END */
|
||||||
*/
|
*/
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use crate::component::ComponentRc;
|
|
||||||
use crate::graphics::Point;
|
use crate::graphics::Point;
|
||||||
use crate::item_tree::ItemVisitorResult;
|
use crate::item_tree::ItemVisitorResult;
|
||||||
use crate::items::{ItemRc, ItemRef, ItemWeak};
|
use crate::items::{ItemRc, ItemRef, ItemWeak};
|
||||||
use crate::Property;
|
use crate::Property;
|
||||||
|
use crate::{component::ComponentRc, SharedString};
|
||||||
use const_field_offset::FieldOffsets;
|
use const_field_offset::FieldOffsets;
|
||||||
use euclid::default::Vector2D;
|
use euclid::default::Vector2D;
|
||||||
use sixtyfps_corelib_macros::*;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
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
|
/// InternalKeyCode is used to certain keys to unicode characters, since our
|
||||||
/// key mappings, different keys may produce different key codes.
|
/// public key event only exposes a string. This enum captures this mapping.
|
||||||
/// Key codes are typically produced when pressing or releasing a key.
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
#[repr(C)]
|
pub enum InternalKeyCode {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, MappedKeyCode)]
|
/// Code corresponding to the left cursor key - encoded as 0xE ASCII (shift out)
|
||||||
#[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,
|
|
||||||
Left,
|
Left,
|
||||||
Up,
|
/// Code corresponding to the right cursor key -- encoded as 0xF ASCII (shift in)
|
||||||
Right,
|
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,
|
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,
|
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<char> for KeyCode {
|
const LEFT_CODE: char = '\u{000E}'; // shift out
|
||||||
type Error = ();
|
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<Self, Self::Error> {
|
impl InternalKeyCode {
|
||||||
Ok(match value {
|
/// Encodes the internal key code as string
|
||||||
'a' => Self::A,
|
pub fn encode_to_string(&self) -> SharedString {
|
||||||
'b' => Self::B,
|
match self {
|
||||||
'c' => Self::C,
|
InternalKeyCode::Left => LEFT_CODE,
|
||||||
'd' => Self::D,
|
InternalKeyCode::Right => RIGHT_CODE,
|
||||||
'e' => Self::E,
|
InternalKeyCode::Home => HOME_CODE,
|
||||||
'f' => Self::F,
|
InternalKeyCode::End => END_CODE,
|
||||||
'g' => Self::G,
|
InternalKeyCode::Back => BACK_CODE,
|
||||||
'h' => Self::H,
|
InternalKeyCode::Delete => DELETE_CODE,
|
||||||
'i' => Self::I,
|
InternalKeyCode::Return => RETURN_CODE,
|
||||||
'j' => Self::J,
|
}
|
||||||
'k' => Self::K,
|
.to_string()
|
||||||
'l' => Self::L,
|
.into()
|
||||||
'm' => Self::M,
|
}
|
||||||
'n' => Self::N,
|
/// Tries to see if the provided string corresponds to a single special
|
||||||
'o' => Self::O,
|
/// encoded key.
|
||||||
'p' => Self::P,
|
pub fn try_decode_from_string(str: &SharedString) -> Option<Self> {
|
||||||
'q' => Self::Q,
|
let mut chars = str.chars();
|
||||||
'r' => Self::R,
|
let ch = chars.next();
|
||||||
's' => Self::S,
|
if ch.is_some() && chars.next().is_none() {
|
||||||
't' => Self::T,
|
Some(match ch.unwrap() {
|
||||||
'u' => Self::U,
|
LEFT_CODE => Self::Left,
|
||||||
'v' => Self::V,
|
RIGHT_CODE => Self::Right,
|
||||||
'w' => Self::W,
|
HOME_CODE => Self::Home,
|
||||||
'x' => Self::X,
|
END_CODE => Self::End,
|
||||||
'y' => Self::Y,
|
BACK_CODE => Self::Back,
|
||||||
'z' => Self::Z,
|
DELETE_CODE => Self::Delete,
|
||||||
'1' => Self::Key1,
|
RETURN_CODE => Self::Return,
|
||||||
'2' => Self::Key2,
|
_ => return None,
|
||||||
'3' => Self::Key3,
|
})
|
||||||
'4' => Self::Key4,
|
} else {
|
||||||
'5' => Self::Key5,
|
None
|
||||||
'6' => Self::Key6,
|
}
|
||||||
'7' => Self::Key7,
|
|
||||||
'8' => Self::Key8,
|
|
||||||
'9' => Self::Key9,
|
|
||||||
_ => return Err(()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,47 +254,18 @@ impl core::ops::BitOrAssign<KeyboardModifier> for KeyboardModifiers {
|
||||||
pub enum KeyEvent {
|
pub enum KeyEvent {
|
||||||
/// A key on a keyboard was pressed.
|
/// A key on a keyboard was pressed.
|
||||||
KeyPressed {
|
KeyPressed {
|
||||||
/// The key code of the pressed key.
|
/// The unicode representation of the key pressed.
|
||||||
code: KeyCode,
|
string: SharedString,
|
||||||
/// The keyboard modifiers active at the time of the key press event.
|
/// The keyboard modifiers active at the time of the key press event.
|
||||||
modifiers: KeyboardModifiers,
|
modifiers: KeyboardModifiers,
|
||||||
},
|
},
|
||||||
/// A key on a keyboard was released.
|
/// A key on a keyboard was released.
|
||||||
KeyReleased {
|
KeyReleased {
|
||||||
/// The key code of the released key.
|
/// The unicode representation of the key released.
|
||||||
code: KeyCode,
|
string: SharedString,
|
||||||
/// The keyboard modifiers active at the time of the key release event.
|
/// The keyboard modifiers active at the time of the key release event.
|
||||||
modifiers: KeyboardModifiers,
|
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<Self, Self::Error> {
|
|
||||||
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.
|
/// Represents how an item's key_event handler dealt with a key event.
|
||||||
|
|
|
@ -426,15 +426,11 @@ impl Item for FocusScope {
|
||||||
|
|
||||||
fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
|
fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
|
||||||
match event {
|
match event {
|
||||||
KeyEvent::KeyPressed { .. } => {}
|
KeyEvent::KeyPressed { string, .. } => {
|
||||||
KeyEvent::KeyReleased { .. } => {}
|
Self::FIELD_OFFSETS.key_pressed.apply_pin(self).emit(&(string.clone(),));
|
||||||
KeyEvent::CharacterInput { unicode_scalar, .. } => {
|
}
|
||||||
if let Some(char) = std::char::from_u32(*unicode_scalar) {
|
KeyEvent::KeyReleased { string, .. } => {
|
||||||
let key = SharedString::from(char.to_string().as_str());
|
Self::FIELD_OFFSETS.key_released.apply_pin(self).emit(&(string.clone(),));
|
||||||
// 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,));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
KeyEventResult::EventAccepted
|
KeyEventResult::EventAccepted
|
||||||
|
|
|
@ -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 super::{Item, ItemConsts, ItemRc, VoidArg};
|
||||||
use crate::graphics::{Color, Point, Rect, Size};
|
use crate::graphics::{Color, Point, Rect, Size};
|
||||||
|
use crate::input::InternalKeyCode;
|
||||||
use crate::input::{
|
use crate::input::{
|
||||||
FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent,
|
FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent,
|
||||||
MouseEventType,
|
MouseEventType,
|
||||||
|
@ -269,18 +270,53 @@ impl Item for TextInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
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();
|
self.delete_selection();
|
||||||
|
|
||||||
let mut text: String = self.text().into();
|
let mut text: String = self.text().into();
|
||||||
|
|
||||||
// FIXME: respect grapheme boundaries
|
// FIXME: respect grapheme boundaries
|
||||||
let insert_pos = self.cursor_position() as usize;
|
let insert_pos = self.cursor_position() as usize;
|
||||||
let ch = char::try_from(*unicode_scalar).unwrap().to_string();
|
text.insert_str(insert_pos, &string);
|
||||||
text.insert_str(insert_pos, &ch);
|
|
||||||
|
|
||||||
self.as_ref().text.set(text.into());
|
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().cursor_position.set(new_cursor_pos);
|
||||||
self.as_ref().anchor_position.set(new_cursor_pos);
|
self.as_ref().anchor_position.set(new_cursor_pos);
|
||||||
|
|
||||||
|
@ -292,68 +328,6 @@ impl Item for TextInput {
|
||||||
|
|
||||||
KeyEventResult::EventAccepted
|
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,
|
_ => KeyEventResult::EventIgnored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,6 +364,20 @@ enum TextCursorDirection {
|
||||||
EndOfLine,
|
EndOfLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<InternalKeyCode> for TextCursorDirection {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: InternalKeyCode) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match value {
|
||||||
|
InternalKeyCode::Left => Self::Backward,
|
||||||
|
InternalKeyCode::Right => Self::Forward,
|
||||||
|
InternalKeyCode::Home => Self::StartOfLine,
|
||||||
|
InternalKeyCode::End => Self::EndOfLine,
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum AnchorMode {
|
enum AnchorMode {
|
||||||
KeepAnchor,
|
KeepAnchor,
|
||||||
MoveAnchor,
|
MoveAnchor,
|
||||||
|
|
|
@ -10,8 +10,9 @@ LICENSE END */
|
||||||
//! Functions usefull for testing
|
//! Functions usefull for testing
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use crate::input::{MouseEvent, MouseEventType};
|
use crate::input::{KeyEvent, KeyboardModifiers, MouseEvent, MouseEventType, SHIFT_MODIFIER};
|
||||||
use crate::window::ComponentWindow;
|
use crate::window::ComponentWindow;
|
||||||
|
use crate::SharedString;
|
||||||
|
|
||||||
/// SixtyFPS animations do not use real time, but use a mocked time.
|
/// SixtyFPS animations do not use real time, but use a mocked time.
|
||||||
/// Normally, the event loop update the time of the animation using
|
/// 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<crate::input::KeyCode>,
|
|
||||||
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.
|
/// Simulate a character input event.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn send_keyboard_string_sequence(
|
pub extern "C" fn send_keyboard_string_sequence(
|
||||||
sequence: &crate::SharedString,
|
sequence: &crate::SharedString,
|
||||||
modifiers: crate::input::KeyboardModifiers,
|
modifiers: KeyboardModifiers,
|
||||||
window: &ComponentWindow,
|
window: &ComponentWindow,
|
||||||
) {
|
) {
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
let key_down = |maybe_code: &Option<crate::input::KeyCode>| {
|
|
||||||
maybe_code.clone().map(|code| {
|
|
||||||
window.process_key_input(&crate::input::KeyEvent::KeyPressed { code: code, modifiers });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let key_up = |maybe_code: &Option<crate::input::KeyCode>| {
|
|
||||||
maybe_code.clone().map(|code| {
|
|
||||||
window
|
|
||||||
.process_key_input(&crate::input::KeyEvent::KeyReleased { code: code, modifiers });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
for ch in sequence.chars() {
|
for ch in sequence.chars() {
|
||||||
let mut modifiers = modifiers;
|
let mut modifiers = modifiers;
|
||||||
let maybe_key_code = if ch.is_ascii_uppercase() {
|
if ch.is_ascii_uppercase() {
|
||||||
modifiers |= crate::input::SHIFT_MODIFIER;
|
modifiers |= SHIFT_MODIFIER;
|
||||||
ch.to_ascii_lowercase().try_into()
|
|
||||||
} else {
|
|
||||||
ch.try_into()
|
|
||||||
}
|
}
|
||||||
.ok();
|
let string: SharedString = ch.to_string().into();
|
||||||
|
|
||||||
key_down(&maybe_key_code);
|
window.process_key_input(&KeyEvent::KeyPressed { string: string.clone(), modifiers });
|
||||||
|
window.process_key_input(&KeyEvent::KeyReleased { string, modifiers });
|
||||||
window.process_key_input(&crate::input::KeyEvent::CharacterInput {
|
|
||||||
unicode_scalar: ch.into(),
|
|
||||||
modifiers,
|
|
||||||
});
|
|
||||||
|
|
||||||
key_up(&maybe_key_code);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,30 +175,3 @@ fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
|
||||||
}
|
}
|
||||||
None
|
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::<Vec<_>>();
|
|
||||||
|
|
||||||
quote!(
|
|
||||||
impl From<winit::event::VirtualKeyCode> for KeyCode {
|
|
||||||
fn from(code: winit::event::VirtualKeyCode) -> Self {
|
|
||||||
match code {
|
|
||||||
#(winit::event::VirtualKeyCode::#variants => Self::#variants),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ LICENSE END */
|
||||||
use sixtyfps_corelib as corelib;
|
use sixtyfps_corelib as corelib;
|
||||||
|
|
||||||
use corelib::graphics::Point;
|
use corelib::graphics::Point;
|
||||||
use corelib::input::{KeyEvent, MouseEventType};
|
use corelib::input::{InternalKeyCode, KeyEvent, MouseEventType};
|
||||||
use corelib::window::*;
|
use corelib::window::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -292,19 +291,57 @@ pub fn run() {
|
||||||
if let Some(Some(window)) =
|
if let Some(Some(window)) =
|
||||||
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
|
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
|
||||||
{
|
{
|
||||||
if let Some(ref key_event) =
|
if let Some(key_code) =
|
||||||
(input, window.current_keyboard_modifiers()).try_into().ok()
|
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
|
window
|
||||||
.self_weak
|
.self_weak
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.process_key_input(key_event);
|
.process_key_input(&event);
|
||||||
// FIXME: remove this, it should be based on actual changes rather than this
|
// FIXME: remove this, it should be based on actual changes rather than this
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -321,8 +358,8 @@ pub fn run() {
|
||||||
let modifiers = window.current_keyboard_modifiers();
|
let modifiers = window.current_keyboard_modifiers();
|
||||||
|
|
||||||
if !modifiers.control() && !modifiers.alt() && !modifiers.logo() {
|
if !modifiers.control() && !modifiers.alt() && !modifiers.logo() {
|
||||||
let key_event = KeyEvent::CharacterInput {
|
let key_event = KeyEvent::KeyReleased {
|
||||||
unicode_scalar: ch.into(),
|
string: ch.to_string().into(),
|
||||||
modifiers,
|
modifiers,
|
||||||
};
|
};
|
||||||
window
|
window
|
||||||
|
|
|
@ -12,7 +12,7 @@ use cpp::*;
|
||||||
use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment};
|
use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment};
|
||||||
use sixtyfps_corelib::component::ComponentRc;
|
use sixtyfps_corelib::component::ComponentRc;
|
||||||
use sixtyfps_corelib::graphics::{FontRequest, Point, RenderingCache};
|
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::item_rendering::{CachedRenderingData, ItemRenderer};
|
||||||
use sixtyfps_corelib::items::{self, ItemRef};
|
use sixtyfps_corelib::items::{self, ItemRef};
|
||||||
use sixtyfps_corelib::properties::PropertyTracker;
|
use sixtyfps_corelib::properties::PropertyTracker;
|
||||||
|
@ -21,7 +21,6 @@ use sixtyfps_corelib::window::PlatformWindow;
|
||||||
use sixtyfps_corelib::{PathData, Property, Resource};
|
use sixtyfps_corelib::{PathData, Property, Resource};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
@ -661,35 +660,26 @@ impl QtWindow {
|
||||||
if modif & key_generated::Qt_KeyboardModifier_MetaModifier != 0 {
|
if modif & key_generated::Qt_KeyboardModifier_MetaModifier != 0 {
|
||||||
modifiers |= sixtyfps_corelib::input::LOGO_MODIFIER
|
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 string = match key as key_generated::Qt_Key {
|
||||||
let event = if released {
|
key_generated::Qt_Key_Key_Left => Some(InternalKeyCode::Left),
|
||||||
KeyEvent::KeyReleased { code, modifiers }
|
key_generated::Qt_Key_Key_Right => Some(InternalKeyCode::Right),
|
||||||
} else {
|
key_generated::Qt_Key_Key_Backspace => Some(InternalKeyCode::Back),
|
||||||
KeyEvent::KeyPressed { code, modifiers }
|
key_generated::Qt_Key_Key_Delete => Some(InternalKeyCode::Delete),
|
||||||
};
|
key_generated::Qt_Key_Key_End => Some(InternalKeyCode::End),
|
||||||
self.self_weak.get().unwrap().upgrade().unwrap().process_key_input(&event);
|
key_generated::Qt_Key_Key_Home => Some(InternalKeyCode::Home),
|
||||||
}
|
key_generated::Qt_Key_Key_Return => Some(InternalKeyCode::Return),
|
||||||
if released && !text.is_empty() {
|
_ => None,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.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();
|
timer_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,14 @@ TestCase := TextInput {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
```rust
|
```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();
|
let instance = TestCase::new();
|
||||||
sixtyfps::testing::send_mouse_click(&instance, 50., 50.);
|
sixtyfps::testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -28,39 +36,39 @@ assert_eq!(instance.get_test_text(), "Test");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into());
|
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());
|
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into());
|
||||||
assert!(instance.get_has_selection());
|
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());
|
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");
|
assert_eq!(instance.get_test_text(), "Te");
|
||||||
|
|
||||||
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_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]);
|
sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string());
|
||||||
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_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Right]);
|
sixtyfps::testing::send_keyboard_string_sequence(&instance, &RIGHT_CODE.to_string());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 2);
|
assert_eq!(instance.get_test_cursor_pos(), 2);
|
||||||
|
|
||||||
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_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]);
|
sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
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_key_clicks(&instance, &[sixtyfps::re_exports::KeyCode::Left]);
|
sixtyfps::testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 0);
|
assert_eq!(instance.get_test_cursor_pos(), 0);
|
||||||
|
|
||||||
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into());
|
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());
|
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into());
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 2);
|
assert_eq!(instance.get_test_cursor_pos(), 2);
|
||||||
assert_eq!(instance.get_test_anchor_pos(), 0);
|
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());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into());
|
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());
|
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::NO_MODIFIER.into());
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 0);
|
assert_eq!(instance.get_test_cursor_pos(), 0);
|
||||||
|
|
|
@ -19,6 +19,11 @@ TestCase := TextInput {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
```rust
|
```rust
|
||||||
|
|
||||||
|
// from input.rs
|
||||||
|
const LEFT_CODE: char = '\u{000E}'; // shift out
|
||||||
|
const BACK_CODE: char = '\u{0007}'; // backspace \b
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
sixtyfps::testing::send_mouse_click(&instance, 50., 50.);
|
sixtyfps::testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -28,11 +33,11 @@ assert_eq!(instance.get_test_text(), "😍");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
sixtyfps::testing::set_current_keyboard_modifiers(&instance, sixtyfps::re_exports::SHIFT_MODIFIER.into());
|
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!(instance.get_has_selection());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 0);
|
assert_eq!(instance.get_test_cursor_pos(), 0);
|
||||||
assert_eq!(instance.get_test_anchor_pos(), 4);
|
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!(!instance.get_has_selection());
|
||||||
|
|
||||||
assert_eq!(instance.get_test_text(), "");
|
assert_eq!(instance.get_test_text(), "");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue