mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 04:45:13 +00:00
Add support for dispatching key events through the public platform API
This change adds `KeyPress` and `KeyRelease` variants to the `WindowEvent` enum, along with the new `slint::Key` enum, that allows encoding keys.
This commit is contained in:
parent
e3838543fe
commit
61c39b5fa1
36 changed files with 681 additions and 315 deletions
|
@ -10,12 +10,16 @@ All notable changes to this project are documented in this file.
|
||||||
- `Window`'s `default-font-size` property is now always set to a non-zero value, provided by
|
- `Window`'s `default-font-size` property is now always set to a non-zero value, provided by
|
||||||
either the style or the backend.
|
either the style or the backend.
|
||||||
- In the interpreter, calling `set_property` or `get_property` on properties of the base no longer works.
|
- In the interpreter, calling `set_property` or `get_property` on properties of the base no longer works.
|
||||||
|
- Renamed the `Keys` namespace for use in `key-pressed`/`key-released` callbacks to `Key`. The
|
||||||
|
old name continues to work.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added `material` style with `material-light` and `fluent-dark` as explicit styles
|
- Added `material` style with `material-light` and `fluent-dark` as explicit styles
|
||||||
- Added `Window::is_visible`
|
- Added `Window::is_visible`
|
||||||
- Added `From<char>` for `SharedString` in Rust.
|
- Added `From<char>` for `SharedString` in Rust.
|
||||||
|
- Added `KeyPressed` and `KeyReleased` variants to `slint::WindowEvent` in Rust, along
|
||||||
|
with `slint::Key`, for use by custom platform backends.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -1005,15 +1005,25 @@ namespace slint::testing {
|
||||||
|
|
||||||
using cbindgen_private::KeyboardModifiers;
|
using cbindgen_private::KeyboardModifiers;
|
||||||
|
|
||||||
|
/// Send a key events to the given component instance
|
||||||
|
inline void send_keyboard_char(const slint::interpreter::ComponentInstance *component,
|
||||||
|
const slint::SharedString &str, bool pressed)
|
||||||
|
{
|
||||||
|
const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr;
|
||||||
|
cbindgen_private::slint_interpreter_component_instance_window(
|
||||||
|
reinterpret_cast<const cbindgen_private::ErasedComponentBox *>(component), &win_ptr);
|
||||||
|
cbindgen_private::slint_send_keyboard_char(
|
||||||
|
&str, pressed, reinterpret_cast<const cbindgen_private::WindowAdapterRc *>(win_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a key events to the given component instance
|
/// Send a key events to the given component instance
|
||||||
inline void send_keyboard_string_sequence(const slint::interpreter::ComponentInstance *component,
|
inline void send_keyboard_string_sequence(const slint::interpreter::ComponentInstance *component,
|
||||||
const slint::SharedString &str,
|
const slint::SharedString &str)
|
||||||
KeyboardModifiers modifiers = {})
|
|
||||||
{
|
{
|
||||||
const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr;
|
const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr;
|
||||||
cbindgen_private::slint_interpreter_component_instance_window(
|
cbindgen_private::slint_interpreter_component_instance_window(
|
||||||
reinterpret_cast<const cbindgen_private::ErasedComponentBox *>(component), &win_ptr);
|
reinterpret_cast<const cbindgen_private::ErasedComponentBox *>(component), &win_ptr);
|
||||||
cbindgen_private::send_keyboard_string_sequence(
|
cbindgen_private::send_keyboard_string_sequence(
|
||||||
&str, modifiers, reinterpret_cast<const cbindgen_private::WindowAdapterRc *>(win_ptr));
|
&str, reinterpret_cast<const cbindgen_private::WindowAdapterRc *>(win_ptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,17 @@ inline void send_mouse_click(const Component *component, float x, float y)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Component>
|
template<typename Component>
|
||||||
inline void send_keyboard_string_sequence(const Component *component,
|
inline void send_keyboard_char(const Component *component, const slint::SharedString &str,
|
||||||
const slint::SharedString &str,
|
bool pressed)
|
||||||
cbindgen_private::KeyboardModifiers modifiers = {})
|
|
||||||
{
|
{
|
||||||
cbindgen_private::send_keyboard_string_sequence(&str, modifiers,
|
cbindgen_private::slint_send_keyboard_char(&str, pressed, &component->m_window.window_handle());
|
||||||
&component->m_window.window_handle());
|
}
|
||||||
|
|
||||||
|
template<typename Component>
|
||||||
|
inline void send_keyboard_string_sequence(const Component *component,
|
||||||
|
const slint::SharedString &str)
|
||||||
|
{
|
||||||
|
cbindgen_private::send_keyboard_string_sequence(&str, &component->m_window.window_handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
#define assert_eq(A, B) \
|
#define assert_eq(A, B) \
|
||||||
|
|
|
@ -478,7 +478,9 @@ SCENARIO("Send key events")
|
||||||
property <string> result;
|
property <string> result;
|
||||||
scope := FocusScope {
|
scope := FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
|
if (event.text != Key.Shift && event.text != Key.Control) {
|
||||||
result += event.text;
|
result += event.text;
|
||||||
|
}
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,7 +489,7 @@ SCENARIO("Send key events")
|
||||||
"");
|
"");
|
||||||
REQUIRE(comp_def.has_value());
|
REQUIRE(comp_def.has_value());
|
||||||
auto instance = comp_def->create();
|
auto instance = comp_def->create();
|
||||||
slint::testing::send_keyboard_string_sequence(&*instance, "Hello keys!", {});
|
slint::testing::send_keyboard_string_sequence(&*instance, "Hello keys!");
|
||||||
REQUIRE(*instance->get_property("result")->to_string() == "Hello keys!");
|
REQUIRE(*instance->get_property("result")->to_string() == "Hello keys!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,8 @@ pub mod re_exports {
|
||||||
};
|
};
|
||||||
pub use i_slint_core::graphics::*;
|
pub use i_slint_core::graphics::*;
|
||||||
pub use i_slint_core::input::{
|
pub use i_slint_core::input::{
|
||||||
FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent,
|
key_codes::Key, FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers,
|
||||||
|
MouseEvent,
|
||||||
};
|
};
|
||||||
pub use i_slint_core::item_tree::{
|
pub use i_slint_core::item_tree::{
|
||||||
visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak,
|
visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak,
|
||||||
|
|
|
@ -494,7 +494,7 @@ The FocusScope exposes callback to intercept the pressed key when it has focus.
|
||||||
|
|
||||||
The KeyEvent has a text property which is a character of the key entered.
|
The KeyEvent has a text property which is a character of the key entered.
|
||||||
When a non-printable key is pressed, the character will be either a control character,
|
When a non-printable key is pressed, the character will be either a control character,
|
||||||
or it will be mapped to a private unicode character. The mapping of these non-printable, special characters is available in the [`Keys`](#keys) namespace
|
or it will be mapped to a private unicode character. The mapping of these non-printable, special characters is available in the [`Key`](#key) namespace
|
||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
|
@ -522,7 +522,7 @@ Example := Window {
|
||||||
if (event.modifiers.control) {
|
if (event.modifiers.control) {
|
||||||
debug("control was pressed during this event");
|
debug("control was pressed during this event");
|
||||||
}
|
}
|
||||||
if (event.text == Keys.Escape) {
|
if (event.text == Key.Escape) {
|
||||||
debug("Esc key was pressed")
|
debug("Esc key was pressed")
|
||||||
}
|
}
|
||||||
accept
|
accept
|
||||||
|
@ -820,9 +820,9 @@ This structure is generated and passed to the `pointer-event` callback of the `T
|
||||||
|
|
||||||
The following namespaces provide access to common constants such as special keys or named colors.
|
The following namespaces provide access to common constants such as special keys or named colors.
|
||||||
|
|
||||||
## `Keys`
|
## `Key`
|
||||||
|
|
||||||
Use the constants in the `Keys` namespace to handle pressing of keys that don't have a printable character. Check the value of [`KeyEvent`](#keyevent)'s `text` property
|
Use the constants in the `Key` namespace to handle pressing of keys that don't have a printable character. Check the value of [`KeyEvent`](#keyevent)'s `text` property
|
||||||
against the constants below.
|
against the constants below.
|
||||||
|
|
||||||
* **`Backspace`**
|
* **`Backspace`**
|
||||||
|
@ -831,6 +831,15 @@ against the constants below.
|
||||||
* **`Escape`**
|
* **`Escape`**
|
||||||
* **`Backtab`**
|
* **`Backtab`**
|
||||||
* **`Delete`**
|
* **`Delete`**
|
||||||
|
* **`Shift`**
|
||||||
|
* **`Control`**
|
||||||
|
* **`Alt`**
|
||||||
|
* **`AltGr`**
|
||||||
|
* **`CapsLock`**
|
||||||
|
* **`ShiftR`**
|
||||||
|
* **`ControlR`**
|
||||||
|
* **`Meta`**
|
||||||
|
* **`MetaR`**
|
||||||
* **`UpArrow`**
|
* **`UpArrow`**
|
||||||
* **`DownArrow`**
|
* **`DownArrow`**
|
||||||
* **`LeftArrow`**
|
* **`LeftArrow`**
|
||||||
|
|
|
@ -28,17 +28,17 @@ export Carousel := FocusScope {
|
||||||
|
|
||||||
focus-scope:= FocusScope {
|
focus-scope:= FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if(event.text == Keys.UpArrow) {
|
if(event.text == Key.UpArrow) {
|
||||||
root.move-focus-up();
|
root.move-focus-up();
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.text == Keys.RightArrow) {
|
if(event.text == Key.RightArrow) {
|
||||||
root.move-right();
|
root.move-right();
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.text == Keys.LeftArrow) {
|
if(event.text == Key.LeftArrow) {
|
||||||
root.move-left();
|
root.move-left();
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +74,11 @@ export SideBar := Rectangle {
|
||||||
current-item = current-focused;
|
current-item = current-focused;
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.UpArrow) {
|
if (event.text == Key.UpArrow) {
|
||||||
focused-tab = Math.max(focused-tab - 1, 0);
|
focused-tab = Math.max(focused-tab - 1, 0);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.DownArrow) {
|
if (event.text == Key.DownArrow) {
|
||||||
focused-tab = Math.min(focused-tab + 1, model.length - 1);
|
focused-tab = Math.min(focused-tab + 1, model.length - 1);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use i_slint_core::graphics::rendering_metrics_collector::{
|
||||||
RenderingMetrics, RenderingMetricsCollector,
|
RenderingMetrics, RenderingMetricsCollector,
|
||||||
};
|
};
|
||||||
use i_slint_core::graphics::{euclid, Brush, Color, FontRequest, Image, Point, SharedImageBuffer};
|
use i_slint_core::graphics::{euclid, Brush, Color, FontRequest, Image, Point, SharedImageBuffer};
|
||||||
use i_slint_core::input::{KeyEvent, KeyEventType, MouseEvent};
|
use i_slint_core::input::{KeyEventType, KeyInputEvent, MouseEvent};
|
||||||
use i_slint_core::item_rendering::{ItemCache, ItemRenderer};
|
use i_slint_core::item_rendering::{ItemCache, ItemRenderer};
|
||||||
use i_slint_core::items::{
|
use i_slint_core::items::{
|
||||||
self, FillRule, ImageRendering, InputType, ItemRc, ItemRef, Layer, MouseCursor, Opacity,
|
self, FillRule, ImageRendering, InputType, ItemRc, ItemRef, Layer, MouseCursor, Opacity,
|
||||||
|
@ -172,19 +172,17 @@ cpp! {{
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *event) override {
|
void keyPressEvent(QKeyEvent *event) override {
|
||||||
uint modifiers = uint(event->modifiers());
|
|
||||||
QString text = event->text();
|
QString text = event->text();
|
||||||
int key = event->key();
|
int key = event->key();
|
||||||
rust!(Slint_keyPress [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString", modifiers: u32 as "uint"] {
|
rust!(Slint_keyPress [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString"] {
|
||||||
rust_window.key_event(key, text.clone(), modifiers, false);
|
rust_window.key_event(key, text.clone(), false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void keyReleaseEvent(QKeyEvent *event) override {
|
void keyReleaseEvent(QKeyEvent *event) override {
|
||||||
uint modifiers = uint(event->modifiers());
|
|
||||||
QString text = event->text();
|
QString text = event->text();
|
||||||
int key = event->key();
|
int key = event->key();
|
||||||
rust!(Slint_keyRelease [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString", modifiers: u32 as "uint"] {
|
rust!(Slint_keyRelease [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString"] {
|
||||||
rust_window.key_event(key, text.clone(), modifiers, true);
|
rust_window.key_event(key, text.clone(), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,23 +278,23 @@ cpp! {{
|
||||||
let runtime_window = WindowInner::from_pub(&rust_window.window);
|
let runtime_window = WindowInner::from_pub(&rust_window.window);
|
||||||
|
|
||||||
if !preedit_string.is_empty() {
|
if !preedit_string.is_empty() {
|
||||||
let event = KeyEvent {
|
let event = KeyInputEvent {
|
||||||
event_type: KeyEventType::UpdateComposition,
|
event_type: KeyEventType::UpdateComposition,
|
||||||
text: preedit_string.to_string().into(),
|
text: preedit_string.to_string().into(),
|
||||||
preedit_selection_start: replacement_start as usize,
|
preedit_selection_start: replacement_start as usize,
|
||||||
preedit_selection_end: replacement_start as usize + replacement_length as usize,
|
preedit_selection_end: replacement_start as usize + replacement_length as usize,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
runtime_window.process_key_input(&event);
|
runtime_window.process_key_input(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !commit_string.is_empty() {
|
if !commit_string.is_empty() {
|
||||||
let event = KeyEvent {
|
let event = KeyInputEvent {
|
||||||
event_type: KeyEventType::CommitComposition,
|
event_type: KeyEventType::CommitComposition,
|
||||||
text: commit_string.to_string().into(),
|
text: commit_string.to_string().into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
runtime_window.process_key_input(&event);
|
runtime_window.process_key_input(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1419,25 +1417,18 @@ impl QtWindow {
|
||||||
timer_event();
|
timer_event();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_event(&self, key: i32, text: qttypes::QString, qt_modifiers: u32, released: bool) {
|
fn key_event(&self, key: i32, text: qttypes::QString, released: bool) {
|
||||||
i_slint_core::animations::update_animations();
|
i_slint_core::animations::update_animations();
|
||||||
let text: String = text.into();
|
let text: String = text.into();
|
||||||
let modifiers = i_slint_core::input::KeyboardModifiers {
|
|
||||||
control: (qt_modifiers & key_generated::Qt_KeyboardModifier_ControlModifier) != 0,
|
|
||||||
alt: (qt_modifiers & key_generated::Qt_KeyboardModifier_AltModifier) != 0,
|
|
||||||
shift: (qt_modifiers & key_generated::Qt_KeyboardModifier_ShiftModifier) != 0,
|
|
||||||
meta: (qt_modifiers & key_generated::Qt_KeyboardModifier_MetaModifier) != 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let text = qt_key_to_string(key as key_generated::Qt_Key, text);
|
let text = qt_key_to_string(key as key_generated::Qt_Key, text);
|
||||||
|
|
||||||
let event = KeyEvent {
|
let event = KeyInputEvent {
|
||||||
event_type: if released { KeyEventType::KeyReleased } else { KeyEventType::KeyPressed },
|
event_type: if released { KeyEventType::KeyReleased } else { KeyEventType::KeyPressed },
|
||||||
text,
|
text,
|
||||||
modifiers,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
WindowInner::from_pub(&self.window).process_key_input(&event);
|
WindowInner::from_pub(&self.window).process_key_input(event);
|
||||||
|
|
||||||
timer_event();
|
timer_event();
|
||||||
}
|
}
|
||||||
|
@ -2024,8 +2015,7 @@ mod key_codes {
|
||||||
$($(key_generated::$qt => $char,)*)*
|
$($(key_generated::$qt => $char,)*)*
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let mut buffer = [0; 6];
|
Some(char.into())
|
||||||
Some(i_slint_core::SharedString::from(char.encode_utf8(&mut buffer) as &str))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,14 +132,11 @@ pub fn init() {
|
||||||
|
|
||||||
/// This module contains functions useful for unit tests
|
/// This module contains functions useful for unit tests
|
||||||
mod for_unit_test {
|
mod for_unit_test {
|
||||||
use core::cell::Cell;
|
|
||||||
use i_slint_core::api::ComponentHandle;
|
use i_slint_core::api::ComponentHandle;
|
||||||
pub use i_slint_core::tests::slint_mock_elapsed_time as mock_elapsed_time;
|
pub use i_slint_core::tests::slint_mock_elapsed_time as mock_elapsed_time;
|
||||||
use i_slint_core::window::WindowInner;
|
use i_slint_core::window::WindowInner;
|
||||||
use i_slint_core::SharedString;
|
use i_slint_core::SharedString;
|
||||||
|
|
||||||
thread_local!(static KEYBOARD_MODIFIERS : Cell<i_slint_core::input::KeyboardModifiers> = Default::default());
|
|
||||||
|
|
||||||
/// Simulate a mouse click
|
/// Simulate a mouse click
|
||||||
pub fn send_mouse_click<
|
pub fn send_mouse_click<
|
||||||
X: vtable::HasStaticVTable<i_slint_core::component::ComponentVTable> + 'static,
|
X: vtable::HasStaticVTable<i_slint_core::component::ComponentVTable> + 'static,
|
||||||
|
@ -159,15 +156,20 @@ mod for_unit_test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simulate a change in keyboard modifiers being pressed
|
/// Simulate entering a sequence of ascii characters key by (pressed or released).
|
||||||
pub fn set_current_keyboard_modifiers<
|
pub fn send_keyboard_char<
|
||||||
X: vtable::HasStaticVTable<i_slint_core::component::ComponentVTable>,
|
X: vtable::HasStaticVTable<i_slint_core::component::ComponentVTable>,
|
||||||
Component: Into<vtable::VRc<i_slint_core::component::ComponentVTable, X>> + ComponentHandle,
|
Component: Into<vtable::VRc<i_slint_core::component::ComponentVTable, X>> + ComponentHandle,
|
||||||
>(
|
>(
|
||||||
_component: &Component,
|
component: &Component,
|
||||||
modifiers: i_slint_core::input::KeyboardModifiers,
|
string: char,
|
||||||
|
pressed: bool,
|
||||||
) {
|
) {
|
||||||
KEYBOARD_MODIFIERS.with(|x| x.set(modifiers))
|
i_slint_core::tests::slint_send_keyboard_char(
|
||||||
|
&SharedString::from(string),
|
||||||
|
pressed,
|
||||||
|
&WindowInner::from_pub(component.window()).window_adapter(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simulate entering a sequence of ascii characters key by key.
|
/// Simulate entering a sequence of ascii characters key by key.
|
||||||
|
@ -180,7 +182,6 @@ mod for_unit_test {
|
||||||
) {
|
) {
|
||||||
i_slint_core::tests::send_keyboard_string_sequence(
|
i_slint_core::tests::send_keyboard_string_sequence(
|
||||||
&SharedString::from(sequence),
|
&SharedString::from(sequence),
|
||||||
KEYBOARD_MODIFIERS.with(|x| x.get()),
|
|
||||||
&WindowInner::from_pub(component.window()).window_adapter(),
|
&WindowInner::from_pub(component.window()).window_adapter(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use i_slint_core as corelib;
|
||||||
|
|
||||||
use corelib::api::EventLoopError;
|
use corelib::api::EventLoopError;
|
||||||
use corelib::graphics::euclid;
|
use corelib::graphics::euclid;
|
||||||
use corelib::input::{KeyEvent, KeyEventType, KeyboardModifiers, MouseEvent};
|
use corelib::input::{KeyEventType, KeyInputEvent, MouseEvent};
|
||||||
use corelib::window::*;
|
use corelib::window::*;
|
||||||
use corelib::{Coord, SharedString};
|
use corelib::{Coord, SharedString};
|
||||||
use std::cell::{Cell, RefCell, RefMut};
|
use std::cell::{Cell, RefCell, RefMut};
|
||||||
|
@ -30,7 +30,6 @@ pub(crate) static QUIT_ON_LAST_WINDOW_CLOSED: std::sync::atomic::AtomicBool =
|
||||||
|
|
||||||
pub trait WinitWindow: WindowAdapter {
|
pub trait WinitWindow: WindowAdapter {
|
||||||
fn currently_pressed_key_code(&self) -> &Cell<Option<winit::event::VirtualKeyCode>>;
|
fn currently_pressed_key_code(&self) -> &Cell<Option<winit::event::VirtualKeyCode>>;
|
||||||
fn current_keyboard_modifiers(&self) -> &Cell<KeyboardModifiers>;
|
|
||||||
/// Returns true if during the drawing request_redraw() was called.
|
/// Returns true if during the drawing request_redraw() was called.
|
||||||
fn draw(&self) -> bool;
|
fn draw(&self) -> bool;
|
||||||
fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window));
|
fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window));
|
||||||
|
@ -225,8 +224,7 @@ mod key_codes {
|
||||||
$($(winit::event::VirtualKeyCode::$winit => $char,)*)*
|
$($(winit::event::VirtualKeyCode::$winit => $char,)*)*
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let mut buffer = [0; 6];
|
Some(char.into())
|
||||||
Some(i_slint_core::SharedString::from(char.encode_utf8(&mut buffer) as &str))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -239,23 +237,6 @@ fn process_window_event(
|
||||||
cursor_pos: &mut LogicalPoint,
|
cursor_pos: &mut LogicalPoint,
|
||||||
pressed: &mut bool,
|
pressed: &mut bool,
|
||||||
) {
|
) {
|
||||||
fn key_event(
|
|
||||||
event_type: KeyEventType,
|
|
||||||
text: SharedString,
|
|
||||||
modifiers: KeyboardModifiers,
|
|
||||||
) -> KeyEvent {
|
|
||||||
let mut event = KeyEvent { event_type, text, modifiers, ..Default::default() };
|
|
||||||
|
|
||||||
let tab = String::from(corelib::input::key_codes::Tab);
|
|
||||||
|
|
||||||
// map Shift-Tab into (Shift) Backtab to have a similar behavior as Qt backend
|
|
||||||
if event.text == tab && modifiers.shift {
|
|
||||||
event.text = SharedString::from(String::from(corelib::input::key_codes::Backtab));
|
|
||||||
}
|
|
||||||
|
|
||||||
event
|
|
||||||
}
|
|
||||||
|
|
||||||
let runtime_window = WindowInner::from_pub(window.window());
|
let runtime_window = WindowInner::from_pub(window.window());
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::Resized(size) => {
|
WindowEvent::Resized(size) => {
|
||||||
|
@ -279,7 +260,7 @@ fn process_window_event(
|
||||||
.and_then(winit_key_code_to_string)
|
.and_then(winit_key_code_to_string)
|
||||||
.filter(|key_text| !key_text.starts_with(char::is_control))
|
.filter(|key_text| !key_text.starts_with(char::is_control))
|
||||||
} else {
|
} else {
|
||||||
Some(ch.to_string().into())
|
Some(SharedString::from(ch))
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = match text {
|
let text = match text {
|
||||||
|
@ -287,13 +268,16 @@ fn process_window_event(
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let modifiers = window.current_keyboard_modifiers().get();
|
runtime_window.process_key_input(KeyInputEvent {
|
||||||
|
event_type: KeyEventType::KeyPressed,
|
||||||
let mut event = key_event(KeyEventType::KeyPressed, text, modifiers);
|
text: text.clone(),
|
||||||
|
..Default::default()
|
||||||
runtime_window.process_key_input(&event);
|
});
|
||||||
event.event_type = KeyEventType::KeyReleased;
|
runtime_window.process_key_input(KeyInputEvent {
|
||||||
runtime_window.process_key_input(&event);
|
event_type: KeyEventType::KeyReleased,
|
||||||
|
text: text.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
WindowEvent::Focused(have_focus) => {
|
WindowEvent::Focused(have_focus) => {
|
||||||
let have_focus = have_focus || window.input_method_focused();
|
let have_focus = have_focus || window.input_method_focused();
|
||||||
|
@ -305,63 +289,51 @@ fn process_window_event(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { ref input, .. } => {
|
WindowEvent::KeyboardInput { ref input, .. } => {
|
||||||
|
// For now: Match Qt's behavior of mapping command to control and control to meta (LWin/RWin).
|
||||||
|
let key_code = input.virtual_keycode.map(|key_code| match key_code {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
winit::event::VirtualKeyCode::LControl => winit::event::VirtualKeyCode::LWin,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
winit::event::VirtualKeyCode::RControl => winit::event::VirtualKeyCode::RWin,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
winit::event::VirtualKeyCode::LWin => winit::event::VirtualKeyCode::LControl,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
winit::event::VirtualKeyCode::RWin => winit::event::VirtualKeyCode::RControl,
|
||||||
|
code @ _ => code,
|
||||||
|
});
|
||||||
window.currently_pressed_key_code().set(match input.state {
|
window.currently_pressed_key_code().set(match input.state {
|
||||||
winit::event::ElementState::Pressed => input.virtual_keycode,
|
winit::event::ElementState::Pressed => key_code,
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
if let Some(text) = input.virtual_keycode.and_then(key_codes::winit_key_to_string) {
|
if let Some(text) = key_code.and_then(key_codes::winit_key_to_string) {
|
||||||
#[allow(unused_mut)]
|
runtime_window.process_key_input(KeyInputEvent {
|
||||||
let mut modifiers = window.current_keyboard_modifiers().get();
|
event_type: match input.state {
|
||||||
// On wasm, the WindowEvent::ModifiersChanged event is not received
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
{
|
|
||||||
modifiers.shift |= input.modifiers.shift();
|
|
||||||
modifiers.control |= input.modifiers.ctrl();
|
|
||||||
modifiers.meta |= input.modifiers.logo();
|
|
||||||
modifiers.alt |= input.modifiers.alt();
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = key_event(
|
|
||||||
match input.state {
|
|
||||||
winit::event::ElementState::Pressed => KeyEventType::KeyPressed,
|
winit::event::ElementState::Pressed => KeyEventType::KeyPressed,
|
||||||
winit::event::ElementState::Released => KeyEventType::KeyReleased,
|
winit::event::ElementState::Released => KeyEventType::KeyReleased,
|
||||||
},
|
},
|
||||||
text,
|
text,
|
||||||
modifiers,
|
..Default::default()
|
||||||
);
|
});
|
||||||
runtime_window.process_key_input(&event);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
WindowEvent::Ime(winit::event::Ime::Preedit(string, preedit_selection)) => {
|
WindowEvent::Ime(winit::event::Ime::Preedit(string, preedit_selection)) => {
|
||||||
let preedit_selection = preedit_selection.unwrap_or((0, 0));
|
let preedit_selection = preedit_selection.unwrap_or((0, 0));
|
||||||
let event = KeyEvent {
|
let event = KeyInputEvent {
|
||||||
event_type: KeyEventType::UpdateComposition,
|
event_type: KeyEventType::UpdateComposition,
|
||||||
text: string.into(),
|
text: string.into(),
|
||||||
preedit_selection_start: preedit_selection.0,
|
preedit_selection_start: preedit_selection.0,
|
||||||
preedit_selection_end: preedit_selection.1,
|
preedit_selection_end: preedit_selection.1,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
runtime_window.process_key_input(&event);
|
runtime_window.process_key_input(event);
|
||||||
}
|
}
|
||||||
WindowEvent::Ime(winit::event::Ime::Commit(string)) => {
|
WindowEvent::Ime(winit::event::Ime::Commit(string)) => {
|
||||||
let event = KeyEvent {
|
let event = KeyInputEvent {
|
||||||
event_type: KeyEventType::CommitComposition,
|
event_type: KeyEventType::CommitComposition,
|
||||||
text: string.into(),
|
text: string.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
runtime_window.process_key_input(&event);
|
runtime_window.process_key_input(event);
|
||||||
}
|
|
||||||
WindowEvent::ModifiersChanged(state) => {
|
|
||||||
// To provide an easier cross-platform behavior, we map the command key to control
|
|
||||||
// on macOS, and control to meta.
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let (control, meta) = (state.logo(), state.ctrl());
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let (control, meta) = (state.ctrl(), state.logo());
|
|
||||||
let modifiers =
|
|
||||||
KeyboardModifiers { shift: state.shift(), alt: state.alt(), control, meta };
|
|
||||||
window.current_keyboard_modifiers().set(modifiers);
|
|
||||||
}
|
}
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
let position = position.to_logical(runtime_window.scale_factor() as f64);
|
let position = position.to_logical(runtime_window.scale_factor() as f64);
|
||||||
|
|
|
@ -14,7 +14,6 @@ use crate::renderer::{WinitCompatibleCanvas, WinitCompatibleRenderer};
|
||||||
use const_field_offset::FieldOffsets;
|
use const_field_offset::FieldOffsets;
|
||||||
use corelib::component::ComponentRc;
|
use corelib::component::ComponentRc;
|
||||||
use corelib::graphics::euclid::num::Zero;
|
use corelib::graphics::euclid::num::Zero;
|
||||||
use corelib::input::KeyboardModifiers;
|
|
||||||
use corelib::items::{ItemRef, MouseCursor};
|
use corelib::items::{ItemRef, MouseCursor};
|
||||||
use corelib::layout::Orientation;
|
use corelib::layout::Orientation;
|
||||||
use corelib::lengths::{LogicalLength, LogicalPoint, LogicalSize};
|
use corelib::lengths::{LogicalLength, LogicalPoint, LogicalSize};
|
||||||
|
@ -83,7 +82,6 @@ pub(crate) struct GLWindow<Renderer: WinitCompatibleRenderer + 'static> {
|
||||||
window: corelib::api::Window,
|
window: corelib::api::Window,
|
||||||
self_weak: Weak<Self>,
|
self_weak: Weak<Self>,
|
||||||
map_state: RefCell<GraphicsWindowBackendState<Renderer>>,
|
map_state: RefCell<GraphicsWindowBackendState<Renderer>>,
|
||||||
keyboard_modifiers: std::cell::Cell<KeyboardModifiers>,
|
|
||||||
currently_pressed_key_code: std::cell::Cell<Option<winit::event::VirtualKeyCode>>,
|
currently_pressed_key_code: std::cell::Cell<Option<winit::event::VirtualKeyCode>>,
|
||||||
pending_redraw: Cell<bool>,
|
pending_redraw: Cell<bool>,
|
||||||
|
|
||||||
|
@ -108,7 +106,6 @@ impl<Renderer: WinitCompatibleRenderer + 'static> GLWindow<Renderer> {
|
||||||
requested_position: None,
|
requested_position: None,
|
||||||
requested_size: None,
|
requested_size: None,
|
||||||
}),
|
}),
|
||||||
keyboard_modifiers: Default::default(),
|
|
||||||
currently_pressed_key_code: Default::default(),
|
currently_pressed_key_code: Default::default(),
|
||||||
pending_redraw: Cell::new(false),
|
pending_redraw: Cell::new(false),
|
||||||
renderer: Renderer::new(
|
renderer: Renderer::new(
|
||||||
|
@ -184,10 +181,6 @@ impl<Renderer: WinitCompatibleRenderer + 'static> WinitWindow for GLWindow<Rende
|
||||||
&self.currently_pressed_key_code
|
&self.currently_pressed_key_code
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_keyboard_modifiers(&self) -> &Cell<KeyboardModifiers> {
|
|
||||||
&self.keyboard_modifiers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw the items of the specified `component` in the given window.
|
/// Draw the items of the specified `component` in the given window.
|
||||||
fn draw(&self) -> bool {
|
fn draw(&self) -> bool {
|
||||||
let window = match self.borrow_mapped_window() {
|
let window = match self.borrow_mapped_window() {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use i_slint_core::input::{KeyEvent, KeyEventType, KeyboardModifiers};
|
use i_slint_core::input::{KeyEventType, KeyInputEvent};
|
||||||
use i_slint_core::window::{WindowAdapter, WindowInner};
|
use i_slint_core::window::{WindowAdapter, WindowInner};
|
||||||
use i_slint_core::SharedString;
|
use i_slint_core::SharedString;
|
||||||
use wasm_bindgen::closure::Closure;
|
use wasm_bindgen::closure::Closure;
|
||||||
|
@ -83,8 +83,7 @@ impl WasmInputHelper {
|
||||||
if let (Some(window_adapter), Some(text)) = (win.upgrade(), event_text(&e)) {
|
if let (Some(window_adapter), Some(text)) = (win.upgrade(), event_text(&e)) {
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
shared_state2.borrow_mut().has_key_down = true;
|
shared_state2.borrow_mut().has_key_down = true;
|
||||||
WindowInner::from_pub(window_adapter.window()).process_key_input(&KeyEvent {
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
modifiers: modifiers(&e),
|
|
||||||
text,
|
text,
|
||||||
event_type: KeyEventType::KeyPressed,
|
event_type: KeyEventType::KeyPressed,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -98,8 +97,7 @@ impl WasmInputHelper {
|
||||||
if let (Some(window_adapter), Some(text)) = (win.upgrade(), event_text(&e)) {
|
if let (Some(window_adapter), Some(text)) = (win.upgrade(), event_text(&e)) {
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
shared_state2.borrow_mut().has_key_down = false;
|
shared_state2.borrow_mut().has_key_down = false;
|
||||||
WindowInner::from_pub(window_adapter.window()).process_key_input(&KeyEvent {
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
modifiers: modifiers(&e),
|
|
||||||
text,
|
text,
|
||||||
event_type: KeyEventType::KeyReleased,
|
event_type: KeyEventType::KeyReleased,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -116,12 +114,12 @@ impl WasmInputHelper {
|
||||||
if !shared_state2.borrow_mut().has_key_down {
|
if !shared_state2.borrow_mut().has_key_down {
|
||||||
let window_inner = WindowInner::from_pub(window_adapter.window());
|
let window_inner = WindowInner::from_pub(window_adapter.window());
|
||||||
let text = SharedString::from(data.as_str());
|
let text = SharedString::from(data.as_str());
|
||||||
window_inner.process_key_input(&KeyEvent {
|
window_inner.process_key_input(KeyInputEvent {
|
||||||
text: text.clone(),
|
text: text.clone(),
|
||||||
event_type: KeyEventType::KeyPressed,
|
event_type: KeyEventType::KeyPressed,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
window_inner.process_key_input(&KeyEvent {
|
window_inner.process_key_input(KeyInputEvent {
|
||||||
text,
|
text,
|
||||||
event_type: KeyEventType::KeyReleased,
|
event_type: KeyEventType::KeyReleased,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -138,7 +136,7 @@ impl WasmInputHelper {
|
||||||
h.add_event_listener("compositionend", move |e: web_sys::CompositionEvent| {
|
h.add_event_listener("compositionend", move |e: web_sys::CompositionEvent| {
|
||||||
if let (Some(window_adapter), Some(data)) = (win.upgrade(), e.data()) {
|
if let (Some(window_adapter), Some(data)) = (win.upgrade(), e.data()) {
|
||||||
let window_inner = WindowInner::from_pub(window_adapter.window());
|
let window_inner = WindowInner::from_pub(window_adapter.window());
|
||||||
window_inner.process_key_input(&KeyEvent {
|
window_inner.process_key_input(KeyInputEvent {
|
||||||
text: data.into(),
|
text: data.into(),
|
||||||
event_type: KeyEventType::CommitComposition,
|
event_type: KeyEventType::CommitComposition,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -153,7 +151,7 @@ impl WasmInputHelper {
|
||||||
let window_inner = WindowInner::from_pub(window_adapter.window());
|
let window_inner = WindowInner::from_pub(window_adapter.window());
|
||||||
let text: SharedString = data.into();
|
let text: SharedString = data.into();
|
||||||
let preedit_cursor_pos = text.len();
|
let preedit_cursor_pos = text.len();
|
||||||
window_inner.process_key_input(&KeyEvent {
|
window_inner.process_key_input(KeyInputEvent {
|
||||||
text,
|
text,
|
||||||
event_type: KeyEventType::UpdateComposition,
|
event_type: KeyEventType::UpdateComposition,
|
||||||
preedit_selection_start: preedit_cursor_pos,
|
preedit_selection_start: preedit_cursor_pos,
|
||||||
|
@ -214,10 +212,7 @@ fn event_text(e: &web_sys::KeyboardEvent) -> Option<SharedString> {
|
||||||
|
|
||||||
let key = e.key();
|
let key = e.key();
|
||||||
|
|
||||||
let convert = |char: char| {
|
let convert = |char: char| Some(char.into());
|
||||||
let mut buffer = [0; 6];
|
|
||||||
Some(SharedString::from(char.encode_utf8(&mut buffer) as &str))
|
|
||||||
};
|
|
||||||
|
|
||||||
macro_rules! check_non_printable_code {
|
macro_rules! check_non_printable_code {
|
||||||
($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident)|* ;)*) => {
|
($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident)|* ;)*) => {
|
||||||
|
@ -243,12 +238,3 @@ fn event_text(e: &web_sys::KeyboardEvent) -> Option<SharedString> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modifiers(e: &web_sys::KeyboardEvent) -> KeyboardModifiers {
|
|
||||||
KeyboardModifiers {
|
|
||||||
alt: e.alt_key(),
|
|
||||||
control: e.ctrl_key(),
|
|
||||||
meta: e.meta_key(),
|
|
||||||
shift: e.shift_key(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,6 +18,20 @@ macro_rules! for_each_special_keys {
|
||||||
'\u{0019}' # Backtab # Qt_Key_Key_Backtab # ;
|
'\u{0019}' # Backtab # Qt_Key_Key_Backtab # ;
|
||||||
'\u{007f}' # Delete # Qt_Key_Key_Delete # Delete ;
|
'\u{007f}' # Delete # Qt_Key_Key_Delete # Delete ;
|
||||||
|
|
||||||
|
// The modifier key codes comes from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode.
|
||||||
|
'\u{0010}' # Shift # Qt_Key_Key_Shift # LShift ;
|
||||||
|
'\u{0011}' # Control # Qt_Key_Key_Meta # LControl ;
|
||||||
|
'\u{0012}' # Alt # Qt_Key_Key_Alt # LAlt ;
|
||||||
|
'\u{0013}' # AltGr # Qt_Key_Key_AltGr # RAlt ;
|
||||||
|
'\u{0014}' # CapsLock # Qt_Key_Key_CapsLock # ;
|
||||||
|
|
||||||
|
'\u{0015}' # ShiftR # # RShift ;
|
||||||
|
'\u{0016}' # ControlR # # RControl ;
|
||||||
|
|
||||||
|
// meta defines the macos command key (check DOM_VK_META on https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)
|
||||||
|
'\u{00E0}' # Meta # Qt_Key_Key_Control # LWin ;
|
||||||
|
'\u{00E1}' # MetaR # # RWin ;
|
||||||
|
|
||||||
'\u{F700}' # UpArrow # Qt_Key_Key_Up # Up ;
|
'\u{F700}' # UpArrow # Qt_Key_Key_Up # Up ;
|
||||||
'\u{F701}' # DownArrow # Qt_Key_Key_Down # Down ;
|
'\u{F701}' # DownArrow # Qt_Key_Key_Down # Down ;
|
||||||
'\u{F702}' # LeftArrow # Qt_Key_Key_Left # Left ;
|
'\u{F702}' # LeftArrow # Qt_Key_Key_Left # Left ;
|
||||||
|
|
|
@ -89,6 +89,7 @@ pub enum BuiltinNamespace {
|
||||||
Colors,
|
Colors,
|
||||||
Math,
|
Math,
|
||||||
Keys,
|
Keys,
|
||||||
|
Key,
|
||||||
SlintInternal,
|
SlintInternal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +103,7 @@ impl LookupResult {
|
||||||
pub fn deprecated(&self) -> Option<&str> {
|
pub fn deprecated(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Expression { deprecated: Some(x), .. } => Some(x.as_str()),
|
Self::Expression { deprecated: Some(x), .. } => Some(x.as_str()),
|
||||||
|
Self::Namespace(BuiltinNamespace::Keys) => Some("Key"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +154,7 @@ impl LookupObject for LookupResult {
|
||||||
}
|
}
|
||||||
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
|
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.for_each_entry(ctx, f),
|
||||||
LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.for_each_entry(ctx, f),
|
LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.for_each_entry(ctx, f),
|
||||||
|
LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.for_each_entry(ctx, f),
|
||||||
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
|
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
|
||||||
SlintInternal.for_each_entry(ctx, f)
|
SlintInternal.for_each_entry(ctx, f)
|
||||||
}
|
}
|
||||||
|
@ -167,6 +170,7 @@ impl LookupObject for LookupResult {
|
||||||
}
|
}
|
||||||
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
|
LookupResult::Namespace(BuiltinNamespace::Math) => MathFunctions.lookup(ctx, name),
|
||||||
LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.lookup(ctx, name),
|
LookupResult::Namespace(BuiltinNamespace::Keys) => KeysLookup.lookup(ctx, name),
|
||||||
|
LookupResult::Namespace(BuiltinNamespace::Key) => KeysLookup.lookup(ctx, name),
|
||||||
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
|
LookupResult::Namespace(BuiltinNamespace::SlintInternal) => {
|
||||||
SlintInternal.lookup(ctx, name)
|
SlintInternal.lookup(ctx, name)
|
||||||
}
|
}
|
||||||
|
@ -701,6 +705,7 @@ impl LookupObject for BuiltinNamespaceLookup {
|
||||||
None.or_else(|| f("Colors", LookupResult::Namespace(BuiltinNamespace::Colors)))
|
None.or_else(|| f("Colors", LookupResult::Namespace(BuiltinNamespace::Colors)))
|
||||||
.or_else(|| f("Math", LookupResult::Namespace(BuiltinNamespace::Math)))
|
.or_else(|| f("Math", LookupResult::Namespace(BuiltinNamespace::Math)))
|
||||||
.or_else(|| f("Keys", LookupResult::Namespace(BuiltinNamespace::Keys)))
|
.or_else(|| f("Keys", LookupResult::Namespace(BuiltinNamespace::Keys)))
|
||||||
|
.or_else(|| f("Key", LookupResult::Namespace(BuiltinNamespace::Key)))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
f("SlintInternal", LookupResult::Namespace(BuiltinNamespace::SlintInternal))
|
f("SlintInternal", LookupResult::Namespace(BuiltinNamespace::SlintInternal))
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,4 +23,15 @@ Xxx := Rectangle {
|
||||||
color = #000000;
|
color = #000000;
|
||||||
// ^warning{The property 'color' has been deprecated. Please use 'background' instead}
|
// ^warning{The property 'color' has been deprecated. Please use 'background' instead}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Foo := FocusScope {
|
||||||
|
key-pressed(event) => {
|
||||||
|
if (event.text == Keys.Escape) {
|
||||||
|
// ^warning{The property 'Keys' has been deprecated. Please use 'Key' instead}
|
||||||
|
debug("Esc key was pressed")
|
||||||
|
}
|
||||||
|
accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,10 +202,10 @@ export SpinBox := FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (enabled && event.text == Keys.UpArrow && value < maximum) {
|
if (enabled && event.text == Key.UpArrow && value < maximum) {
|
||||||
value += 1;
|
value += 1;
|
||||||
accept
|
accept
|
||||||
} else if (enabled && event.text == Keys.DownArrow && value > minimum) {
|
} else if (enabled && event.text == Key.DownArrow && value > minimum) {
|
||||||
value -= 1;
|
value -= 1;
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,10 +291,10 @@ export Slider := Rectangle {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (enabled && event.text == Keys.RightArrow) {
|
if (enabled && event.text == Key.RightArrow) {
|
||||||
value = Math.min(value + 1, maximum);
|
value = Math.min(value + 1, maximum);
|
||||||
accept
|
accept
|
||||||
} else if (enabled && event.text == Keys.LeftArrow) {
|
} else if (enabled && event.text == Key.LeftArrow) {
|
||||||
value = Math.max(value - 1, minimum);
|
value = Math.max(value - 1, minimum);
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
@ -431,11 +431,11 @@ export TabBarImpl := Rectangle {
|
||||||
current = current-focused;
|
current = current-focused;
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.LeftArrow) {
|
if (event.text == Key.LeftArrow) {
|
||||||
focused-tab = Math.max(focused-tab - 1, 0);
|
focused-tab = Math.max(focused-tab - 1, 0);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.RightArrow) {
|
if (event.text == Key.RightArrow) {
|
||||||
focused-tab = Math.min(focused-tab + 1, num-tabs - 1);
|
focused-tab = Math.min(focused-tab + 1, num-tabs - 1);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
@ -516,10 +516,10 @@ export StandardListView := ListView {
|
||||||
}
|
}
|
||||||
FocusScope {
|
FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow && current-item > 0) {
|
if (event.text == Key.UpArrow && current-item > 0) {
|
||||||
current-item -= 1;
|
current-item -= 1;
|
||||||
return accept;
|
return accept;
|
||||||
} else if (event.text == Keys.DownArrow && current-item + 1 < model.length) {
|
} else if (event.text == Key.DownArrow && current-item + 1 < model.length) {
|
||||||
current-item += 1;
|
current-item += 1;
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
@ -539,16 +539,16 @@ export ComboBox := FocusScope {
|
||||||
accessible-value <=> current-value;
|
accessible-value <=> current-value;
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow) {
|
if (event.text == Key.UpArrow) {
|
||||||
current-index = Math.max(current-index - 1, 0);
|
current-index = Math.max(current-index - 1, 0);
|
||||||
current-value = model[current-index];
|
current-value = model[current-index];
|
||||||
return accept;
|
return accept;
|
||||||
} else if (event.text == Keys.DownArrow) {
|
} else if (event.text == Key.DownArrow) {
|
||||||
current-index = Math.min(current-index + 1, model.length - 1);
|
current-index = Math.min(current-index + 1, model.length - 1);
|
||||||
current-value = model[current-index];
|
current-value = model[current-index];
|
||||||
return accept;
|
return accept;
|
||||||
// PopupWindow can not get hidden again at this time, so do not allow to pop that up.
|
// PopupWindow can not get hidden again at this time, so do not allow to pop that up.
|
||||||
// } else if (event.text == Keys.Return) {
|
// } else if (event.text == Key.Return) {
|
||||||
// touch.clicked()
|
// touch.clicked()
|
||||||
// return accept;
|
// return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,11 +88,11 @@ export ComboBox := FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow) {
|
if (event.text == Key.UpArrow) {
|
||||||
current-index = Math.max(current-index - 1, 0);
|
current-index = Math.max(current-index - 1, 0);
|
||||||
current-value = model[current-index];
|
current-value = model[current-index];
|
||||||
return accept;
|
return accept;
|
||||||
} else if (event.text == Keys.DownArrow) {
|
} else if (event.text == Key.DownArrow) {
|
||||||
current-index = Math.min(current-index + 1, model.length - 1);
|
current-index = Math.min(current-index + 1, model.length - 1);
|
||||||
current-value = model[current-index];
|
current-value = model[current-index];
|
||||||
return accept;
|
return accept;
|
||||||
|
|
|
@ -23,10 +23,10 @@ export StandardListView := ListView {
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow && current-item > 0) {
|
if (event.text == Key.UpArrow && current-item > 0) {
|
||||||
current-item -= 1;
|
current-item -= 1;
|
||||||
return accept;
|
return accept;
|
||||||
} else if (event.text == Keys.DownArrow && current-item + 1 < model.length) {
|
} else if (event.text == Key.DownArrow && current-item + 1 < model.length) {
|
||||||
current-item += 1;
|
current-item += 1;
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,10 +86,10 @@ export Slider := Rectangle {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (enabled && event.text == Keys.RightArrow) {
|
if (enabled && event.text == Key.RightArrow) {
|
||||||
value = Math.min(value + 1, maximum);
|
value = Math.min(value + 1, maximum);
|
||||||
accept
|
accept
|
||||||
} else if (enabled && event.text == Keys.LeftArrow) {
|
} else if (enabled && event.text == Key.LeftArrow) {
|
||||||
value = Math.max(value - 1, minimum);
|
value = Math.max(value - 1, minimum);
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -149,10 +149,10 @@ export SpinBox := FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (enabled && event.text == Keys.UpArrow && value < maximum) {
|
if (enabled && event.text == Key.UpArrow && value < maximum) {
|
||||||
value += 1;
|
value += 1;
|
||||||
accept
|
accept
|
||||||
} else if (enabled && event.text == Keys.DownArrow && value > minimum) {
|
} else if (enabled && event.text == Key.DownArrow && value > minimum) {
|
||||||
value -= 1;
|
value -= 1;
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -112,11 +112,11 @@ export TabBarImpl := Rectangle {
|
||||||
current = current-focused;
|
current = current-focused;
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.LeftArrow) {
|
if (event.text == Key.LeftArrow) {
|
||||||
focused-tab = Math.max(focused-tab - 1, 0);
|
focused-tab = Math.max(focused-tab - 1, 0);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.RightArrow) {
|
if (event.text == Key.RightArrow) {
|
||||||
focused-tab = Math.min(focused-tab + 1, num-tabs - 1);
|
focused-tab = Math.min(focused-tab + 1, num-tabs - 1);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,10 @@ export Slider := NativeSlider {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
|
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (root.enabled && event.text == Keys.RightArrow) {
|
if (root.enabled && event.text == Key.RightArrow) {
|
||||||
root.value = Math.min(root.value + 1, root.maximum);
|
root.value = Math.min(root.value + 1, root.maximum);
|
||||||
accept
|
accept
|
||||||
} else if (root.enabled && event.text == Keys.LeftArrow) {
|
} else if (root.enabled && event.text == Key.LeftArrow) {
|
||||||
root.value = Math.max(root.value - 1, root.minimum);
|
root.value = Math.max(root.value - 1, root.minimum);
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,10 +127,10 @@ export StandardListView := ListView {
|
||||||
}
|
}
|
||||||
FocusScope {
|
FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow && current-item > 0) {
|
if (event.text == Key.UpArrow && current-item > 0) {
|
||||||
current-item -= 1;
|
current-item -= 1;
|
||||||
accept
|
accept
|
||||||
} else if (event.text == Keys.DownArrow && current-item + 1 < model.length) {
|
} else if (event.text == Key.DownArrow && current-item + 1 < model.length) {
|
||||||
current-item += 1;
|
current-item += 1;
|
||||||
accept
|
accept
|
||||||
} else {
|
} else {
|
||||||
|
@ -180,16 +180,16 @@ export ComboBox := NativeComboBox {
|
||||||
|
|
||||||
fs := FocusScope {
|
fs := FocusScope {
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.UpArrow) {
|
if (event.text == Key.UpArrow) {
|
||||||
root.current-index = Math.max(root.current-index - 1, 0);
|
root.current-index = Math.max(root.current-index - 1, 0);
|
||||||
root.current-value = model[root.current-index];
|
root.current-value = model[root.current-index];
|
||||||
return accept;
|
return accept;
|
||||||
} else if (event.text == Keys.DownArrow) {
|
} else if (event.text == Key.DownArrow) {
|
||||||
root.current-index = Math.min(root.current-index + 1, root.model.length - 1);
|
root.current-index = Math.min(root.current-index + 1, root.model.length - 1);
|
||||||
root.current-value = model[root.current-index];
|
root.current-value = model[root.current-index];
|
||||||
return accept;
|
return accept;
|
||||||
// PopupWindow can not get hidden again at this time, so do not allow to pop that up.
|
// PopupWindow can not get hidden again at this time, so do not allow to pop that up.
|
||||||
// } else if (event.text == Keys.Return) {
|
// } else if (event.text == Key.Return) {
|
||||||
// touch.clicked()
|
// touch.clicked()
|
||||||
// return accept;
|
// return accept;
|
||||||
}
|
}
|
||||||
|
@ -223,11 +223,11 @@ export TabBarImpl := Rectangle {
|
||||||
fs := FocusScope {
|
fs := FocusScope {
|
||||||
width: 0px; // Do not react on clicks
|
width: 0px; // Do not react on clicks
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.LeftArrow) {
|
if (event.text == Key.LeftArrow) {
|
||||||
root.current = Math.max(root.current - 1, 0);
|
root.current = Math.max(root.current - 1, 0);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
if (event.text == Keys.RightArrow) {
|
if (event.text == Key.RightArrow) {
|
||||||
root.current = Math.min(root.current + 1, num-tabs - 1);
|
root.current = Math.min(root.current + 1, num-tabs - 1);
|
||||||
return accept;
|
return accept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,12 @@ This module contains types that are public and re-exported in the slint-rs as we
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
use crate::component::ComponentVTable;
|
use crate::component::ComponentVTable;
|
||||||
|
use crate::input::{KeyEventType, KeyInputEvent, MouseEvent};
|
||||||
use crate::window::{WindowAdapter, WindowInner};
|
use crate::window::{WindowAdapter, WindowInner};
|
||||||
|
|
||||||
|
// reexport key enum to the public api
|
||||||
|
pub use crate::input::key_codes::Key;
|
||||||
|
|
||||||
/// A position represented in the coordinate space of logical pixels. That is the space before applying
|
/// A position represented in the coordinate space of logical pixels. That is the space before applying
|
||||||
/// a display device specific scale factor.
|
/// a display device specific scale factor.
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
||||||
|
@ -421,7 +425,44 @@ impl Window {
|
||||||
/// Any position fields in the event must be in the logical pixel coordinate system relative to
|
/// Any position fields in the event must be in the logical pixel coordinate system relative to
|
||||||
/// the top left corner of the window.
|
/// the top left corner of the window.
|
||||||
pub fn dispatch_event(&self, event: WindowEvent) {
|
pub fn dispatch_event(&self, event: WindowEvent) {
|
||||||
self.0.process_mouse_input(event.into())
|
match event {
|
||||||
|
WindowEvent::PointerPressed { position, button } => {
|
||||||
|
self.0.process_mouse_input(MouseEvent::Pressed {
|
||||||
|
position: position.to_euclid().cast(),
|
||||||
|
button,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowEvent::PointerReleased { position, button } => {
|
||||||
|
self.0.process_mouse_input(MouseEvent::Released {
|
||||||
|
position: position.to_euclid().cast(),
|
||||||
|
button,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowEvent::PointerMoved { position } => {
|
||||||
|
self.0.process_mouse_input(MouseEvent::Moved {
|
||||||
|
position: position.to_euclid().cast(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowEvent::PointerScrolled { position, delta_x, delta_y } => {
|
||||||
|
self.0.process_mouse_input(MouseEvent::Wheel {
|
||||||
|
position: position.to_euclid().cast(),
|
||||||
|
delta_x,
|
||||||
|
delta_y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowEvent::PointerExited => self.0.process_mouse_input(MouseEvent::Exit),
|
||||||
|
|
||||||
|
WindowEvent::KeyPressed { text } => self.0.process_key_input(KeyInputEvent {
|
||||||
|
text: SharedString::from(text),
|
||||||
|
event_type: KeyEventType::KeyPressed,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
WindowEvent::KeyReleased { text } => self.0.process_key_input(KeyInputEvent {
|
||||||
|
text: SharedString::from(text),
|
||||||
|
event_type: KeyEventType::KeyReleased,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if there is an animation currently active on any property in the Window; false otherwise.
|
/// Returns true if there is an animation currently active on any property in the Window; false otherwise.
|
||||||
|
@ -438,6 +479,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::input::PointerEventButton;
|
pub use crate::input::PointerEventButton;
|
||||||
|
pub use crate::SharedString;
|
||||||
|
|
||||||
/// A event that describes user input.
|
/// A event that describes user input.
|
||||||
///
|
///
|
||||||
|
@ -449,7 +491,7 @@ pub use crate::input::PointerEventButton;
|
||||||
///
|
///
|
||||||
/// All position fields are in logical window coordinates.
|
/// All position fields are in logical window coordinates.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum WindowEvent {
|
pub enum WindowEvent {
|
||||||
/// A pointer was pressed.
|
/// A pointer was pressed.
|
||||||
|
@ -476,6 +518,30 @@ pub enum WindowEvent {
|
||||||
},
|
},
|
||||||
/// The pointer exited the window.
|
/// The pointer exited the window.
|
||||||
PointerExited,
|
PointerExited,
|
||||||
|
/// A key was pressed.
|
||||||
|
KeyPressed {
|
||||||
|
// FIXME: use SharedString instead of char (breaking change)
|
||||||
|
/// The unicode representation of the key pressed.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// A specific key can be mapped to a unicode by using the `Key` enum
|
||||||
|
/// ```rust
|
||||||
|
/// let _ = slint::WindowEvent::KeyPressed { text: slint::Key::Shift.into() };
|
||||||
|
/// ```
|
||||||
|
text: char,
|
||||||
|
},
|
||||||
|
/// A key was pressed.
|
||||||
|
KeyReleased {
|
||||||
|
// FIXME: use SharedString instead of char (breaking change)
|
||||||
|
/// The unicode representation of the key released.
|
||||||
|
/// ///
|
||||||
|
/// # Example
|
||||||
|
/// A specific key can be mapped to a unicode by using the `Key` enum
|
||||||
|
/// ```rust
|
||||||
|
/// let _ = slint::WindowEvent::KeyReleased { text: slint::Key::Shift.into() };
|
||||||
|
/// ```
|
||||||
|
text: char,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowEvent {
|
impl WindowEvent {
|
||||||
|
@ -486,7 +552,7 @@ impl WindowEvent {
|
||||||
WindowEvent::PointerReleased { position, .. } => Some(*position),
|
WindowEvent::PointerReleased { position, .. } => Some(*position),
|
||||||
WindowEvent::PointerMoved { position } => Some(*position),
|
WindowEvent::PointerMoved { position } => Some(*position),
|
||||||
WindowEvent::PointerScrolled { position, .. } => Some(*position),
|
WindowEvent::PointerScrolled { position, .. } => Some(*position),
|
||||||
WindowEvent::PointerExited => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,26 +68,6 @@ impl MouseEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<crate::api::WindowEvent> for MouseEvent {
|
|
||||||
fn from(event: crate::api::WindowEvent) -> Self {
|
|
||||||
match event {
|
|
||||||
crate::api::WindowEvent::PointerPressed { position, button } => {
|
|
||||||
MouseEvent::Pressed { position: position.to_euclid().cast(), button }
|
|
||||||
}
|
|
||||||
crate::api::WindowEvent::PointerReleased { position, button } => {
|
|
||||||
MouseEvent::Released { position: position.to_euclid().cast(), button }
|
|
||||||
}
|
|
||||||
crate::api::WindowEvent::PointerMoved { position } => {
|
|
||||||
MouseEvent::Moved { position: position.to_euclid().cast() }
|
|
||||||
}
|
|
||||||
crate::api::WindowEvent::PointerScrolled { position, delta_x, delta_y } => {
|
|
||||||
MouseEvent::Wheel { position: position.to_euclid().cast(), delta_x, delta_y }
|
|
||||||
}
|
|
||||||
crate::api::WindowEvent::PointerExited => MouseEvent::Exit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This value is returned by the `input_event` function of an Item
|
/// This value is returned by the `input_event` function of an Item
|
||||||
/// to notify the run-time about how the event was handled and
|
/// to notify the run-time about how the event was handled and
|
||||||
/// what the next steps are.
|
/// what the next steps are.
|
||||||
|
@ -145,18 +125,87 @@ impl Default for InputEventFilterResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This module contains the constant character code used to represent the keys
|
/// This module contains the constant character code used to represent the keys.
|
||||||
#[allow(missing_docs, non_upper_case_globals)]
|
#[allow(missing_docs, non_upper_case_globals)]
|
||||||
pub mod key_codes {
|
pub mod key_codes {
|
||||||
macro_rules! declare_consts_for_special_keys {
|
macro_rules! declare_consts_for_special_keys {
|
||||||
($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident)|* ;)*) => {
|
($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident)|* ;)*) => {
|
||||||
$(pub const $name : char = $char;)*
|
$(pub const $name : char = $char;)*
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[repr(C)]
|
||||||
|
/// The `Key` enum is used to map a specific key by name e.g. `Key::Control` to an
|
||||||
|
/// internal used unicode representation. The enum is convertible to [`std::char`] and [`slint::SharedString`](`crate::SharedString`).
|
||||||
|
/// Use this with [`slint::WindowEvent`](`crate::api::WindowEvent`) to supply key events to Slint's platform abstraction.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let slint_key_code: char = slint::Key::Tab.into();
|
||||||
|
/// assert_eq!(slint_key_code, '\t')
|
||||||
|
/// ```
|
||||||
|
pub enum Key {
|
||||||
|
$($name,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for char {
|
||||||
|
fn from(k: Key) -> Self {
|
||||||
|
match k {
|
||||||
|
$(Key::$name => $name,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for crate::SharedString {
|
||||||
|
fn from(k: Key) -> Self {
|
||||||
|
char::from(k).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
i_slint_common::for_each_special_keys!(declare_consts_for_special_keys);
|
i_slint_common::for_each_special_keys!(declare_consts_for_special_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal struct to maintain the pressed/released state of the keys that
|
||||||
|
/// map to keyboard modifiers.
|
||||||
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub(crate) struct InternalKeyboardModifierState {
|
||||||
|
left_alt: bool,
|
||||||
|
right_alt: bool,
|
||||||
|
left_control: bool,
|
||||||
|
right_control: bool,
|
||||||
|
left_meta: bool,
|
||||||
|
right_meta: bool,
|
||||||
|
left_shift: bool,
|
||||||
|
right_shift: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternalKeyboardModifierState {
|
||||||
|
/// Updates a flag of the modifiers if the key of the given text is pressed.
|
||||||
|
/// Returns an updated modifier if detected; None otherwise;
|
||||||
|
pub(crate) fn state_update(mut self, pressed: bool, text: &SharedString) -> Option<Self> {
|
||||||
|
if let Some(key_code) = text.chars().next() {
|
||||||
|
match key_code {
|
||||||
|
key_codes::Alt => self.left_alt = pressed,
|
||||||
|
key_codes::Control => self.left_control = pressed,
|
||||||
|
key_codes::ControlR => self.right_control = pressed,
|
||||||
|
key_codes::Shift => self.left_shift = pressed,
|
||||||
|
key_codes::ShiftR => self.right_shift = pressed,
|
||||||
|
key_codes::Meta => self.left_meta = pressed,
|
||||||
|
key_codes::MetaR => self.right_meta = pressed,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
// Encoded keyboard modifiers must appear as individual key events. This could
|
||||||
|
// be relaxed by implementing a string split, but right now WindowEvent::KeyPressed
|
||||||
|
// holds only a single char.
|
||||||
|
debug_assert_eq!(key_code.len_utf8(), text.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// KeyboardModifier provides booleans to indicate possible modifier keys
|
/// KeyboardModifier provides booleans to indicate possible modifier keys
|
||||||
/// on a keyboard, such as Shift, Control, etc.
|
/// on a keyboard, such as Shift, Control, etc.
|
||||||
///
|
///
|
||||||
|
@ -170,12 +219,23 @@ pub struct KeyboardModifiers {
|
||||||
pub alt: bool,
|
pub alt: bool,
|
||||||
/// Indicates the control key on a keyboard.
|
/// Indicates the control key on a keyboard.
|
||||||
pub control: bool,
|
pub control: bool,
|
||||||
/// Indicates the logo key on macOS and the windows key on Windows.
|
/// Indicates the command key on macos.
|
||||||
pub meta: bool,
|
pub meta: bool,
|
||||||
/// Indicates the shift key on a keyboard.
|
/// Indicates the shift key on a keyboard.
|
||||||
pub shift: bool,
|
pub shift: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InternalKeyboardModifierState> for KeyboardModifiers {
|
||||||
|
fn from(internal_state: InternalKeyboardModifierState) -> Self {
|
||||||
|
Self {
|
||||||
|
alt: internal_state.left_alt | internal_state.right_alt,
|
||||||
|
control: internal_state.left_control | internal_state.right_control,
|
||||||
|
meta: internal_state.left_meta | internal_state.right_meta,
|
||||||
|
shift: internal_state.left_shift | internal_state.right_shift,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This enum defines the different kinds of key events that can happen.
|
/// This enum defines the different kinds of key events that can happen.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -200,9 +260,29 @@ impl Default for KeyEventType {
|
||||||
/// Represents a key event sent by the windowing system.
|
/// Represents a key event sent by the windowing system.
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
pub struct KeyInputEvent {
|
||||||
|
/// The unicode representation of the key pressed.
|
||||||
|
pub text: SharedString,
|
||||||
|
|
||||||
|
// note: this field is not exported in the .slint in the KeyEvent builtin struct
|
||||||
|
/// Indicates whether the key was pressed or released
|
||||||
|
pub event_type: KeyEventType,
|
||||||
|
|
||||||
|
/// If the event type is KeyEventType::UpdateComposition, then this field specifies
|
||||||
|
/// the start of the selection as byte offsets within the preedit text.
|
||||||
|
pub preedit_selection_start: usize,
|
||||||
|
/// If the event type is KeyEventType::UpdateComposition, then this field specifies
|
||||||
|
/// the end of the selection as byte offsets within the preedit text.
|
||||||
|
pub preedit_selection_end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a key event.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct KeyEvent {
|
pub struct KeyEvent {
|
||||||
/// The keyboard modifiers active at the time of the key press event.
|
/// The keyboard modifiers active at the time of the key press event.
|
||||||
pub modifiers: KeyboardModifiers,
|
pub modifiers: KeyboardModifiers,
|
||||||
|
|
||||||
/// The unicode representation of the key pressed.
|
/// The unicode representation of the key pressed.
|
||||||
pub text: SharedString,
|
pub text: SharedString,
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use crate::input::{KeyEvent, KeyEventType, KeyboardModifiers, MouseEvent};
|
use crate::input::{key_codes::Key, KeyEventType, KeyInputEvent, MouseEvent};
|
||||||
use crate::window::WindowInner;
|
use crate::window::WindowInner;
|
||||||
use crate::Coord;
|
use crate::Coord;
|
||||||
use crate::SharedString;
|
use crate::SharedString;
|
||||||
|
@ -57,33 +57,54 @@ pub extern "C" fn slint_send_mouse_click(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simulate a character input event (pressed or released).
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn slint_send_keyboard_char(
|
||||||
|
string: &crate::SharedString,
|
||||||
|
pressed: bool,
|
||||||
|
window_adapter: &crate::window::WindowAdapterRc,
|
||||||
|
) {
|
||||||
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
|
event_type: if pressed { KeyEventType::KeyPressed } else { KeyEventType::KeyReleased },
|
||||||
|
text: string.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// 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: KeyboardModifiers,
|
|
||||||
window_adapter: &crate::window::WindowAdapterRc,
|
window_adapter: &crate::window::WindowAdapterRc,
|
||||||
) {
|
) {
|
||||||
for ch in sequence.chars() {
|
for ch in sequence.chars() {
|
||||||
let mut modifiers = modifiers;
|
|
||||||
if ch.is_ascii_uppercase() {
|
if ch.is_ascii_uppercase() {
|
||||||
modifiers.shift = true;
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
|
event_type: KeyEventType::KeyPressed,
|
||||||
|
text: Key::Shift.into(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let mut buffer = [0; 6];
|
let text = SharedString::from(ch);
|
||||||
let text = SharedString::from(ch.encode_utf8(&mut buffer) as &str);
|
|
||||||
|
|
||||||
WindowInner::from_pub(window_adapter.window()).process_key_input(&KeyEvent {
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
event_type: KeyEventType::KeyPressed,
|
event_type: KeyEventType::KeyPressed,
|
||||||
text: text.clone(),
|
text: text.clone(),
|
||||||
modifiers,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
WindowInner::from_pub(window_adapter.window()).process_key_input(&KeyEvent {
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
event_type: KeyEventType::KeyReleased,
|
event_type: KeyEventType::KeyReleased,
|
||||||
text,
|
text,
|
||||||
modifiers,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ch.is_ascii_uppercase() {
|
||||||
|
WindowInner::from_pub(window_adapter.window()).process_key_input(KeyInputEvent {
|
||||||
|
event_type: KeyEventType::KeyReleased,
|
||||||
|
text: Key::Shift.into(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ use crate::api::{
|
||||||
use crate::component::{ComponentRc, ComponentRef, ComponentVTable, ComponentWeak};
|
use crate::component::{ComponentRc, ComponentRef, ComponentVTable, ComponentWeak};
|
||||||
use crate::graphics::Point;
|
use crate::graphics::Point;
|
||||||
use crate::input::{
|
use crate::input::{
|
||||||
key_codes, KeyEvent, KeyEventType, MouseEvent, MouseInputState, TextCursorBlinker,
|
key_codes, InternalKeyboardModifierState, KeyEvent, KeyEventType, KeyInputEvent,
|
||||||
|
KeyboardModifiers, MouseEvent, MouseInputState, TextCursorBlinker,
|
||||||
};
|
};
|
||||||
use crate::item_tree::ItemRc;
|
use crate::item_tree::ItemRc;
|
||||||
use crate::items::{ItemRef, MouseCursor};
|
use crate::items::{ItemRef, MouseCursor};
|
||||||
|
@ -35,6 +36,17 @@ fn previous_focus_item(item: ItemRc) -> ItemRc {
|
||||||
item.previous_focus_item()
|
item.previous_focus_item()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transforms a `KeyInputEvent` into an `KeyEvent` with the given `KeyboardModifiers`.
|
||||||
|
fn input_as_key_event(input: KeyInputEvent, modifiers: KeyboardModifiers) -> KeyEvent {
|
||||||
|
KeyEvent {
|
||||||
|
modifiers,
|
||||||
|
text: input.text,
|
||||||
|
event_type: input.event_type,
|
||||||
|
preedit_selection_start: input.preedit_selection_start,
|
||||||
|
preedit_selection_end: input.preedit_selection_end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This trait represents the adaptation layer between the [`Window`] API, and the
|
/// This trait represents the adaptation layer between the [`Window`] API, and the
|
||||||
/// internal type from the backend that provides functionality such as device-independent pixels,
|
/// internal type from the backend that provides functionality such as device-independent pixels,
|
||||||
/// window resizing, and other typically windowing system related tasks.
|
/// window resizing, and other typically windowing system related tasks.
|
||||||
|
@ -202,6 +214,7 @@ pub struct WindowInner {
|
||||||
window_adapter_weak: Weak<dyn WindowAdapter>,
|
window_adapter_weak: Weak<dyn WindowAdapter>,
|
||||||
component: RefCell<ComponentWeak>,
|
component: RefCell<ComponentWeak>,
|
||||||
mouse_input_state: Cell<MouseInputState>,
|
mouse_input_state: Cell<MouseInputState>,
|
||||||
|
modifiers: Cell<InternalKeyboardModifierState>,
|
||||||
redraw_tracker: Pin<Box<PropertyTracker<WindowRedrawTracker>>>,
|
redraw_tracker: Pin<Box<PropertyTracker<WindowRedrawTracker>>>,
|
||||||
/// Gets dirty when the layout restrictions, or some other property of the windows change
|
/// Gets dirty when the layout restrictions, or some other property of the windows change
|
||||||
window_properties_tracker: Pin<Box<PropertyTracker<WindowPropertiesTracker>>>,
|
window_properties_tracker: Pin<Box<PropertyTracker<WindowPropertiesTracker>>>,
|
||||||
|
@ -251,6 +264,7 @@ impl WindowInner {
|
||||||
window_adapter_weak,
|
window_adapter_weak,
|
||||||
component: Default::default(),
|
component: Default::default(),
|
||||||
mouse_input_state: Default::default(),
|
mouse_input_state: Default::default(),
|
||||||
|
modifiers: Default::default(),
|
||||||
redraw_tracker: Box::pin(redraw_tracker),
|
redraw_tracker: Box::pin(redraw_tracker),
|
||||||
window_properties_tracker: Box::pin(window_properties_tracker),
|
window_properties_tracker: Box::pin(window_properties_tracker),
|
||||||
focus_item: Default::default(),
|
focus_item: Default::default(),
|
||||||
|
@ -271,6 +285,7 @@ impl WindowInner {
|
||||||
self.close_popup();
|
self.close_popup();
|
||||||
self.focus_item.replace(Default::default());
|
self.focus_item.replace(Default::default());
|
||||||
self.mouse_input_state.replace(Default::default());
|
self.mouse_input_state.replace(Default::default());
|
||||||
|
self.modifiers.replace(Default::default());
|
||||||
self.component.replace(ComponentRc::downgrade(component));
|
self.component.replace(ComponentRc::downgrade(component));
|
||||||
self.window_properties_tracker.set_dirty(); // component changed, layout constraints for sure must be re-calculated
|
self.window_properties_tracker.set_dirty(); // component changed, layout constraints for sure must be re-calculated
|
||||||
let window_adapter = self.window_adapter();
|
let window_adapter = self.window_adapter();
|
||||||
|
@ -370,7 +385,18 @@ impl WindowInner {
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// * `event`: The key event received by the windowing system.
|
/// * `event`: The key event received by the windowing system.
|
||||||
/// * `component`: The Slint compiled component that provides the tree of items.
|
/// * `component`: The Slint compiled component that provides the tree of items.
|
||||||
pub fn process_key_input(&self, event: &KeyEvent) {
|
pub fn process_key_input(&self, event: KeyInputEvent) {
|
||||||
|
if let Some(updated_modifier) = self
|
||||||
|
.modifiers
|
||||||
|
.get()
|
||||||
|
.state_update(event.event_type == KeyEventType::KeyPressed, &event.text)
|
||||||
|
{
|
||||||
|
// Updates the key modifiers depending on the key code and pressed state.
|
||||||
|
self.modifiers.set(updated_modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = input_as_key_event(event, self.modifiers.get().into());
|
||||||
|
|
||||||
let mut item = self.focus_item.borrow().clone().upgrade();
|
let mut item = self.focus_item.borrow().clone().upgrade();
|
||||||
while let Some(focus_item) = item {
|
while let Some(focus_item) = item {
|
||||||
if !focus_item.is_visible() {
|
if !focus_item.is_visible() {
|
||||||
|
@ -378,7 +404,7 @@ impl WindowInner {
|
||||||
self.take_focus_item();
|
self.take_focus_item();
|
||||||
} else {
|
} else {
|
||||||
if focus_item.borrow().as_ref().key_event(
|
if focus_item.borrow().as_ref().key_event(
|
||||||
event,
|
&event,
|
||||||
&self.window_adapter(),
|
&self.window_adapter(),
|
||||||
&focus_item,
|
&focus_item,
|
||||||
) == crate::input::KeyEventResult::EventAccepted
|
) == crate::input::KeyEventResult::EventAccepted
|
||||||
|
@ -390,9 +416,13 @@ impl WindowInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make Tab/Backtab handle keyboard focus
|
// Make Tab/Backtab handle keyboard focus
|
||||||
if event.text.starts_with(key_codes::Tab) && event.event_type == KeyEventType::KeyPressed {
|
if event.text.starts_with(key_codes::Tab)
|
||||||
|
&& !event.modifiers.shift
|
||||||
|
&& event.event_type == KeyEventType::KeyPressed
|
||||||
|
{
|
||||||
self.focus_next_item();
|
self.focus_next_item();
|
||||||
} else if event.text.starts_with(key_codes::Backtab)
|
} else if (event.text.starts_with(key_codes::Backtab)
|
||||||
|
|| (event.text.starts_with(key_codes::Tab) && event.modifiers.shift))
|
||||||
&& event.event_type == KeyEventType::KeyPressed
|
&& event.event_type == KeyEventType::KeyPressed
|
||||||
{
|
{
|
||||||
self.focus_previous_item();
|
self.focus_previous_item();
|
||||||
|
|
|
@ -1108,6 +1108,18 @@ pub mod testing {
|
||||||
&WindowInner::from_pub(comp.window()).window_adapter(),
|
&WindowInner::from_pub(comp.window()).window_adapter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/// Wrapper around [`i_slint_core::tests::slint_send_keyboard_char`]
|
||||||
|
pub fn send_keyboard_char(
|
||||||
|
comp: &super::ComponentInstance,
|
||||||
|
string: i_slint_core::SharedString,
|
||||||
|
pressed: bool,
|
||||||
|
) {
|
||||||
|
i_slint_core::tests::slint_send_keyboard_char(
|
||||||
|
&string,
|
||||||
|
pressed,
|
||||||
|
&WindowInner::from_pub(comp.window()).window_adapter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
/// Wrapper around [`i_slint_core::tests::send_keyboard_string_sequence`]
|
/// Wrapper around [`i_slint_core::tests::send_keyboard_string_sequence`]
|
||||||
pub fn send_keyboard_string_sequence(
|
pub fn send_keyboard_string_sequence(
|
||||||
comp: &super::ComponentInstance,
|
comp: &super::ComponentInstance,
|
||||||
|
@ -1115,7 +1127,6 @@ pub mod testing {
|
||||||
) {
|
) {
|
||||||
i_slint_core::tests::send_keyboard_string_sequence(
|
i_slint_core::tests::send_keyboard_string_sequence(
|
||||||
&string,
|
&string,
|
||||||
Default::default(),
|
|
||||||
&WindowInner::from_pub(comp.window()).window_adapter(),
|
&WindowInner::from_pub(comp.window()).window_adapter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ W := Window {
|
||||||
field := FocusScope {
|
field := FocusScope {
|
||||||
vertical_stretch: 1;
|
vertical_stretch: 1;
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
if (event.text == Keys.F1) {
|
if (event.text == Key.F1) {
|
||||||
debug("F1");
|
debug("F1");
|
||||||
}
|
}
|
||||||
if (event.text == Keys.PageUp) {
|
if (event.text == Key.PageUp) {
|
||||||
debug("PageUp");
|
debug("PageUp");
|
||||||
}
|
}
|
||||||
if (event.modifiers.control) {
|
if (event.modifiers.control) {
|
||||||
|
|
|
@ -13,7 +13,9 @@ TestCase := Rectangle {
|
||||||
FocusScope {
|
FocusScope {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
|
if (event.text != Key.Shift && event.text != Key.Control) {
|
||||||
received += event.text;
|
received += event.text;
|
||||||
|
}
|
||||||
accept
|
accept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,12 +41,6 @@ TestCase := Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
```rust
|
|
||||||
let ctrl_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
control: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
|
|
||||||
assert!(!instance.get_input1_focused());
|
assert!(!instance.get_input1_focused());
|
||||||
|
@ -55,7 +51,7 @@ assert_eq!(instance.get_input2_text(), "Hello");
|
||||||
assert_eq!(instance.get_input1_text(), "");
|
assert_eq!(instance.get_input1_text(), "");
|
||||||
assert_eq!(instance.get_received(), "");
|
assert_eq!(instance.get_received(), "");
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, ctrl_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, "ß");
|
slint_testing::send_keyboard_string_sequence(&instance, "ß");
|
||||||
assert_eq!(instance.get_input2_text(), "Hello");
|
assert_eq!(instance.get_input2_text(), "Hello");
|
||||||
assert_eq!(instance.get_input1_text(), "");
|
assert_eq!(instance.get_input1_text(), "");
|
||||||
|
@ -63,9 +59,6 @@ assert_eq!(instance.get_received(), "ß");
|
||||||
```
|
```
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
slint::cbindgen_private::KeyboardModifiers ctrl_modifier{};
|
|
||||||
ctrl_modifier.control = true;
|
|
||||||
|
|
||||||
auto handle = TestCase::create();
|
auto handle = TestCase::create();
|
||||||
const TestCase &instance = *handle;
|
const TestCase &instance = *handle;
|
||||||
|
|
||||||
|
@ -77,7 +70,9 @@ assert_eq(instance.get_input2_text(), "Hello");
|
||||||
assert_eq(instance.get_input1_text(), "");
|
assert_eq(instance.get_input1_text(), "");
|
||||||
assert_eq(instance.get_received(), "");
|
assert_eq(instance.get_received(), "");
|
||||||
|
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, "ß", ctrl_modifier);
|
// Control key
|
||||||
|
slint_testing::send_keyboard_char(&instance, slint::SharedString(u8"\U00000011"), true);
|
||||||
|
slint_testing::send_keyboard_string_sequence(&instance, "ß");
|
||||||
assert_eq(instance.get_input2_text(), "Hello");
|
assert_eq(instance.get_input2_text(), "Hello");
|
||||||
assert_eq(instance.get_input1_text(), "");
|
assert_eq(instance.get_input1_text(), "");
|
||||||
assert_eq(instance.get_received(), "ß");
|
assert_eq(instance.get_received(), "ß");
|
||||||
|
|
|
@ -21,23 +21,27 @@ const RIGHT_CODE: char = '\u{F703}';
|
||||||
const DEL_CODE: char = '\u{007f}';
|
const DEL_CODE: char = '\u{007f}';
|
||||||
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
||||||
|
|
||||||
let shift_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
fn send_move_mod_modifier(instance: &TestCase, pressed: bool) {
|
||||||
shift: true,
|
if cfg!(not(target_os = "macos")) {
|
||||||
..Default::default()
|
slint_testing::send_keyboard_char(instance, slint::private_unstable_api::re_exports::Key::Control.into(), pressed);
|
||||||
};
|
}
|
||||||
|
|
||||||
let move_mod_shift_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
if cfg!(target_os = "macos") {
|
||||||
shift: true,
|
slint_testing::send_keyboard_char(instance, slint::private_unstable_api::re_exports::Key::Alt.into(), pressed);
|
||||||
control: cfg!(not(target_os = "macos")),
|
}
|
||||||
alt: cfg!(target_os = "macos"),
|
}
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let move_mod_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
fn send_move_mod_shift_modifier(instance: &TestCase, pressed: bool) {
|
||||||
control: cfg!(not(target_os = "macos")),
|
slint_testing::send_keyboard_char(instance, slint::private_unstable_api::re_exports::Key::Shift.into(), pressed);
|
||||||
alt: cfg!(target_os = "macos"),
|
|
||||||
..Default::default()
|
if cfg!(not(target_os = "macos")) {
|
||||||
};
|
slint_testing::send_keyboard_char(instance, slint::private_unstable_api::re_exports::Key::Control.into(), pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
slint_testing::send_keyboard_char(instance, slint::private_unstable_api::re_exports::Key::Alt.into(), pressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
slint_testing::send_mouse_click(&instance, 50., 50.);
|
slint_testing::send_mouse_click(&instance, 50., 50.);
|
||||||
|
@ -47,9 +51,9 @@ slint_testing::send_keyboard_string_sequence(&instance, "Test");
|
||||||
assert_eq!(instance.get_test_text(), "Test");
|
assert_eq!(instance.get_test_text(), "Test");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), false);
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
@ -69,9 +73,9 @@ slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_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);
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_shift_modifier);
|
send_move_mod_shift_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &DOWN_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &DOWN_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_shift_modifier(&instance, false);
|
||||||
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);
|
||||||
|
@ -79,20 +83,17 @@ assert_eq!(instance.get_test_anchor_pos(), 0);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_shift_modifier);
|
send_move_mod_shift_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &UP_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &UP_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_shift_modifier(&instance, false);
|
||||||
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(), 1);
|
assert_eq!(instance.get_test_anchor_pos(), 1);
|
||||||
|
|
||||||
// Select all and start over
|
// Select all and start over
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers {
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
control: true,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &"a");
|
slint_testing::send_keyboard_string_sequence(&instance, &"a");
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), false);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_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(), "");
|
||||||
|
@ -108,15 +109,15 @@ assert_eq!(instance.get_test_cursor_pos(), 22);
|
||||||
|
|
||||||
// Delete word backwards when the cursor is between the 'F' of Fifth and the leading space.
|
// Delete word backwards when the cursor is between the 'F' of Fifth and the leading space.
|
||||||
// -> Delete "Word"
|
// -> Delete "Word"
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_modifier);
|
send_move_mod_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_modifier(&instance, false);
|
||||||
assert_eq!(instance.get_test_text(), "First Word Third Fifth");
|
assert_eq!(instance.get_test_text(), "First Word Third Fifth");
|
||||||
|
|
||||||
// Once more :-)
|
// Once more :-)
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_modifier);
|
send_move_mod_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_modifier(&instance, false);
|
||||||
assert_eq!(instance.get_test_text(), "First Word Fifth");
|
assert_eq!(instance.get_test_text(), "First Word Fifth");
|
||||||
|
|
||||||
// Move cursor between the "d" of "Word" and the trailing space
|
// Move cursor between the "d" of "Word" and the trailing space
|
||||||
|
@ -128,18 +129,15 @@ slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
|
|
||||||
// Delete word forwards
|
// Delete word forwards
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_modifier);
|
send_move_mod_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &DEL_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &DEL_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_modifier(&instance, false);
|
||||||
assert_eq!(instance.get_test_text(), "First Fifth");
|
assert_eq!(instance.get_test_text(), "First Fifth");
|
||||||
|
|
||||||
// Select all and start over
|
// Select all and start over
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers {
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
control: true,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &"a");
|
slint_testing::send_keyboard_string_sequence(&instance, &"a");
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), false);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_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(), "");
|
||||||
|
@ -150,16 +148,16 @@ assert_eq!(instance.get_test_text(), "First Second");
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), false);
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
|
|
||||||
// When there's an existing selection, always just delete that
|
// When there's an existing selection, always just delete that
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_modifier);
|
send_move_mod_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_modifier(&instance, false);
|
||||||
assert_eq!(instance.get_test_text(), "First Send");
|
assert_eq!(instance.get_test_text(), "First Send");
|
||||||
|
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
|
@ -168,15 +166,15 @@ slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), false);
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
|
|
||||||
// When there's an existing selection, always just delete that
|
// When there's an existing selection, always just delete that
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, move_mod_modifier);
|
send_move_mod_modifier(&instance, true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &DEL_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &DEL_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
send_move_mod_modifier(&instance, false);
|
||||||
assert_eq!(instance.get_test_text(), "Fist Send");
|
assert_eq!(instance.get_test_text(), "Fist Send");
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,11 +17,6 @@ TestCase := TextInput {
|
||||||
const LEFT_CODE: char = '\u{F702}';
|
const LEFT_CODE: char = '\u{F702}';
|
||||||
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
||||||
|
|
||||||
let shift_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
shift: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
slint_testing::send_mouse_click(&instance, 50., 50.);
|
slint_testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -31,9 +26,9 @@ assert_eq!(instance.get_test_text(), "e\u{0301}");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
// Test that selecting the grapheme works
|
// Test that selecting the grapheme works
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), false);
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &BACK_CODE.to_string());
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,6 @@ TestCase := TextInput {
|
||||||
|
|
||||||
const LEFT_CODE: char = '\u{F702}';
|
const LEFT_CODE: char = '\u{F702}';
|
||||||
|
|
||||||
let shift_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
shift: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let control_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
control: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
slint_testing::send_mouse_click(&instance, 50., 50.);
|
slint_testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -34,14 +24,16 @@ slint_testing::send_keyboard_string_sequence(&instance, "Test");
|
||||||
assert_eq!(instance.get_test_text(), "Test");
|
assert_eq!(instance.get_test_text(), "Test");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, control_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, "a");
|
slint_testing::send_keyboard_string_sequence(&instance, "a");
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, control_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, "x");
|
slint_testing::send_keyboard_string_sequence(&instance, "x");
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), false);
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
assert_eq!(instance.get_test_text(), "st");
|
assert_eq!(instance.get_test_text(), "st");
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 0);
|
assert_eq!(instance.get_test_cursor_pos(), 0);
|
||||||
|
|
186
tests/cases/text/keyboard_modifiers.slint
Normal file
186
tests/cases/text/keyboard_modifiers.slint
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
TestCase := Window {
|
||||||
|
width: 100phx;
|
||||||
|
height: 100phx;
|
||||||
|
ti := FocusScope {
|
||||||
|
key-pressed(event) => {
|
||||||
|
debug("pressy");
|
||||||
|
debug(event.modifiers.shift);
|
||||||
|
shift_modifier = event.modifiers.shift;
|
||||||
|
alt_modifier = event.modifiers.alt;
|
||||||
|
control_modifier = event.modifiers.control;
|
||||||
|
meta_modifier = event.modifiers.meta;
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
key-released(event) => {
|
||||||
|
shift_modifier = event.modifiers.shift;
|
||||||
|
alt_modifier = event.modifiers.alt;
|
||||||
|
control_modifier = event.modifiers.control;
|
||||||
|
meta_modifier = event.modifiers.meta;
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property <bool> input_focused: ti.has_focus;
|
||||||
|
property <bool> shift_modifier;
|
||||||
|
property <bool> alt_modifier;
|
||||||
|
property <bool> control_modifier;
|
||||||
|
property <bool> meta_modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
```rust
|
||||||
|
|
||||||
|
const SHIFT_CODE: char = '\u{0010}';
|
||||||
|
const SHIFTR_CODE: char = '\u{0015}';
|
||||||
|
const ALT_CODE: char = '\u{0012}';
|
||||||
|
const ALTGR_CODE: char = '\u{0013}';
|
||||||
|
const CONTROL_CODE: char = '\u{0011}';
|
||||||
|
const CONTROLR_CODE: char = '\u{0016}';
|
||||||
|
const META_CODE: char = '\u{00E0}';
|
||||||
|
const METAR_CODE: char = '\u{00E1}';
|
||||||
|
|
||||||
|
let instance = TestCase::new();
|
||||||
|
slint_testing::send_mouse_click(&instance, 5., 5.);
|
||||||
|
assert!(instance.get_input_focused());
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, SHIFT_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), true);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, SHIFTR_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), true);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, SHIFT_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), true);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, SHIFTR_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, ALT_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), true);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, ALTGR_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), true);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
// AltGr does not set the alt modifier
|
||||||
|
slint_testing::send_keyboard_char(&instance, ALT_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, ALTGR_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, CONTROL_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), true);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, CONTROLR_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), true);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, CONTROL_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), true);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, CONTROLR_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, META_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), true);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, METAR_CODE, true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), true);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, META_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), true);
|
||||||
|
|
||||||
|
slint_testing::send_keyboard_char(&instance, METAR_CODE, false);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', true);
|
||||||
|
slint_testing::send_keyboard_char(&instance, 'a', false);
|
||||||
|
assert_eq!(instance.get_shift_modifier(), false);
|
||||||
|
assert_eq!(instance.get_alt_modifier(), false);
|
||||||
|
assert_eq!(instance.get_control_modifier(), false);
|
||||||
|
assert_eq!(instance.get_meta_modifier(), false);
|
||||||
|
|
||||||
|
```
|
||||||
|
*/
|
|
@ -14,12 +14,6 @@ TestCase := TextInput {
|
||||||
/*
|
/*
|
||||||
```rust
|
```rust
|
||||||
|
|
||||||
|
|
||||||
let control_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
control: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
slint_testing::send_mouse_click(&instance, 50., 50.);
|
slint_testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -28,9 +22,9 @@ slint_testing::send_keyboard_string_sequence(&instance, "Test");
|
||||||
assert_eq!(instance.get_test_text(), "Test");
|
assert_eq!(instance.get_test_text(), "Test");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, control_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, "a");
|
slint_testing::send_keyboard_string_sequence(&instance, "a");
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, slint::private_unstable_api::re_exports::KeyboardModifiers::default());
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Control.into(), true);
|
||||||
assert!(instance.get_has_selection());
|
assert!(instance.get_has_selection());
|
||||||
assert_eq!(instance.get_test_cursor_pos(), 4);
|
assert_eq!(instance.get_test_cursor_pos(), 4);
|
||||||
assert_eq!(instance.get_test_anchor_pos(), 0);
|
assert_eq!(instance.get_test_anchor_pos(), 0);
|
||||||
|
|
|
@ -17,11 +17,6 @@ TestCase := TextInput {
|
||||||
const LEFT_CODE: char = '\u{F702}';
|
const LEFT_CODE: char = '\u{F702}';
|
||||||
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
const BACK_CODE: char = '\u{0008}'; // backspace \b
|
||||||
|
|
||||||
let shift_modifier = slint::private_unstable_api::re_exports::KeyboardModifiers {
|
|
||||||
shift: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let instance = TestCase::new();
|
let instance = TestCase::new();
|
||||||
slint_testing::send_mouse_click(&instance, 50., 50.);
|
slint_testing::send_mouse_click(&instance, 50., 50.);
|
||||||
assert!(instance.get_input_focused());
|
assert!(instance.get_input_focused());
|
||||||
|
@ -30,7 +25,7 @@ slint_testing::send_keyboard_string_sequence(&instance, "😍");
|
||||||
assert_eq!(instance.get_test_text(), "😍");
|
assert_eq!(instance.get_test_text(), "😍");
|
||||||
assert!(!instance.get_has_selection());
|
assert!(!instance.get_has_selection());
|
||||||
|
|
||||||
slint_testing::set_current_keyboard_modifiers(&instance, shift_modifier);
|
slint_testing::send_keyboard_char(&instance, slint::private_unstable_api::re_exports::Key::Shift.into(), true);
|
||||||
slint_testing::send_keyboard_string_sequence(&instance, &LEFT_CODE.to_string());
|
slint_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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue