@effindomv2/fui-rs 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMMERCIAL.md +7 -0
- package/Cargo.toml +34 -0
- package/LICENSE.md +9 -0
- package/README.md +88 -0
- package/package.json +54 -0
- package/scripts/build.sh +227 -0
- package/scripts/check-runtime-dependency.sh +41 -0
- package/scripts/framework-host-services.ts +40 -0
- package/scripts/generate-host-events.ts +17 -0
- package/scripts/generate-host-services.ts +28 -0
- package/scripts/hostgen/common.ts +73 -0
- package/scripts/hostgen/registry.ts +159 -0
- package/scripts/hostgen/rust-host-events.ts +171 -0
- package/scripts/hostgen/rust-host-services.ts +257 -0
- package/src/animation.rs +625 -0
- package/src/app.rs +324 -0
- package/src/assets.rs +572 -0
- package/src/bindings/mod.rs +1 -0
- package/src/bindings/ui.rs +705 -0
- package/src/bitmap.rs +317 -0
- package/src/bridge_callbacks.rs +242 -0
- package/src/context_menu_manager.rs +332 -0
- package/src/controls/anti_selection_area.rs +54 -0
- package/src/controls/button.rs +542 -0
- package/src/controls/checkbox.rs +372 -0
- package/src/controls/combobox.rs +1574 -0
- package/src/controls/context_menu.rs +1367 -0
- package/src/controls/control_template_set.rs +46 -0
- package/src/controls/control_tokens.rs +596 -0
- package/src/controls/dialog.rs +590 -0
- package/src/controls/dropdown.rs +1010 -0
- package/src/controls/form.rs +229 -0
- package/src/controls/internal/button_presenter.rs +142 -0
- package/src/controls/internal/checkbox_indicator_presenter.rs +210 -0
- package/src/controls/internal/dropdown_chevron_presenter.rs +134 -0
- package/src/controls/internal/dropdown_field_presenter.rs +269 -0
- package/src/controls/internal/dropdown_option_row_presenter.rs +184 -0
- package/src/controls/internal/mod.rs +13 -0
- package/src/controls/internal/pressable_indicator_presenter.rs +26 -0
- package/src/controls/internal/pressable_labeled_control.rs +492 -0
- package/src/controls/internal/radio_indicator_presenter.rs +208 -0
- package/src/controls/internal/selectable_popup_list.rs +597 -0
- package/src/controls/internal/slider_presenter.rs +282 -0
- package/src/controls/internal/switch_indicator_presenter.rs +235 -0
- package/src/controls/internal/text_input_core.rs +1074 -0
- package/src/controls/internal/text_input_presenter.rs +108 -0
- package/src/controls/mod.rs +235 -0
- package/src/controls/nav_link.rs +395 -0
- package/src/controls/popup.rs +191 -0
- package/src/controls/progress_bar.rs +287 -0
- package/src/controls/radio_button.rs +348 -0
- package/src/controls/radio_group.rs +283 -0
- package/src/controls/selection_area.rs +82 -0
- package/src/controls/shared.rs +68 -0
- package/src/controls/slider.rs +701 -0
- package/src/controls/switch.rs +293 -0
- package/src/controls/templating.rs +49 -0
- package/src/controls/tests.rs +3905 -0
- package/src/controls/text_area.rs +207 -0
- package/src/controls/text_input.rs +192 -0
- package/src/debug.rs +146 -0
- package/src/drag_drop.rs +424 -0
- package/src/drag_gesture.rs +258 -0
- package/src/drawing.rs +385 -0
- package/src/event.rs +1603 -0
- package/src/external_drop.rs +467 -0
- package/src/fetch.rs +500 -0
- package/src/ffi.rs +3542 -0
- package/src/file.rs +1677 -0
- package/src/focus_adorner.rs +307 -0
- package/src/focus_visibility.rs +121 -0
- package/src/frame_scheduler.rs +159 -0
- package/src/frame_signal.rs +64 -0
- package/src/generated/ffi.rs +798 -0
- package/src/generated/framework_host_services.rs +74 -0
- package/src/generated/mod.rs +2 -0
- package/src/host_services.rs +162 -0
- package/src/image_sampling.rs +78 -0
- package/src/keyboard_scroll.rs +137 -0
- package/src/keyboard_scroll_tracker.rs +385 -0
- package/src/lib.rs +547 -0
- package/src/logger.rs +56 -0
- package/src/mobile_text_selection_toolbar.rs +1034 -0
- package/src/navigation.rs +48 -0
- package/src/node/core.rs +2210 -0
- package/src/node/custom_drawable.rs +149 -0
- package/src/node/flex_box.rs +874 -0
- package/src/node/grid.rs +304 -0
- package/src/node/helpers.rs +442 -0
- package/src/node/image.rs +506 -0
- package/src/node/mod.rs +315 -0
- package/src/node/scroll_bar.rs +845 -0
- package/src/node/scroll_box.rs +514 -0
- package/src/node/scroll_state.rs +121 -0
- package/src/node/scroll_view.rs +557 -0
- package/src/node/svg_node.rs +474 -0
- package/src/node/text_node.rs +633 -0
- package/src/node/virtual_list.rs +678 -0
- package/src/panic_hook.rs +36 -0
- package/src/persisted.rs +274 -0
- package/src/platform.rs +556 -0
- package/src/popup_presenter.rs +344 -0
- package/src/selection_handle_adorner.rs +838 -0
- package/src/signal.rs +134 -0
- package/src/text.rs +1065 -0
- package/src/theme.rs +571 -0
- package/src/timers.rs +106 -0
- package/src/tool_tip.rs +167 -0
- package/src/tool_tip_manager.rs +691 -0
- package/src/transitions.rs +41 -0
- package/src/typography.rs +520 -0
- package/src/viewport.rs +108 -0
- package/src/worker.rs +308 -0
- package/src/worker_job.rs +82 -0
- package/src/worker_runtime.rs +172 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Generated by hostgen from ./scripts/framework-host-services.ts#frameworkHostServices.
|
|
2
|
+
// Do not edit by hand.
|
|
3
|
+
|
|
4
|
+
#![allow(dead_code)]
|
|
5
|
+
#![allow(non_snake_case)]
|
|
6
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
7
|
+
#[cfg_attr(target_family = "wasm", link(wasm_import_module = "fui_host"))]
|
|
8
|
+
unsafe extern "C" {
|
|
9
|
+
#[link_name = "fui_get_accent_color"]
|
|
10
|
+
fn __host_fui_get_accent_color() -> u32;
|
|
11
|
+
#[link_name = "fui_get_platform_family"]
|
|
12
|
+
fn __host_fui_get_platform_family() -> u32;
|
|
13
|
+
#[link_name = "fui_is_coarse_pointer"]
|
|
14
|
+
fn __host_fui_is_coarse_pointer() -> bool;
|
|
15
|
+
#[link_name = "fui_is_dark_mode"]
|
|
16
|
+
fn __host_fui_is_dark_mode() -> bool;
|
|
17
|
+
#[link_name = "fui_now_ms"]
|
|
18
|
+
fn __host_fui_now_ms() -> f64;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn fui_get_accent_color() -> u32 {
|
|
22
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
23
|
+
{
|
|
24
|
+
unsafe { __host_fui_get_accent_color() }
|
|
25
|
+
}
|
|
26
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
27
|
+
{
|
|
28
|
+
unsafe { crate::ffi::fui_get_accent_color() }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn fui_get_platform_family() -> u32 {
|
|
33
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
34
|
+
{
|
|
35
|
+
unsafe { __host_fui_get_platform_family() }
|
|
36
|
+
}
|
|
37
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
38
|
+
{
|
|
39
|
+
unsafe { crate::ffi::fui_get_platform_family() }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn fui_is_coarse_pointer() -> bool {
|
|
44
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
45
|
+
{
|
|
46
|
+
unsafe { __host_fui_is_coarse_pointer() }
|
|
47
|
+
}
|
|
48
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
49
|
+
{
|
|
50
|
+
unsafe { crate::ffi::fui_is_coarse_pointer() }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn fui_is_dark_mode() -> bool {
|
|
55
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
56
|
+
{
|
|
57
|
+
unsafe { __host_fui_is_dark_mode() }
|
|
58
|
+
}
|
|
59
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
60
|
+
{
|
|
61
|
+
unsafe { crate::ffi::fui_is_dark_mode() }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pub fn fui_now_ms() -> f64 {
|
|
66
|
+
#[cfg(any(target_family = "wasm", feature = "native-runtime"))]
|
|
67
|
+
{
|
|
68
|
+
unsafe { __host_fui_now_ms() }
|
|
69
|
+
}
|
|
70
|
+
#[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
|
|
71
|
+
{
|
|
72
|
+
crate::ffi::test::host_now_ms()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
use crate::event;
|
|
2
|
+
|
|
3
|
+
pub fn host_service_result_buffer_ptr() -> *mut u8 {
|
|
4
|
+
event::__fui_text_buffer() as *mut u8
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
pub fn host_service_result_buffer_size() -> u32 {
|
|
8
|
+
event::__fui_text_buffer_size()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
fn assert_result_byte_length(result_len: u32, import_name: &str) {
|
|
12
|
+
let capacity = host_service_result_buffer_size();
|
|
13
|
+
if result_len > capacity {
|
|
14
|
+
panic!(
|
|
15
|
+
"Host service {} returned {} bytes but the shared result buffer only holds {}.",
|
|
16
|
+
import_name, result_len, capacity
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn decode_host_service_string_result(
|
|
22
|
+
result_ptr: *mut u8,
|
|
23
|
+
result_len: u32,
|
|
24
|
+
import_name: &str,
|
|
25
|
+
) -> String {
|
|
26
|
+
assert_result_byte_length(result_len, import_name);
|
|
27
|
+
if result_len == 0 {
|
|
28
|
+
return String::new();
|
|
29
|
+
}
|
|
30
|
+
let bytes = unsafe { std::slice::from_raw_parts(result_ptr as *const u8, result_len as usize) };
|
|
31
|
+
String::from_utf8_lossy(bytes).into_owned()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn decode_host_service_bytes_result(
|
|
35
|
+
result_ptr: *mut u8,
|
|
36
|
+
result_len: u32,
|
|
37
|
+
import_name: &str,
|
|
38
|
+
) -> Vec<u8> {
|
|
39
|
+
assert_result_byte_length(result_len, import_name);
|
|
40
|
+
if result_len == 0 {
|
|
41
|
+
return Vec::new();
|
|
42
|
+
}
|
|
43
|
+
unsafe { std::slice::from_raw_parts(result_ptr as *const u8, result_len as usize) }.to_vec()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn decode_host_service_i32_array_result(
|
|
47
|
+
result_ptr: *mut u8,
|
|
48
|
+
result_len: u32,
|
|
49
|
+
import_name: &str,
|
|
50
|
+
) -> Vec<i32> {
|
|
51
|
+
assert_result_byte_length(result_len, import_name);
|
|
52
|
+
assert_eq!(
|
|
53
|
+
result_len & 3,
|
|
54
|
+
0,
|
|
55
|
+
"Host service {} returned misaligned i32 array byte length.",
|
|
56
|
+
import_name
|
|
57
|
+
);
|
|
58
|
+
if result_len == 0 {
|
|
59
|
+
return Vec::new();
|
|
60
|
+
}
|
|
61
|
+
unsafe {
|
|
62
|
+
std::slice::from_raw_parts(result_ptr as *const i32, (result_len >> 2) as usize).to_vec()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub fn decode_host_service_u32_array_result(
|
|
67
|
+
result_ptr: *mut u8,
|
|
68
|
+
result_len: u32,
|
|
69
|
+
import_name: &str,
|
|
70
|
+
) -> Vec<u32> {
|
|
71
|
+
assert_result_byte_length(result_len, import_name);
|
|
72
|
+
assert_eq!(
|
|
73
|
+
result_len & 3,
|
|
74
|
+
0,
|
|
75
|
+
"Host service {} returned misaligned u32 array byte length.",
|
|
76
|
+
import_name
|
|
77
|
+
);
|
|
78
|
+
if result_len == 0 {
|
|
79
|
+
return Vec::new();
|
|
80
|
+
}
|
|
81
|
+
unsafe {
|
|
82
|
+
std::slice::from_raw_parts(result_ptr as *const u32, (result_len >> 2) as usize).to_vec()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pub fn decode_host_service_i64_array_result(
|
|
87
|
+
result_ptr: *mut u8,
|
|
88
|
+
result_len: u32,
|
|
89
|
+
import_name: &str,
|
|
90
|
+
) -> Vec<i64> {
|
|
91
|
+
assert_result_byte_length(result_len, import_name);
|
|
92
|
+
assert_eq!(
|
|
93
|
+
result_len & 7,
|
|
94
|
+
0,
|
|
95
|
+
"Host service {} returned misaligned i64 array byte length.",
|
|
96
|
+
import_name
|
|
97
|
+
);
|
|
98
|
+
if result_len == 0 {
|
|
99
|
+
return Vec::new();
|
|
100
|
+
}
|
|
101
|
+
unsafe {
|
|
102
|
+
std::slice::from_raw_parts(result_ptr as *const i64, (result_len >> 3) as usize).to_vec()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pub fn decode_host_service_u64_array_result(
|
|
107
|
+
result_ptr: *mut u8,
|
|
108
|
+
result_len: u32,
|
|
109
|
+
import_name: &str,
|
|
110
|
+
) -> Vec<u64> {
|
|
111
|
+
assert_result_byte_length(result_len, import_name);
|
|
112
|
+
assert_eq!(
|
|
113
|
+
result_len & 7,
|
|
114
|
+
0,
|
|
115
|
+
"Host service {} returned misaligned u64 array byte length.",
|
|
116
|
+
import_name
|
|
117
|
+
);
|
|
118
|
+
if result_len == 0 {
|
|
119
|
+
return Vec::new();
|
|
120
|
+
}
|
|
121
|
+
unsafe {
|
|
122
|
+
std::slice::from_raw_parts(result_ptr as *const u64, (result_len >> 3) as usize).to_vec()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
pub fn decode_host_service_f64_array_result(
|
|
127
|
+
result_ptr: *mut u8,
|
|
128
|
+
result_len: u32,
|
|
129
|
+
import_name: &str,
|
|
130
|
+
) -> Vec<f64> {
|
|
131
|
+
assert_result_byte_length(result_len, import_name);
|
|
132
|
+
assert_eq!(
|
|
133
|
+
result_len & 7,
|
|
134
|
+
0,
|
|
135
|
+
"Host service {} returned misaligned f64 array byte length.",
|
|
136
|
+
import_name
|
|
137
|
+
);
|
|
138
|
+
if result_len == 0 {
|
|
139
|
+
return Vec::new();
|
|
140
|
+
}
|
|
141
|
+
unsafe {
|
|
142
|
+
std::slice::from_raw_parts(result_ptr as *const f64, (result_len >> 3) as usize).to_vec()
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#[cfg(test)]
|
|
147
|
+
mod tests {
|
|
148
|
+
use super::*;
|
|
149
|
+
|
|
150
|
+
#[test]
|
|
151
|
+
fn decodes_string_result_from_shared_text_buffer() {
|
|
152
|
+
let ptr = host_service_result_buffer_ptr();
|
|
153
|
+
let bytes = b"demo-host";
|
|
154
|
+
unsafe {
|
|
155
|
+
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
|
|
156
|
+
}
|
|
157
|
+
assert_eq!(
|
|
158
|
+
decode_host_service_string_result(ptr, bytes.len() as u32, "demo_service"),
|
|
159
|
+
"demo-host"
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
use crate::ffi::ImageSamplingKind;
|
|
2
|
+
|
|
3
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
4
|
+
pub enum ImageSamplingMode {
|
|
5
|
+
Linear,
|
|
6
|
+
Nearest,
|
|
7
|
+
LinearMipmapNearest,
|
|
8
|
+
LinearMipmapLinear,
|
|
9
|
+
CubicMitchell,
|
|
10
|
+
CubicCatmullRom,
|
|
11
|
+
Anisotropic,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl ImageSamplingMode {
|
|
15
|
+
pub(crate) fn ffi_kind(self) -> ImageSamplingKind {
|
|
16
|
+
match self {
|
|
17
|
+
Self::Linear => ImageSamplingKind::Linear,
|
|
18
|
+
Self::Nearest => ImageSamplingKind::Nearest,
|
|
19
|
+
Self::LinearMipmapNearest => ImageSamplingKind::LinearMipmapNearest,
|
|
20
|
+
Self::LinearMipmapLinear => ImageSamplingKind::LinearMipmapLinear,
|
|
21
|
+
Self::CubicMitchell => ImageSamplingKind::CubicMitchell,
|
|
22
|
+
Self::CubicCatmullRom => ImageSamplingKind::CubicCatmullRom,
|
|
23
|
+
Self::Anisotropic => ImageSamplingKind::Anisotropic,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
29
|
+
pub struct ImageSampling {
|
|
30
|
+
kind: ImageSamplingMode,
|
|
31
|
+
max_aniso: u32,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl ImageSampling {
|
|
35
|
+
pub fn linear() -> Self {
|
|
36
|
+
Self::new(ImageSamplingMode::Linear, 0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pub fn nearest() -> Self {
|
|
40
|
+
Self::new(ImageSamplingMode::Nearest, 0)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn linear_mipmap_nearest() -> Self {
|
|
44
|
+
Self::new(ImageSamplingMode::LinearMipmapNearest, 0)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn linear_mipmap_linear() -> Self {
|
|
48
|
+
Self::new(ImageSamplingMode::LinearMipmapLinear, 0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn cubic_mitchell() -> Self {
|
|
52
|
+
Self::new(ImageSamplingMode::CubicMitchell, 0)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn cubic_catmull_rom() -> Self {
|
|
56
|
+
Self::new(ImageSamplingMode::CubicCatmullRom, 0)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn anisotropic(max_aniso: u32) -> Self {
|
|
60
|
+
Self::new(ImageSamplingMode::Anisotropic, max_aniso)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub fn new(kind: ImageSamplingMode, max_aniso: u32) -> Self {
|
|
64
|
+
Self { kind, max_aniso }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
pub fn kind(self) -> ImageSamplingMode {
|
|
68
|
+
self.kind
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub(crate) fn ffi_kind(self) -> ImageSamplingKind {
|
|
72
|
+
self.kind.ffi_kind()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pub fn max_aniso(self) -> u32 {
|
|
76
|
+
self.max_aniso
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
use crate::event;
|
|
2
|
+
|
|
3
|
+
const KEYBOARD_SCROLL_LINE_STEP: f32 = 40.0;
|
|
4
|
+
const KEYBOARD_SCROLL_PAGE_OVERLAP: f32 = 40.0;
|
|
5
|
+
const KEYBOARD_SCROLL_TOLERANCE: f32 = 0.5;
|
|
6
|
+
|
|
7
|
+
fn is_keyboard_scroll_key(key: &str) -> bool {
|
|
8
|
+
matches!(
|
|
9
|
+
key,
|
|
10
|
+
"ArrowLeft"
|
|
11
|
+
| "ArrowRight"
|
|
12
|
+
| "ArrowUp"
|
|
13
|
+
| "ArrowDown"
|
|
14
|
+
| "PageUp"
|
|
15
|
+
| "PageDown"
|
|
16
|
+
| "Home"
|
|
17
|
+
| "End"
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fn is_horizontal_keyboard_scroll_key(key: &str) -> bool {
|
|
22
|
+
matches!(key, "ArrowLeft" | "ArrowRight")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn is_vertical_keyboard_scroll_key(key: &str) -> bool {
|
|
26
|
+
matches!(
|
|
27
|
+
key,
|
|
28
|
+
"ArrowUp" | "ArrowDown" | "PageUp" | "PageDown" | "Home" | "End"
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fn page_step(viewport_height: f32) -> f32 {
|
|
33
|
+
if viewport_height <= 0.0 {
|
|
34
|
+
return 0.0;
|
|
35
|
+
}
|
|
36
|
+
if viewport_height > KEYBOARD_SCROLL_PAGE_OVERLAP {
|
|
37
|
+
return viewport_height - KEYBOARD_SCROLL_PAGE_OVERLAP;
|
|
38
|
+
}
|
|
39
|
+
viewport_height * 0.875
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn clamp(value: f32, min_value: f32, max_value: f32) -> f32 {
|
|
43
|
+
if value < min_value {
|
|
44
|
+
min_value
|
|
45
|
+
} else if value > max_value {
|
|
46
|
+
max_value
|
|
47
|
+
} else {
|
|
48
|
+
value
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fn try_scroll_viewport(handle: crate::node::NodeHandle, key: &str) -> bool {
|
|
53
|
+
let Some(node) = event::resolve_node(handle) else {
|
|
54
|
+
return false;
|
|
55
|
+
};
|
|
56
|
+
let Some(state) = node.scroll_routing_state() else {
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
if is_horizontal_keyboard_scroll_key(key) {
|
|
60
|
+
if !state.enabled_x {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
let max_offset_x = (state.content_width - state.viewport_width).max(0.0);
|
|
64
|
+
if max_offset_x <= KEYBOARD_SCROLL_TOLERANCE {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
let current_offset_x = clamp(state.offset_x, 0.0, max_offset_x);
|
|
68
|
+
let mut next_offset_x = match key {
|
|
69
|
+
"ArrowLeft" => current_offset_x - KEYBOARD_SCROLL_LINE_STEP,
|
|
70
|
+
"ArrowRight" => current_offset_x + KEYBOARD_SCROLL_LINE_STEP,
|
|
71
|
+
_ => return false,
|
|
72
|
+
};
|
|
73
|
+
next_offset_x = clamp(next_offset_x, 0.0, max_offset_x);
|
|
74
|
+
if (next_offset_x - current_offset_x).abs() <= KEYBOARD_SCROLL_TOLERANCE {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
crate::bindings::ui::set_scroll_offset(handle.raw(), next_offset_x, state.offset_y);
|
|
78
|
+
node.set_scroll_routing_offsets(next_offset_x, state.offset_y);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if !is_vertical_keyboard_scroll_key(key) || !state.enabled_y {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
let max_offset_y = (state.content_height - state.viewport_height).max(0.0);
|
|
86
|
+
if max_offset_y <= KEYBOARD_SCROLL_TOLERANCE {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
let current_offset_y = clamp(state.offset_y, 0.0, max_offset_y);
|
|
90
|
+
let mut next_offset_y = match key {
|
|
91
|
+
"ArrowUp" => current_offset_y - KEYBOARD_SCROLL_LINE_STEP,
|
|
92
|
+
"ArrowDown" => current_offset_y + KEYBOARD_SCROLL_LINE_STEP,
|
|
93
|
+
"PageUp" => current_offset_y - page_step(state.viewport_height),
|
|
94
|
+
"PageDown" => current_offset_y + page_step(state.viewport_height),
|
|
95
|
+
"Home" => 0.0,
|
|
96
|
+
"End" => max_offset_y,
|
|
97
|
+
_ => return false,
|
|
98
|
+
};
|
|
99
|
+
next_offset_y = clamp(next_offset_y, 0.0, max_offset_y);
|
|
100
|
+
if (next_offset_y - current_offset_y).abs() <= KEYBOARD_SCROLL_TOLERANCE {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
crate::bindings::ui::set_scroll_offset(handle.raw(), state.offset_x, next_offset_y);
|
|
104
|
+
node.set_scroll_routing_offsets(state.offset_x, next_offset_y);
|
|
105
|
+
true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub(crate) fn handle_keyboard_scroll_fallback(key: &str, modifiers: u32) -> bool {
|
|
109
|
+
if modifiers != 0 || !is_keyboard_scroll_key(key) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if let Some(focused_node) = event::focused_node() {
|
|
114
|
+
let mut current = Some(focused_node);
|
|
115
|
+
while let Some(node) = current {
|
|
116
|
+
if node.is_scroll_view_for_routing() && try_scroll_viewport(node.handle(), key) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
current = node.parent();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if let Some(selected_candidate) =
|
|
124
|
+
crate::keyboard_scroll_tracker::get_keyboard_scroll_selected_candidate()
|
|
125
|
+
{
|
|
126
|
+
if try_scroll_viewport(selected_candidate, key) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for candidate in crate::keyboard_scroll_tracker::get_keyboard_scroll_fallback_candidates() {
|
|
132
|
+
if try_scroll_viewport(candidate, key) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
false
|
|
137
|
+
}
|