@effindomv2/fui-rs 0.1.17 → 0.1.18
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/Cargo.toml +1 -1
- package/README.md +69 -0
- package/package.json +1 -1
- package/scripts/hostgen/rust-host-events.ts +43 -13
- package/src/app.rs +7 -0
- package/src/assets.rs +2 -2
- package/src/bindings/ui.rs +2 -0
- package/src/bridge_callbacks.rs +27 -17
- package/src/controls/button.rs +1 -0
- package/src/controls/checkbox.rs +1 -0
- package/src/controls/combobox.rs +5 -3
- package/src/controls/dropdown.rs +3 -1
- package/src/controls/internal/pressable_labeled_control.rs +1 -0
- package/src/controls/internal/text_input_core.rs +7 -3
- package/src/controls/radio_button.rs +1 -0
- package/src/controls/slider.rs +1 -0
- package/src/controls/switch.rs +1 -0
- package/src/controls/tests.rs +19 -7
- package/src/debug.rs +8 -5
- package/src/drag_drop.rs +1 -3
- package/src/drawing.rs +8 -0
- package/src/event.rs +39 -27
- package/src/fetch.rs +21 -7
- package/src/ffi.rs +3 -1
- package/src/file.rs +88 -59
- package/src/host_events.rs +41 -0
- package/src/lib.rs +52 -9
- package/src/node/core.rs +2 -21
- package/src/node/helpers.rs +1 -1
- package/src/node/mod.rs +1 -1
- package/src/node/scroll_bar.rs +101 -0
- package/src/node/scroll_box.rs +7 -0
- package/src/node/scroll_view.rs +20 -14
- package/src/node/virtual_list.rs +120 -33
- package/src/platform.rs +3 -0
- package/src/popup_presenter.rs +1 -0
- package/src/theme.rs +2 -2
- package/src/timers.rs +1 -1
- package/src/typography.rs +6 -14
- package/src/worker.rs +25 -9
- package/src/worker_runtime.rs +61 -17
package/src/event.rs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#![allow(clippy::too_many_arguments)]
|
|
2
|
+
|
|
1
3
|
use crate::context_menu_manager;
|
|
2
4
|
use crate::drag_drop;
|
|
3
5
|
use crate::external_drop;
|
|
@@ -1331,27 +1333,27 @@ pub(crate) fn dispatch_external_drop_event(
|
|
|
1331
1333
|
})
|
|
1332
1334
|
}
|
|
1333
1335
|
|
|
1334
|
-
#[no_mangle]
|
|
1336
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1335
1337
|
pub extern "C" fn __fui_key_buffer() -> *const u8 {
|
|
1336
1338
|
std::ptr::addr_of_mut!(KEY_BUFFER).cast::<u8>()
|
|
1337
1339
|
}
|
|
1338
1340
|
|
|
1339
|
-
#[no_mangle]
|
|
1341
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1340
1342
|
pub extern "C" fn __fui_key_buffer_size() -> u32 {
|
|
1341
1343
|
256
|
|
1342
1344
|
}
|
|
1343
1345
|
|
|
1344
|
-
#[no_mangle]
|
|
1346
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1345
1347
|
pub extern "C" fn __fui_text_buffer() -> *const u8 {
|
|
1346
1348
|
std::ptr::addr_of_mut!(TEXT_BUFFER).cast::<u8>()
|
|
1347
1349
|
}
|
|
1348
1350
|
|
|
1349
|
-
#[no_mangle]
|
|
1351
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1350
1352
|
pub extern "C" fn __fui_text_buffer_size() -> u32 {
|
|
1351
1353
|
(16 * 1024) as u32
|
|
1352
1354
|
}
|
|
1353
1355
|
|
|
1354
|
-
#[no_mangle]
|
|
1356
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1355
1357
|
pub extern "C" fn __fui_on_pointer_event_with_metadata(
|
|
1356
1358
|
event_type: u32,
|
|
1357
1359
|
handle: u64,
|
|
@@ -1391,7 +1393,7 @@ pub extern "C" fn __fui_on_pointer_event_with_metadata(
|
|
|
1391
1393
|
)
|
|
1392
1394
|
}
|
|
1393
1395
|
|
|
1394
|
-
#[no_mangle]
|
|
1396
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1395
1397
|
pub extern "C" fn __fui_on_wheel_event(
|
|
1396
1398
|
handle: u64,
|
|
1397
1399
|
x: f32,
|
|
@@ -1412,8 +1414,10 @@ pub extern "C" fn __fui_on_wheel_event(
|
|
|
1412
1414
|
)
|
|
1413
1415
|
}
|
|
1414
1416
|
|
|
1415
|
-
#[no_mangle]
|
|
1416
|
-
|
|
1417
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1418
|
+
/// # Safety
|
|
1419
|
+
/// `key_ptr` must be null for an empty key or point to `key_len` readable bytes.
|
|
1420
|
+
pub unsafe extern "C" fn __fui_on_key_event(
|
|
1417
1421
|
event_type: u32,
|
|
1418
1422
|
key_ptr: *const u8,
|
|
1419
1423
|
key_len: u32,
|
|
@@ -1435,13 +1439,15 @@ pub extern "C" fn __fui_on_key_event(
|
|
|
1435
1439
|
)
|
|
1436
1440
|
}
|
|
1437
1441
|
|
|
1438
|
-
#[no_mangle]
|
|
1442
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1439
1443
|
pub extern "C" fn __fui_on_focus_changed(handle: u64, focused: bool) {
|
|
1440
1444
|
dispatch_focus_changed(NodeHandle::from_raw(handle), focused);
|
|
1441
1445
|
}
|
|
1442
1446
|
|
|
1443
|
-
#[no_mangle]
|
|
1444
|
-
|
|
1447
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1448
|
+
/// # Safety
|
|
1449
|
+
/// `text_ptr` must be null for empty text or point to `text_len` readable bytes.
|
|
1450
|
+
pub unsafe extern "C" fn __fui_on_text_changed(handle: u64, text_ptr: *const u8, text_len: u32) {
|
|
1445
1451
|
let text = if text_ptr.is_null() || text_len == 0 {
|
|
1446
1452
|
String::new()
|
|
1447
1453
|
} else {
|
|
@@ -1451,8 +1457,10 @@ pub extern "C" fn __fui_on_text_changed(handle: u64, text_ptr: *const u8, text_l
|
|
|
1451
1457
|
dispatch_text_changed(NodeHandle::from_raw(handle), text);
|
|
1452
1458
|
}
|
|
1453
1459
|
|
|
1454
|
-
#[no_mangle]
|
|
1455
|
-
|
|
1460
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1461
|
+
/// # Safety
|
|
1462
|
+
/// `text_ptr` must be null for empty replacement text or point to `text_len` readable bytes.
|
|
1463
|
+
pub unsafe extern "C" fn __fui_on_text_replaced(
|
|
1456
1464
|
handle: u64,
|
|
1457
1465
|
start: u32,
|
|
1458
1466
|
end: u32,
|
|
@@ -1468,13 +1476,15 @@ pub extern "C" fn __fui_on_text_replaced(
|
|
|
1468
1476
|
dispatch_text_replaced(NodeHandle::from_raw(handle), start, end, text);
|
|
1469
1477
|
}
|
|
1470
1478
|
|
|
1471
|
-
#[no_mangle]
|
|
1479
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1472
1480
|
pub extern "C" fn __fui_on_selection_changed(handle: u64, start: u32, end: u32) {
|
|
1473
1481
|
dispatch_selection_changed(NodeHandle::from_raw(handle), start, end);
|
|
1474
1482
|
}
|
|
1475
1483
|
|
|
1476
|
-
#[no_mangle]
|
|
1477
|
-
|
|
1484
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1485
|
+
/// # Safety
|
|
1486
|
+
/// `text_ptr` must be null for empty text or point to `text_len` readable bytes.
|
|
1487
|
+
pub unsafe extern "C" fn __fui_on_cross_selection_changed(
|
|
1478
1488
|
handle: u64,
|
|
1479
1489
|
text_ptr: *const u8,
|
|
1480
1490
|
text_len: u32,
|
|
@@ -1488,17 +1498,17 @@ pub extern "C" fn __fui_on_cross_selection_changed(
|
|
|
1488
1498
|
dispatch_cross_selection_changed(NodeHandle::from_raw(handle), text);
|
|
1489
1499
|
}
|
|
1490
1500
|
|
|
1491
|
-
#[no_mangle]
|
|
1501
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1492
1502
|
pub extern "C" fn __fui_resolve_gesture_owner(handle: u64) -> u64 {
|
|
1493
1503
|
resolve_gesture_owner(NodeHandle::from_raw(handle)).raw()
|
|
1494
1504
|
}
|
|
1495
1505
|
|
|
1496
|
-
#[no_mangle]
|
|
1506
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1497
1507
|
pub extern "C" fn __fui_get_gesture_intent(handle: u64) -> u32 {
|
|
1498
1508
|
get_gesture_intent(NodeHandle::from_raw(handle)) as u32
|
|
1499
1509
|
}
|
|
1500
1510
|
|
|
1501
|
-
#[no_mangle]
|
|
1511
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1502
1512
|
pub extern "C" fn __fui_on_gesture_event(
|
|
1503
1513
|
handle: u64,
|
|
1504
1514
|
phase: u32,
|
|
@@ -1523,27 +1533,27 @@ pub extern "C" fn __fui_on_gesture_event(
|
|
|
1523
1533
|
)
|
|
1524
1534
|
}
|
|
1525
1535
|
|
|
1526
|
-
#[no_mangle]
|
|
1536
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1527
1537
|
pub extern "C" fn __fui_resolve_long_press_owner(handle: u64) -> u64 {
|
|
1528
1538
|
resolve_long_press_owner(NodeHandle::from_raw(handle)).raw()
|
|
1529
1539
|
}
|
|
1530
1540
|
|
|
1531
|
-
#[no_mangle]
|
|
1541
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1532
1542
|
pub extern "C" fn __fui_get_long_press_minimum_duration_ms(handle: u64) -> i32 {
|
|
1533
1543
|
get_long_press_minimum_duration_ms(NodeHandle::from_raw(handle))
|
|
1534
1544
|
}
|
|
1535
1545
|
|
|
1536
|
-
#[no_mangle]
|
|
1546
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1537
1547
|
pub extern "C" fn __fui_get_long_press_movement_tolerance(handle: u64) -> f32 {
|
|
1538
1548
|
get_long_press_movement_tolerance(NodeHandle::from_raw(handle))
|
|
1539
1549
|
}
|
|
1540
1550
|
|
|
1541
|
-
#[no_mangle]
|
|
1551
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1542
1552
|
pub extern "C" fn __fui_long_press_continues_pointer_events(handle: u64) -> bool {
|
|
1543
1553
|
registered_node(handle).is_some_and(|node| node.has_drag_source())
|
|
1544
1554
|
}
|
|
1545
1555
|
|
|
1546
|
-
#[no_mangle]
|
|
1556
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1547
1557
|
pub extern "C" fn __fui_on_long_press_event(
|
|
1548
1558
|
handle: u64,
|
|
1549
1559
|
scene_x: f32,
|
|
@@ -1564,8 +1574,10 @@ pub extern "C" fn __fui_on_long_press_event(
|
|
|
1564
1574
|
)
|
|
1565
1575
|
}
|
|
1566
1576
|
|
|
1567
|
-
#[no_mangle]
|
|
1568
|
-
|
|
1577
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1578
|
+
/// # Safety
|
|
1579
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1580
|
+
pub unsafe extern "C" fn __fui_on_external_drag_event(
|
|
1569
1581
|
event_type: u32,
|
|
1570
1582
|
handle: u64,
|
|
1571
1583
|
x: f32,
|
|
@@ -1595,7 +1607,7 @@ pub extern "C" fn __fui_on_external_drag_event(
|
|
|
1595
1607
|
effect as u32
|
|
1596
1608
|
}
|
|
1597
1609
|
|
|
1598
|
-
#[no_mangle]
|
|
1610
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1599
1611
|
pub extern "C" fn fui_dispatch_custom_draw(handle: u64, canvas_ptr: usize) {
|
|
1600
1612
|
if let Some(node) = resolve_node(NodeHandle::from_raw(handle)) {
|
|
1601
1613
|
node.handle_custom_draw(canvas_ptr);
|
package/src/fetch.rs
CHANGED
|
@@ -337,8 +337,10 @@ pub fn reset_fetch_runtime() {
|
|
|
337
337
|
});
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
#[no_mangle]
|
|
341
|
-
|
|
340
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
341
|
+
/// # Safety
|
|
342
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
343
|
+
pub unsafe extern "C" fn __fui_on_fetch_complete(
|
|
342
344
|
request_id: u32,
|
|
343
345
|
ok: bool,
|
|
344
346
|
status: i32,
|
|
@@ -361,8 +363,14 @@ pub extern "C" fn __fui_on_fetch_complete(
|
|
|
361
363
|
);
|
|
362
364
|
}
|
|
363
365
|
|
|
364
|
-
#[no_mangle]
|
|
365
|
-
|
|
366
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
367
|
+
/// # Safety
|
|
368
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
369
|
+
pub unsafe extern "C" fn __fui_on_fetch_error(
|
|
370
|
+
request_id: u32,
|
|
371
|
+
payload_ptr: *const u8,
|
|
372
|
+
payload_len: u32,
|
|
373
|
+
) {
|
|
366
374
|
let message = if payload_ptr.is_null() || payload_len == 0 {
|
|
367
375
|
"Fetch request failed.".to_string()
|
|
368
376
|
} else {
|
|
@@ -413,7 +421,9 @@ mod tests {
|
|
|
413
421
|
}
|
|
414
422
|
bytes
|
|
415
423
|
};
|
|
416
|
-
|
|
424
|
+
unsafe {
|
|
425
|
+
super::__fui_on_fetch_complete(1, true, 200, payload.as_ptr(), payload.len() as u32);
|
|
426
|
+
}
|
|
417
427
|
assert_eq!(&*result.borrow(), "OK");
|
|
418
428
|
drop(request);
|
|
419
429
|
}
|
|
@@ -464,7 +474,9 @@ mod tests {
|
|
|
464
474
|
}
|
|
465
475
|
bytes
|
|
466
476
|
};
|
|
467
|
-
|
|
477
|
+
unsafe {
|
|
478
|
+
super::__fui_on_fetch_complete(1, true, 200, payload.as_ptr(), payload.len() as u32);
|
|
479
|
+
}
|
|
468
480
|
assert_eq!(&*result.borrow(), "");
|
|
469
481
|
drop(request);
|
|
470
482
|
}
|
|
@@ -493,7 +505,9 @@ mod tests {
|
|
|
493
505
|
result_clone.replace(event.message);
|
|
494
506
|
})
|
|
495
507
|
.start();
|
|
496
|
-
|
|
508
|
+
unsafe {
|
|
509
|
+
super::__fui_on_fetch_error(1, std::ptr::null(), 0);
|
|
510
|
+
}
|
|
497
511
|
assert_eq!(&*result.borrow(), "Fetch request failed.");
|
|
498
512
|
drop(request);
|
|
499
513
|
}
|
package/src/ffi.rs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#![allow(clippy::too_many_arguments, clippy::type_complexity)]
|
|
2
|
+
|
|
1
3
|
pub use crate::generated::ffi::*;
|
|
2
4
|
|
|
3
5
|
#[cfg(all(not(target_arch = "wasm32"), not(feature = "native-runtime")))]
|
|
@@ -3433,7 +3435,7 @@ pub unsafe fn fui_canvas_create_offscreen(width: u32, height: u32) -> u32 {
|
|
|
3433
3435
|
#[cfg(all(not(target_arch = "wasm32"), not(feature = "native-runtime")))]
|
|
3434
3436
|
pub unsafe fn fui_canvas_get_offscreen_ptr(offscreen_id: u32) -> usize {
|
|
3435
3437
|
push_call(Call::CanvasGetOffscreenPtr { offscreen_id });
|
|
3436
|
-
|
|
3438
|
+
0x1000 + offscreen_id as usize
|
|
3437
3439
|
}
|
|
3438
3440
|
|
|
3439
3441
|
#[cfg(all(not(target_arch = "wasm32"), not(feature = "native-runtime")))]
|
package/src/file.rs
CHANGED
|
@@ -1124,11 +1124,8 @@ fn finish_worker_process(
|
|
|
1124
1124
|
Option<FileWorkerCompleteCallback>,
|
|
1125
1125
|
Option<FileErrorCallback>,
|
|
1126
1126
|
)> {
|
|
1127
|
-
let
|
|
1128
|
-
|
|
1129
|
-
else {
|
|
1130
|
-
return None;
|
|
1131
|
-
};
|
|
1127
|
+
let request = ACTIVE_WORKER_PROCESS_REQUESTS
|
|
1128
|
+
.with(|requests| requests.borrow_mut().remove(&request_id))?;
|
|
1132
1129
|
let mut inner = request.borrow_mut();
|
|
1133
1130
|
if inner.finished {
|
|
1134
1131
|
return None;
|
|
@@ -1160,8 +1157,10 @@ pub fn reset_file_runtime() {
|
|
|
1160
1157
|
NEXT_FILE_REQUEST_ID.with(|slot| *slot.borrow_mut() = 1);
|
|
1161
1158
|
}
|
|
1162
1159
|
|
|
1163
|
-
#[no_mangle]
|
|
1164
|
-
|
|
1160
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1161
|
+
/// # Safety
|
|
1162
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1163
|
+
pub unsafe extern "C" fn __fui_on_file_pick_result(
|
|
1165
1164
|
request_id: u32,
|
|
1166
1165
|
status: u32,
|
|
1167
1166
|
payload_ptr: *const u8,
|
|
@@ -1191,8 +1190,10 @@ pub extern "C" fn __fui_on_file_pick_result(
|
|
|
1191
1190
|
);
|
|
1192
1191
|
}
|
|
1193
1192
|
|
|
1194
|
-
#[no_mangle]
|
|
1195
|
-
|
|
1193
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1194
|
+
/// # Safety
|
|
1195
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1196
|
+
pub unsafe extern "C" fn __fui_on_file_read_result(
|
|
1196
1197
|
request_id: u32,
|
|
1197
1198
|
status: u32,
|
|
1198
1199
|
offset_bytes: u64,
|
|
@@ -1227,8 +1228,10 @@ pub extern "C" fn __fui_on_file_read_result(
|
|
|
1227
1228
|
);
|
|
1228
1229
|
}
|
|
1229
1230
|
|
|
1230
|
-
#[no_mangle]
|
|
1231
|
-
|
|
1231
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1232
|
+
/// # Safety
|
|
1233
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1234
|
+
pub unsafe extern "C" fn __fui_on_file_save_result(
|
|
1232
1235
|
request_id: u32,
|
|
1233
1236
|
status: u32,
|
|
1234
1237
|
written_bytes: u64,
|
|
@@ -1264,8 +1267,10 @@ pub extern "C" fn __fui_on_file_save_result(
|
|
|
1264
1267
|
);
|
|
1265
1268
|
}
|
|
1266
1269
|
|
|
1267
|
-
#[no_mangle]
|
|
1268
|
-
|
|
1270
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1271
|
+
/// # Safety
|
|
1272
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1273
|
+
pub unsafe extern "C" fn __fui_on_file_writer_created(
|
|
1269
1274
|
request_id: u32,
|
|
1270
1275
|
status: u32,
|
|
1271
1276
|
payload_ptr: *const u8,
|
|
@@ -1301,8 +1306,10 @@ pub extern "C" fn __fui_on_file_writer_created(
|
|
|
1301
1306
|
);
|
|
1302
1307
|
}
|
|
1303
1308
|
|
|
1304
|
-
#[no_mangle]
|
|
1305
|
-
|
|
1309
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1310
|
+
/// # Safety
|
|
1311
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1312
|
+
pub unsafe extern "C" fn __fui_on_file_write_result(
|
|
1306
1313
|
request_id: u32,
|
|
1307
1314
|
status: u32,
|
|
1308
1315
|
written_bytes: u64,
|
|
@@ -1332,8 +1339,10 @@ pub extern "C" fn __fui_on_file_write_result(
|
|
|
1332
1339
|
);
|
|
1333
1340
|
}
|
|
1334
1341
|
|
|
1335
|
-
#[no_mangle]
|
|
1336
|
-
|
|
1342
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1343
|
+
/// # Safety
|
|
1344
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1345
|
+
pub unsafe extern "C" fn __fui_on_file_finish_result(
|
|
1337
1346
|
request_id: u32,
|
|
1338
1347
|
status: u32,
|
|
1339
1348
|
written_bytes: u64,
|
|
@@ -1370,8 +1379,10 @@ pub extern "C" fn __fui_on_file_finish_result(
|
|
|
1370
1379
|
);
|
|
1371
1380
|
}
|
|
1372
1381
|
|
|
1373
|
-
#[no_mangle]
|
|
1374
|
-
|
|
1382
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1383
|
+
/// # Safety
|
|
1384
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1385
|
+
pub unsafe extern "C" fn __fui_on_file_worker_process_progress(
|
|
1375
1386
|
request_id: u32,
|
|
1376
1387
|
processed_bytes: u64,
|
|
1377
1388
|
total_bytes: u64,
|
|
@@ -1398,8 +1409,10 @@ pub extern "C" fn __fui_on_file_worker_process_progress(
|
|
|
1398
1409
|
}
|
|
1399
1410
|
}
|
|
1400
1411
|
|
|
1401
|
-
#[no_mangle]
|
|
1402
|
-
|
|
1412
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1413
|
+
/// # Safety
|
|
1414
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1415
|
+
pub unsafe extern "C" fn __fui_on_file_worker_process_chunk(
|
|
1403
1416
|
request_id: u32,
|
|
1404
1417
|
offset_bytes: u64,
|
|
1405
1418
|
file_size_bytes: u64,
|
|
@@ -1426,8 +1439,10 @@ pub extern "C" fn __fui_on_file_worker_process_chunk(
|
|
|
1426
1439
|
}
|
|
1427
1440
|
}
|
|
1428
1441
|
|
|
1429
|
-
#[no_mangle]
|
|
1430
|
-
|
|
1442
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1443
|
+
/// # Safety
|
|
1444
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1445
|
+
pub unsafe extern "C" fn __fui_on_file_worker_process_complete(
|
|
1431
1446
|
request_id: u32,
|
|
1432
1447
|
processed_bytes: u64,
|
|
1433
1448
|
payload_ptr: *const u8,
|
|
@@ -1451,8 +1466,10 @@ pub extern "C" fn __fui_on_file_worker_process_complete(
|
|
|
1451
1466
|
}
|
|
1452
1467
|
}
|
|
1453
1468
|
|
|
1454
|
-
#[no_mangle]
|
|
1455
|
-
|
|
1469
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
1470
|
+
/// # Safety
|
|
1471
|
+
/// `payload_ptr` must be null for an empty payload or point to `payload_len` readable bytes.
|
|
1472
|
+
pub unsafe extern "C" fn __fui_on_file_worker_process_error(
|
|
1456
1473
|
request_id: u32,
|
|
1457
1474
|
status: u32,
|
|
1458
1475
|
payload_ptr: *const u8,
|
|
@@ -1513,12 +1530,14 @@ mod tests {
|
|
|
1513
1530
|
payload.extend_from_slice(b"note.txt");
|
|
1514
1531
|
payload.extend_from_slice(&10u32.to_le_bytes());
|
|
1515
1532
|
payload.extend_from_slice(b"text/plain");
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1533
|
+
unsafe {
|
|
1534
|
+
super::__fui_on_file_pick_result(
|
|
1535
|
+
1,
|
|
1536
|
+
FILE_STATUS_SUCCESS,
|
|
1537
|
+
payload.as_ptr(),
|
|
1538
|
+
payload.len() as u32,
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1522
1541
|
|
|
1523
1542
|
let picked = picked.borrow();
|
|
1524
1543
|
assert_eq!(picked.len(), 1);
|
|
@@ -1542,14 +1561,16 @@ mod tests {
|
|
|
1542
1561
|
assert!(calls.iter().any(|call| matches!(call, Call::FileReadChunk { request_id, file_id, offset_bytes, max_bytes } if *request_id == 1 && file_id == "picked-1" && *offset_bytes == 10 && *max_bytes == 8)));
|
|
1543
1562
|
|
|
1544
1563
|
let bytes = b"1234".to_vec();
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1564
|
+
unsafe {
|
|
1565
|
+
super::__fui_on_file_read_result(
|
|
1566
|
+
1,
|
|
1567
|
+
FILE_STATUS_SUCCESS,
|
|
1568
|
+
10,
|
|
1569
|
+
100,
|
|
1570
|
+
bytes.as_ptr(),
|
|
1571
|
+
bytes.len() as u32,
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1553
1574
|
let chunk = chunk.borrow().clone().expect("chunk");
|
|
1554
1575
|
assert_eq!(chunk.offset_bytes, 10);
|
|
1555
1576
|
assert_eq!(chunk.file_size_bytes, 100);
|
|
@@ -1585,12 +1606,14 @@ mod tests {
|
|
|
1585
1606
|
"writer-1",
|
|
1586
1607
|
Some("report.txt"),
|
|
1587
1608
|
);
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1609
|
+
unsafe {
|
|
1610
|
+
super::__fui_on_file_writer_created(
|
|
1611
|
+
1,
|
|
1612
|
+
FILE_STATUS_SUCCESS,
|
|
1613
|
+
payload.as_ptr(),
|
|
1614
|
+
payload.len() as u32,
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1594
1617
|
let writer = writer.borrow().clone().expect("writer");
|
|
1595
1618
|
assert_eq!(writer.file_name, "report.txt");
|
|
1596
1619
|
assert_eq!(writer.mode, FileSaveMode::NativePicker);
|
|
@@ -1604,13 +1627,15 @@ mod tests {
|
|
|
1604
1627
|
let calls = ffi::test::take_calls();
|
|
1605
1628
|
assert!(calls.iter().any(|call| matches!(call, Call::FileWriterFinish { request_id, writer_id } if *request_id == 2 && writer_id == "writer-1")));
|
|
1606
1629
|
let payload = writer_payload(FileSaveMode::NativePicker as u32, "report.txt", None);
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1630
|
+
unsafe {
|
|
1631
|
+
super::__fui_on_file_finish_result(
|
|
1632
|
+
2,
|
|
1633
|
+
FILE_STATUS_SUCCESS,
|
|
1634
|
+
44,
|
|
1635
|
+
payload.as_ptr(),
|
|
1636
|
+
payload.len() as u32,
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1614
1639
|
let finished = finished.borrow().clone().expect("finish");
|
|
1615
1640
|
assert_eq!(finished.file_name, "report.txt");
|
|
1616
1641
|
assert_eq!(finished.written_bytes, 44);
|
|
@@ -1657,18 +1682,22 @@ mod tests {
|
|
|
1657
1682
|
let calls = ffi::test::take_calls();
|
|
1658
1683
|
assert!(calls.iter().any(|call| matches!(call, Call::FileProcessWorkerStart { request_id, worker_wasm_path, worker_entry_name, file_id, suggested_name, chunk_bytes, save_to_picked_file } if *request_id == 1 && worker_wasm_path == "./workers/file_worker.wasm" && worker_entry_name == "entry" && file_id == "picked-1" && suggested_name == "copy.bin" && *chunk_bytes == 64 * 1024 && *save_to_picked_file)));
|
|
1659
1684
|
|
|
1660
|
-
|
|
1685
|
+
unsafe {
|
|
1686
|
+
super::__fui_on_file_worker_process_progress(1, 20, 100, b"copy.bin".as_ptr(), 8);
|
|
1687
|
+
}
|
|
1661
1688
|
let progress = progress.borrow().clone().expect("progress");
|
|
1662
1689
|
assert_eq!(progress.processed_bytes, 20);
|
|
1663
1690
|
assert_eq!(progress.output_file_name, Some("copy.bin".to_string()));
|
|
1664
1691
|
|
|
1665
1692
|
let payload = b"copy.bin\0sha256".to_vec();
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1693
|
+
unsafe {
|
|
1694
|
+
super::__fui_on_file_worker_process_complete(
|
|
1695
|
+
1,
|
|
1696
|
+
100,
|
|
1697
|
+
payload.as_ptr(),
|
|
1698
|
+
payload.len() as u32,
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1672
1701
|
let result = result.borrow().clone().expect("result");
|
|
1673
1702
|
assert_eq!(result.processed_bytes, 100);
|
|
1674
1703
|
assert_eq!(result.output_file_name, Some("copy.bin".to_string()));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#[must_use = "the host-event handler is removed when the subscription is dropped"]
|
|
2
|
+
pub struct HostEventSubscription {
|
|
3
|
+
unsubscribe: Option<Box<dyn FnOnce()>>,
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
impl HostEventSubscription {
|
|
7
|
+
#[doc(hidden)]
|
|
8
|
+
pub fn new(unsubscribe: impl FnOnce() + 'static) -> Self {
|
|
9
|
+
Self {
|
|
10
|
+
unsubscribe: Some(Box::new(unsubscribe)),
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
impl Drop for HostEventSubscription {
|
|
16
|
+
fn drop(&mut self) {
|
|
17
|
+
if let Some(unsubscribe) = self.unsubscribe.take() {
|
|
18
|
+
unsubscribe();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[cfg(test)]
|
|
24
|
+
mod tests {
|
|
25
|
+
use super::*;
|
|
26
|
+
use std::cell::Cell;
|
|
27
|
+
use std::rc::Rc;
|
|
28
|
+
|
|
29
|
+
#[test]
|
|
30
|
+
fn dropping_host_event_subscription_runs_unsubscribe_once() {
|
|
31
|
+
let unsubscribe_count = Rc::new(Cell::new(0));
|
|
32
|
+
let captured_count = unsubscribe_count.clone();
|
|
33
|
+
let subscription = HostEventSubscription::new(move || {
|
|
34
|
+
captured_count.set(captured_count.get() + 1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
drop(subscription);
|
|
38
|
+
|
|
39
|
+
assert_eq!(unsubscribe_count.get(), 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/lib.rs
CHANGED
|
@@ -24,6 +24,7 @@ pub mod frame_scheduler;
|
|
|
24
24
|
pub mod frame_signal;
|
|
25
25
|
#[doc(hidden)]
|
|
26
26
|
pub mod generated;
|
|
27
|
+
pub mod host_events;
|
|
27
28
|
pub mod host_services;
|
|
28
29
|
pub mod image_sampling;
|
|
29
30
|
pub(crate) mod keyboard_scroll;
|
|
@@ -77,6 +78,46 @@ macro_rules! fui_app {
|
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
#[cfg(feature = "worker-runtime")]
|
|
82
|
+
#[macro_export]
|
|
83
|
+
macro_rules! fui_worker {
|
|
84
|
+
($($entry:ident => $job:ty),+ $(,)?) => {
|
|
85
|
+
$(
|
|
86
|
+
#[doc = "Worker entrypoint generated by `fui_worker!`.\n\n# Safety\n`input_ptr` must reference `input_len` readable bytes when `input_len` is non-zero."]
|
|
87
|
+
#[no_mangle]
|
|
88
|
+
pub unsafe extern "C" fn $entry(input_ptr: usize, input_len: u32) {
|
|
89
|
+
::std::thread_local! {
|
|
90
|
+
static ACTIVE_JOB: ::std::cell::RefCell<Option<$job>> =
|
|
91
|
+
const { ::std::cell::RefCell::new(None) };
|
|
92
|
+
}
|
|
93
|
+
let input = unsafe {
|
|
94
|
+
$crate::WorkerRuntime::entry_input(input_ptr, input_len)
|
|
95
|
+
};
|
|
96
|
+
ACTIVE_JOB.with(|slot| {
|
|
97
|
+
let mut active = slot.borrow_mut();
|
|
98
|
+
if active.is_none() {
|
|
99
|
+
$crate::worker_runtime::reset_worker_runtime();
|
|
100
|
+
}
|
|
101
|
+
let mut job = active.take().unwrap_or_default();
|
|
102
|
+
if $crate::WorkerJob::resume(&mut job, input) {
|
|
103
|
+
*active = Some(job);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
)+
|
|
108
|
+
|
|
109
|
+
#[no_mangle]
|
|
110
|
+
pub extern "C" fn __fui_worker_text_buffer() -> usize {
|
|
111
|
+
$crate::worker_runtime::worker_text_buffer_ptr()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#[no_mangle]
|
|
115
|
+
pub extern "C" fn __fui_worker_text_buffer_size() -> u32 {
|
|
116
|
+
$crate::worker_runtime::worker_text_buffer_size()
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
80
121
|
#[macro_export]
|
|
81
122
|
macro_rules! fui_managed_app {
|
|
82
123
|
($page_ty:ty, $build_page:expr, $get_root:expr) => {
|
|
@@ -435,6 +476,9 @@ pub mod prelude {
|
|
|
435
476
|
pub use crate::focus_visibility::show_keyboard_focus_for_key_event;
|
|
436
477
|
pub use crate::frame_scheduler::{mark_needs_commit, on_loaded, LoadedEventArgs};
|
|
437
478
|
pub use crate::fui_component;
|
|
479
|
+
#[cfg(feature = "worker-runtime")]
|
|
480
|
+
pub use crate::fui_worker;
|
|
481
|
+
pub use crate::host_events::HostEventSubscription;
|
|
438
482
|
pub use crate::image_sampling::{ImageSampling, ImageSamplingMode};
|
|
439
483
|
pub use crate::logger;
|
|
440
484
|
pub use crate::navigation;
|
|
@@ -442,9 +486,10 @@ pub mod prelude {
|
|
|
442
486
|
auto, column, custom_drawable, fill, flex_box, grid, image, pct, portal, px, row,
|
|
443
487
|
scroll_box, scroll_view, svg, text, viewport_height, viewport_width, virtual_list, Border,
|
|
444
488
|
Child, ContextMenuEventArgs, Corners, CustomDrawable, DrawableInvalidator, EdgeInsets,
|
|
445
|
-
FlexBox, FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode,
|
|
446
|
-
|
|
447
|
-
Shadow, Svg, SvgNode, Text, TextCore, TextNode,
|
|
489
|
+
FlexBox, FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode,
|
|
490
|
+
Length, Node, PresenterHostStyle, ScrollBar, ScrollBarStyle, ScrollBarVisibility,
|
|
491
|
+
ScrollBox, ScrollState, ScrollView, Shadow, Svg, SvgNode, Text, TextCore, TextNode,
|
|
492
|
+
ThemeBindable, VirtualList,
|
|
448
493
|
};
|
|
449
494
|
pub use crate::persisted;
|
|
450
495
|
pub use crate::platform;
|
|
@@ -553,6 +598,7 @@ pub use file::{
|
|
|
553
598
|
pub use focus_visibility::show_keyboard_focus_for_key_event;
|
|
554
599
|
pub use frame_scheduler::{mark_needs_commit, on_loaded, LoadedEventArgs};
|
|
555
600
|
pub use frame_signal::{frame_time_signal, FrameTimeSignalHandle};
|
|
601
|
+
pub use host_events::HostEventSubscription;
|
|
556
602
|
pub use image_sampling::{ImageSampling, ImageSamplingMode};
|
|
557
603
|
pub use logger::*;
|
|
558
604
|
pub use navigation::*;
|
|
@@ -561,8 +607,8 @@ pub use node::{
|
|
|
561
607
|
scroll_view, svg, text, viewport_height, viewport_width, virtual_list, Border, Child,
|
|
562
608
|
ContextMenuEventArgs, Corners, CustomDrawable, DrawableInvalidator, EdgeInsets, FlexBox,
|
|
563
609
|
FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
|
|
564
|
-
PresenterHostStyle, ScrollBar, ScrollBarVisibility, ScrollBox, ScrollState,
|
|
565
|
-
Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
|
|
610
|
+
PresenterHostStyle, ScrollBar, ScrollBarStyle, ScrollBarVisibility, ScrollBox, ScrollState,
|
|
611
|
+
ScrollView, Shadow, Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
|
|
566
612
|
};
|
|
567
613
|
pub use persisted::*;
|
|
568
614
|
pub use platform::*;
|
|
@@ -592,8 +638,5 @@ pub use worker::{Worker, WorkerCompletedEventArgs, WorkerErrorEventArgs, WorkerP
|
|
|
592
638
|
pub use worker_job::{WorkerJob, WorkerJobState};
|
|
593
639
|
#[cfg(feature = "worker-runtime")]
|
|
594
640
|
pub use worker_runtime::{
|
|
595
|
-
file_read_chunk, file_worker_write_chunk,
|
|
596
|
-
handle_fetch_error as __fui_on_fetch_error, reset_worker_runtime,
|
|
597
|
-
worker_text_buffer_ptr as __fui_worker_text_buffer,
|
|
598
|
-
worker_text_buffer_size as __fui_worker_text_buffer_size, WorkerRuntime,
|
|
641
|
+
file_read_chunk, file_worker_write_chunk, reset_worker_runtime, WorkerRuntime,
|
|
599
642
|
};
|