Remove native and web features from dioxus-html (#3006)
* remove native and web features from dioxus-html * Fix downcasting web events * Fix desktop with tokio disabled
This commit is contained in:
1 parent
677e0e7f4f
commit
cda8bb24f1
54 files changed
+1768
-1512
No files matched your search
@@ -1,50 +0,0 @@
|
||||
/// Code for the Dioxus channel used to communicate between the dioxus and javascript code
|
||||
#[cfg(feature = "native-bind")]
|
||||
pub const NATIVE_EVAL_JS: &str = include_str!("../js/native_eval.js");
|
||||
|
||||
#[cfg(feature = "wasm-bind")]
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
pub struct JSOwner {
|
||||
_owner: Box<dyn std::any::Any>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-bind")]
|
||||
impl JSOwner {
|
||||
pub fn new(owner: impl std::any::Any) -> Self {
|
||||
Self {
|
||||
_owner: Box::new(owner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-bind")]
|
||||
#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")]
|
||||
extern "C" {
|
||||
pub type WebDioxusChannel;
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(owner: JSOwner) -> WebDioxusChannel;
|
||||
|
||||
#[wasm_bindgen(method, js_name = "rustSend")]
|
||||
pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
|
||||
|
||||
#[wasm_bindgen(method, js_name = "rustRecv")]
|
||||
pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel;
|
||||
|
||||
pub type WeakDioxusChannel;
|
||||
|
||||
#[wasm_bindgen(method, js_name = "rustSend")]
|
||||
pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue);
|
||||
|
||||
#[wasm_bindgen(method, js_name = "rustRecv")]
|
||||
pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue;
|
||||
}
|
||||
@@ -7,9 +7,7 @@ use std::{
|
||||
|
||||
use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
|
||||
|
||||
mod bindings;
|
||||
#[allow(unused)]
|
||||
pub use bindings::*;
|
||||
mod eval;
|
||||
pub use eval::*;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Handles querying data from the renderer
|
||||
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
fmt::{Debug, Display, Formatter},
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
};
|
||||
@@ -70,6 +70,12 @@ pub struct MountedData {
|
||||
inner: Box<dyn RenderedElementBacking>,
|
||||
}
|
||||
|
||||
impl Debug for MountedData {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MountedData").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: RenderedElementBacking> From<E> for MountedData {
|
||||
fn from(e: E) -> Self {
|
||||
Self { inner: Box::new(e) }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel,WeakDioxusChannel,DioxusChannel,Channel};
|
||||
@@ -1 +1 @@
|
||||
[10372071913661173523, 8375185156499858125, 4813754958077120784]
|
||||
[206827801705263822, 8375185156499858125]
|
||||
@@ -1 +0,0 @@
|
||||
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class QueryParams{id;data;constructor(id,method,data){this.id=id,this.data={method,data}}}window.__msg_queues=window.__msg_queues||[];window.finalizationRegistry=window.finalizationRegistry||new FinalizationRegistry(({id})=>{window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(id,"drop")}))});window.getQuery=function(request_id){return window.__msg_queues[request_id]};window.createQuery=function(request_id){return new NativeDioxusChannel(request_id)};class NativeDioxusChannel extends DioxusChannel{rust_to_js;request_id;constructor(request_id){super();this.rust_to_js=new Channel,this.request_id=request_id,window.__msg_queues[request_id]=this.weak(),window.finalizationRegistry.register(this,{id:request_id})}async recv(){return await this.rust_to_js.recv()}send(data){window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(this.request_id,"send",data)}))}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){}}export{NativeDioxusChannel};
|
||||
@@ -27,14 +27,8 @@ pub use file_data::*;
|
||||
mod attribute_groups;
|
||||
pub mod geometry;
|
||||
pub mod input_data;
|
||||
#[cfg(feature = "native-bind")]
|
||||
pub mod native_bind;
|
||||
pub mod point_interaction;
|
||||
mod render_template;
|
||||
#[cfg(feature = "wasm-bind")]
|
||||
mod web_sys_bind;
|
||||
#[cfg(feature = "wasm-bind")]
|
||||
pub use web_sys_bind::*;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
mod transit;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
mod native_file_engine;
|
||||
|
||||
pub use native_file_engine::*;
|
||||
@@ -1,56 +0,0 @@
|
||||
use std::any::Any;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::file_data::FileEngine;
|
||||
|
||||
pub struct NativeFileEngine {
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl NativeFileEngine {
|
||||
pub fn new(files: Vec<PathBuf>) -> Self {
|
||||
Self { files }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "file_engine")]
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FileEngine for NativeFileEngine {
|
||||
fn files(&self) -> Vec<String> {
|
||||
self.files
|
||||
.iter()
|
||||
.filter_map(|f| Some(f.to_str()?.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn file_size(&self, file: &str) -> Option<u64> {
|
||||
let file = File::open(file).await.ok()?;
|
||||
Some(file.metadata().await.ok()?.len())
|
||||
}
|
||||
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||
let mut file = File::open(file).await.ok()?;
|
||||
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents).await.ok()?;
|
||||
|
||||
Some(contents)
|
||||
}
|
||||
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||
let mut file = File::open(file).await.ok()?;
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).await.ok()?;
|
||||
|
||||
Some(contents)
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
let file = File::open(file).await.ok()?;
|
||||
Some(Box::new(file))
|
||||
}
|
||||
}
|
||||
@@ -68,41 +68,3 @@ export abstract class DioxusChannel {
|
||||
// Receive data sent from javascript in rust
|
||||
abstract rustRecv(): Promise<any>;
|
||||
}
|
||||
|
||||
export class WebDioxusChannel extends DioxusChannel {
|
||||
js_to_rust: Channel;
|
||||
rust_to_js: Channel;
|
||||
owner: any;
|
||||
|
||||
constructor(owner: any) {
|
||||
super();
|
||||
this.owner = owner;
|
||||
this.js_to_rust = new Channel();
|
||||
this.rust_to_js = new Channel();
|
||||
}
|
||||
|
||||
// Return a weak reference to this channel
|
||||
weak(): WeakDioxusChannel {
|
||||
return new WeakDioxusChannel(this);
|
||||
}
|
||||
|
||||
// Receive message from Rust
|
||||
async recv() {
|
||||
return await this.rust_to_js.recv();
|
||||
}
|
||||
|
||||
// Send message to rust.
|
||||
send(data: any) {
|
||||
this.js_to_rust.send(data);
|
||||
}
|
||||
|
||||
// Send data from rust to javascript
|
||||
rustSend(data: any) {
|
||||
this.rust_to_js.send(data);
|
||||
}
|
||||
|
||||
// Receive data sent from javascript in rust
|
||||
async rustRecv(): Promise<any> {
|
||||
return await this.js_to_rust.recv();
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import { Channel, DioxusChannel, WeakDioxusChannel } from "./eval";
|
||||
|
||||
// In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser
|
||||
declare global {
|
||||
interface Window {
|
||||
__msg_queues: WeakDioxusChannel[];
|
||||
finalizationRegistry: FinalizationRegistry<{ id: number }>;
|
||||
|
||||
getQuery(request_id: number): WeakDioxusChannel;
|
||||
|
||||
createQuery(request_id: number): NativeDioxusChannel;
|
||||
}
|
||||
}
|
||||
|
||||
// A message that can be sent to the desktop renderer about a query
|
||||
class QueryParams {
|
||||
id: number;
|
||||
data: { method: "drop" | "send"; data?: any };
|
||||
|
||||
constructor(id: number, method: "drop" | "send", data?: any) {
|
||||
this.id = id;
|
||||
this.data = { method, data };
|
||||
}
|
||||
}
|
||||
|
||||
window.__msg_queues = window.__msg_queues || [];
|
||||
// In dioxus desktop, eval is copy so we cannot run a drop handler. Instead, the drop handler is run after the channel is garbage collected in the javascript side
|
||||
window.finalizationRegistry =
|
||||
window.finalizationRegistry ||
|
||||
new FinalizationRegistry(({ id }) => {
|
||||
// @ts-ignore - wry gives us this
|
||||
window.ipc.postMessage(
|
||||
JSON.stringify({
|
||||
method: "query",
|
||||
params: new QueryParams(id, "drop"),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Get a query from the global state
|
||||
window.getQuery = function (request_id: number): WeakDioxusChannel {
|
||||
return window.__msg_queues[request_id];
|
||||
};
|
||||
|
||||
// Create a new query (and insert it into the global state)
|
||||
window.createQuery = function (request_id: number): NativeDioxusChannel {
|
||||
return new NativeDioxusChannel(request_id);
|
||||
};
|
||||
|
||||
export class NativeDioxusChannel extends DioxusChannel {
|
||||
rust_to_js: Channel;
|
||||
request_id: number;
|
||||
|
||||
constructor(request_id: number) {
|
||||
super();
|
||||
this.rust_to_js = new Channel();
|
||||
this.request_id = request_id;
|
||||
|
||||
window.__msg_queues[request_id] = this.weak();
|
||||
window.finalizationRegistry.register(this, { id: request_id });
|
||||
}
|
||||
|
||||
// Receive message from Rust
|
||||
async recv() {
|
||||
return await this.rust_to_js.recv();
|
||||
}
|
||||
|
||||
// Send message to rust.
|
||||
send(data: any) {
|
||||
// @ts-ignore - wry gives us this
|
||||
window.ipc.postMessage(
|
||||
JSON.stringify({
|
||||
method: "query",
|
||||
params: new QueryParams(this.request_id, "send", data),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Send data from rust to javascript
|
||||
rustSend(data: any) {
|
||||
this.rust_to_js.send(data);
|
||||
}
|
||||
|
||||
// Receive data sent from javascript in rust. This is a no-op in the native interpreter because the rust code runs remotely
|
||||
async rustRecv(): Promise<any> {}
|
||||
}
|
||||
@@ -1,675 +0,0 @@
|
||||
use crate::events::HasKeyboardData;
|
||||
use crate::events::{
|
||||
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
|
||||
TransitionData, WheelData,
|
||||
};
|
||||
use crate::file_data::HasFileData;
|
||||
use crate::geometry::PixelsSize;
|
||||
use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
|
||||
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
||||
use crate::prelude::*;
|
||||
use keyboard_types::{Code, Key, Modifiers};
|
||||
use std::str::FromStr;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{js_sys, ResizeObserverEntry};
|
||||
use web_sys::{
|
||||
AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent,
|
||||
Touch, TouchEvent, TransitionEvent, WheelEvent,
|
||||
};
|
||||
|
||||
macro_rules! uncheck_convert {
|
||||
($t:ty, $d:ty) => {
|
||||
impl From<Event> for $d {
|
||||
#[inline]
|
||||
fn from(e: Event) -> Self {
|
||||
let e: $t = e.unchecked_into();
|
||||
Self::from(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Event> for $d {
|
||||
#[inline]
|
||||
fn from(e: &Event) -> Self {
|
||||
let e: &$t = e.unchecked_ref();
|
||||
Self::from(e.clone())
|
||||
}
|
||||
}
|
||||
};
|
||||
($($t:ty => $d:ty),+ $(,)?) => {
|
||||
$(uncheck_convert!($t, $d);)+
|
||||
};
|
||||
}
|
||||
|
||||
uncheck_convert![
|
||||
web_sys::CompositionEvent => CompositionData,
|
||||
web_sys::KeyboardEvent => KeyboardData,
|
||||
web_sys::MouseEvent => MouseData,
|
||||
web_sys::TouchEvent => TouchData,
|
||||
web_sys::PointerEvent => PointerData,
|
||||
web_sys::WheelEvent => WheelData,
|
||||
web_sys::AnimationEvent => AnimationData,
|
||||
web_sys::TransitionEvent => TransitionData,
|
||||
web_sys::MouseEvent => DragData,
|
||||
web_sys::FocusEvent => FocusData,
|
||||
];
|
||||
|
||||
impl From<Event> for ResizeData {
|
||||
#[inline]
|
||||
fn from(e: Event) -> Self {
|
||||
<ResizeData as From<&Event>>::from(&e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Event> for ResizeData {
|
||||
#[inline]
|
||||
fn from(e: &Event) -> Self {
|
||||
let e: &CustomEvent = e.unchecked_ref();
|
||||
let value = e.detail();
|
||||
Self::from(value.unchecked_into::<ResizeObserverEntry>())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasCompositionData for CompositionEvent {
|
||||
fn data(&self) -> std::string::String {
|
||||
self.data().unwrap_or_default()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasKeyboardData for KeyboardEvent {
|
||||
fn key(&self) -> Key {
|
||||
Key::from_str(self.key().as_str()).unwrap_or(Key::Unidentified)
|
||||
}
|
||||
|
||||
fn code(&self) -> Code {
|
||||
Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified)
|
||||
}
|
||||
|
||||
fn location(&self) -> keyboard_types::Location {
|
||||
decode_key_location(self.location() as usize)
|
||||
}
|
||||
|
||||
fn is_auto_repeating(&self) -> bool {
|
||||
self.repeat()
|
||||
}
|
||||
|
||||
fn is_composing(&self) -> bool {
|
||||
self.is_composing()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for KeyboardEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
|
||||
if self.alt_key() {
|
||||
modifiers.insert(Modifiers::ALT);
|
||||
}
|
||||
if self.ctrl_key() {
|
||||
modifiers.insert(Modifiers::CONTROL);
|
||||
}
|
||||
if self.meta_key() {
|
||||
modifiers.insert(Modifiers::META);
|
||||
}
|
||||
if self.shift_key() {
|
||||
modifiers.insert(Modifiers::SHIFT);
|
||||
}
|
||||
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionLocation for MouseEvent {
|
||||
fn client_coordinates(&self) -> ClientPoint {
|
||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||
}
|
||||
|
||||
fn page_coordinates(&self) -> PagePoint {
|
||||
PagePoint::new(self.page_x().into(), self.page_y().into())
|
||||
}
|
||||
|
||||
fn screen_coordinates(&self) -> ScreenPoint {
|
||||
ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionElementOffset for MouseEvent {
|
||||
fn element_coordinates(&self) -> ElementPoint {
|
||||
ElementPoint::new(self.offset_x().into(), self.offset_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for MouseEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
|
||||
if self.alt_key() {
|
||||
modifiers.insert(Modifiers::ALT);
|
||||
}
|
||||
if self.ctrl_key() {
|
||||
modifiers.insert(Modifiers::CONTROL);
|
||||
}
|
||||
if self.meta_key() {
|
||||
modifiers.insert(Modifiers::META);
|
||||
}
|
||||
if self.shift_key() {
|
||||
modifiers.insert(Modifiers::SHIFT);
|
||||
}
|
||||
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerInteraction for MouseEvent {
|
||||
fn held_buttons(&self) -> crate::input_data::MouseButtonSet {
|
||||
decode_mouse_button_set(self.buttons())
|
||||
}
|
||||
|
||||
fn trigger_button(&self) -> Option<MouseButton> {
|
||||
Some(MouseButton::from_web_code(self.button()))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasMouseData for MouseEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFileData for MouseEvent {}
|
||||
|
||||
impl HasDragData for MouseEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for TouchEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
|
||||
if self.alt_key() {
|
||||
modifiers.insert(Modifiers::ALT);
|
||||
}
|
||||
if self.ctrl_key() {
|
||||
modifiers.insert(Modifiers::CONTROL);
|
||||
}
|
||||
if self.meta_key() {
|
||||
modifiers.insert(Modifiers::META);
|
||||
}
|
||||
if self.shift_key() {
|
||||
modifiers.insert(Modifiers::SHIFT);
|
||||
}
|
||||
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::events::HasTouchData for TouchEvent {
|
||||
fn touches(&self) -> Vec<TouchPoint> {
|
||||
let touches = TouchEvent::touches(self);
|
||||
let mut result = Vec::with_capacity(touches.length() as usize);
|
||||
for i in 0..touches.length() {
|
||||
let touch = touches.get(i).unwrap();
|
||||
result.push(TouchPoint::new(touch));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn touches_changed(&self) -> Vec<TouchPoint> {
|
||||
let touches = self.changed_touches();
|
||||
let mut result = Vec::with_capacity(touches.length() as usize);
|
||||
for i in 0..touches.length() {
|
||||
let touch = touches.get(i).unwrap();
|
||||
result.push(TouchPoint::new(touch));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn target_touches(&self) -> Vec<TouchPoint> {
|
||||
let touches = self.target_touches();
|
||||
let mut result = Vec::with_capacity(touches.length() as usize);
|
||||
for i in 0..touches.length() {
|
||||
let touch = touches.get(i).unwrap();
|
||||
result.push(TouchPoint::new(touch));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTouchPointData for Touch {
|
||||
fn identifier(&self) -> i32 {
|
||||
self.identifier()
|
||||
}
|
||||
|
||||
fn radius(&self) -> ScreenPoint {
|
||||
ScreenPoint::new(self.radius_x().into(), self.radius_y().into())
|
||||
}
|
||||
|
||||
fn rotation(&self) -> f64 {
|
||||
self.rotation_angle() as f64
|
||||
}
|
||||
|
||||
fn force(&self) -> f64 {
|
||||
self.force() as f64
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionLocation for Touch {
|
||||
fn client_coordinates(&self) -> ClientPoint {
|
||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||
}
|
||||
|
||||
fn screen_coordinates(&self) -> ScreenPoint {
|
||||
ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
|
||||
}
|
||||
|
||||
fn page_coordinates(&self) -> PagePoint {
|
||||
PagePoint::new(self.page_x().into(), self.page_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasPointerData for PointerEvent {
|
||||
fn pointer_id(&self) -> i32 {
|
||||
self.pointer_id()
|
||||
}
|
||||
|
||||
fn width(&self) -> i32 {
|
||||
self.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.height()
|
||||
}
|
||||
|
||||
fn pressure(&self) -> f32 {
|
||||
self.pressure()
|
||||
}
|
||||
|
||||
fn tangential_pressure(&self) -> f32 {
|
||||
self.tangential_pressure()
|
||||
}
|
||||
|
||||
fn tilt_x(&self) -> i32 {
|
||||
self.tilt_x()
|
||||
}
|
||||
|
||||
fn tilt_y(&self) -> i32 {
|
||||
self.tilt_y()
|
||||
}
|
||||
|
||||
fn twist(&self) -> i32 {
|
||||
self.twist()
|
||||
}
|
||||
|
||||
fn pointer_type(&self) -> String {
|
||||
self.pointer_type()
|
||||
}
|
||||
|
||||
fn is_primary(&self) -> bool {
|
||||
self.is_primary()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionLocation for PointerEvent {
|
||||
fn client_coordinates(&self) -> ClientPoint {
|
||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||
}
|
||||
|
||||
fn screen_coordinates(&self) -> ScreenPoint {
|
||||
ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
|
||||
}
|
||||
|
||||
fn page_coordinates(&self) -> PagePoint {
|
||||
PagePoint::new(self.page_x().into(), self.page_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionElementOffset for PointerEvent {
|
||||
fn element_coordinates(&self) -> ElementPoint {
|
||||
ElementPoint::new(self.offset_x().into(), self.offset_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for PointerEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
|
||||
if self.alt_key() {
|
||||
modifiers.insert(Modifiers::ALT);
|
||||
}
|
||||
if self.ctrl_key() {
|
||||
modifiers.insert(Modifiers::CONTROL);
|
||||
}
|
||||
if self.meta_key() {
|
||||
modifiers.insert(Modifiers::META);
|
||||
}
|
||||
if self.shift_key() {
|
||||
modifiers.insert(Modifiers::SHIFT);
|
||||
}
|
||||
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerInteraction for PointerEvent {
|
||||
fn held_buttons(&self) -> crate::input_data::MouseButtonSet {
|
||||
decode_mouse_button_set(self.buttons())
|
||||
}
|
||||
|
||||
fn trigger_button(&self) -> Option<MouseButton> {
|
||||
Some(MouseButton::from_web_code(self.button()))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasWheelData for WheelEvent {
|
||||
fn delta(&self) -> crate::geometry::WheelDelta {
|
||||
crate::geometry::WheelDelta::from_web_attributes(
|
||||
self.delta_mode(),
|
||||
self.delta_x(),
|
||||
self.delta_y(),
|
||||
self.delta_z(),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasMouseData for WheelEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionLocation for WheelEvent {
|
||||
fn client_coordinates(&self) -> ClientPoint {
|
||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||
}
|
||||
|
||||
fn screen_coordinates(&self) -> ScreenPoint {
|
||||
ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
|
||||
}
|
||||
|
||||
fn page_coordinates(&self) -> PagePoint {
|
||||
PagePoint::new(self.page_x().into(), self.page_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractionElementOffset for WheelEvent {
|
||||
fn element_coordinates(&self) -> ElementPoint {
|
||||
ElementPoint::new(self.offset_x().into(), self.offset_y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for WheelEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
|
||||
if self.alt_key() {
|
||||
modifiers.insert(Modifiers::ALT);
|
||||
}
|
||||
if self.ctrl_key() {
|
||||
modifiers.insert(Modifiers::CONTROL);
|
||||
}
|
||||
if self.meta_key() {
|
||||
modifiers.insert(Modifiers::META);
|
||||
}
|
||||
if self.shift_key() {
|
||||
modifiers.insert(Modifiers::SHIFT);
|
||||
}
|
||||
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerInteraction for WheelEvent {
|
||||
fn held_buttons(&self) -> crate::input_data::MouseButtonSet {
|
||||
decode_mouse_button_set(self.buttons())
|
||||
}
|
||||
|
||||
fn trigger_button(&self) -> Option<MouseButton> {
|
||||
Some(MouseButton::from_web_code(self.button()))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasAnimationData for AnimationEvent {
|
||||
fn animation_name(&self) -> String {
|
||||
self.animation_name()
|
||||
}
|
||||
|
||||
fn pseudo_element(&self) -> String {
|
||||
self.pseudo_element()
|
||||
}
|
||||
|
||||
fn elapsed_time(&self) -> f32 {
|
||||
self.elapsed_time()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTransitionData for TransitionEvent {
|
||||
fn elapsed_time(&self) -> f32 {
|
||||
self.elapsed_time()
|
||||
}
|
||||
|
||||
fn property_name(&self) -> String {
|
||||
self.property_name()
|
||||
}
|
||||
|
||||
fn pseudo_element(&self) -> String {
|
||||
self.pseudo_element()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mounted")]
|
||||
impl From<&web_sys::Element> for MountedData {
|
||||
fn from(e: &web_sys::Element) -> Self {
|
||||
MountedData::new(e.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mounted")]
|
||||
impl crate::RenderedElementBacking for web_sys::Element {
|
||||
fn get_scroll_offset(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<
|
||||
dyn std::future::Future<Output = crate::MountedResult<crate::geometry::PixelsVector2D>>,
|
||||
>,
|
||||
> {
|
||||
let left = self.scroll_left();
|
||||
let top = self.scroll_top();
|
||||
let result = Ok(crate::geometry::PixelsVector2D::new(
|
||||
left as f64,
|
||||
top as f64,
|
||||
));
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
fn get_scroll_size(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = crate::MountedResult<crate::geometry::PixelsSize>>>,
|
||||
> {
|
||||
let width = self.scroll_width();
|
||||
let height = self.scroll_height();
|
||||
let result = Ok(crate::geometry::PixelsSize::new(
|
||||
width as f64,
|
||||
height as f64,
|
||||
));
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
fn get_client_rect(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = crate::MountedResult<crate::geometry::PixelsRect>>>,
|
||||
> {
|
||||
let rect = self.get_bounding_client_rect();
|
||||
let result = Ok(crate::geometry::PixelsRect::new(
|
||||
euclid::Point2D::new(rect.left(), rect.top()),
|
||||
euclid::Size2D::new(rect.width(), rect.height()),
|
||||
));
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn scroll_to(
|
||||
&self,
|
||||
behavior: crate::ScrollBehavior,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::MountedResult<()>>>> {
|
||||
let options = web_sys::ScrollIntoViewOptions::new();
|
||||
match behavior {
|
||||
crate::ScrollBehavior::Instant => {
|
||||
options.set_behavior(web_sys::ScrollBehavior::Instant);
|
||||
}
|
||||
crate::ScrollBehavior::Smooth => {
|
||||
options.set_behavior(web_sys::ScrollBehavior::Smooth);
|
||||
}
|
||||
}
|
||||
self.scroll_into_view_with_scroll_into_view_options(&options);
|
||||
|
||||
Box::pin(async { Ok(()) })
|
||||
}
|
||||
|
||||
fn set_focus(
|
||||
&self,
|
||||
focus: bool,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::MountedResult<()>>>> {
|
||||
#[derive(Debug)]
|
||||
struct FocusError(wasm_bindgen::JsValue);
|
||||
|
||||
impl std::fmt::Display for FocusError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "failed to focus element {:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for FocusError {}
|
||||
|
||||
let result = self
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.ok_or_else(|| crate::MountedError::OperationFailed(Box::new(FocusError(self.into()))))
|
||||
.and_then(|e| {
|
||||
(if focus { e.focus() } else { e.blur() })
|
||||
.map_err(|err| crate::MountedError::OperationFailed(Box::new(FocusError(err))))
|
||||
});
|
||||
Box::pin(async { result })
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult<PixelsSize> {
|
||||
let first = resize_observer_output.get(0);
|
||||
let size = first.unchecked_into::<web_sys::ResizeObserverSize>();
|
||||
|
||||
// inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise
|
||||
let inline_size = size.inline_size();
|
||||
// block_size matches the height of the element if its writing-mode is horizontal, the width otherwise
|
||||
let block_size = size.block_size();
|
||||
|
||||
Ok(PixelsSize::new(inline_size, block_size))
|
||||
}
|
||||
|
||||
impl HasResizeData for ResizeObserverEntry {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
extract_first_size(self.border_box_size())
|
||||
}
|
||||
|
||||
fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
extract_first_size(self.content_box_size())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasScrollData for Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasClipboardData for Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Event> for ClipboardData {
|
||||
fn from(e: &Event) -> Self {
|
||||
ClipboardData::new(e.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFocusData for web_sys::FocusEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasToggleData for web_sys::Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSelectionData for web_sys::Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasMediaData for web_sys::Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFileData for web_sys::Event {
|
||||
#[cfg(feature = "file_engine")]
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn crate::file_data::FileEngine>> {
|
||||
let files = self
|
||||
.dyn_ref()
|
||||
.and_then(|input: &web_sys::HtmlInputElement| {
|
||||
input.files().and_then(|files| {
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
crate::web_sys_bind::file_engine::WebFileEngine::new(files)
|
||||
.map(|f| std::sync::Arc::new(f) as std::sync::Arc<dyn crate::FileEngine>)
|
||||
})
|
||||
});
|
||||
|
||||
files
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
use std::any::Any;
|
||||
|
||||
use crate::FileEngine;
|
||||
use futures_channel::oneshot;
|
||||
use js_sys::Uint8Array;
|
||||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use web_sys::{File, FileList, FileReader};
|
||||
|
||||
/// A file engine for the web platform
|
||||
pub struct WebFileEngine {
|
||||
file_reader: FileReader,
|
||||
file_list: FileList,
|
||||
}
|
||||
|
||||
impl WebFileEngine {
|
||||
/// Create a new file engine from a file list
|
||||
pub fn new(file_list: FileList) -> Option<Self> {
|
||||
Some(Self {
|
||||
file_list,
|
||||
file_reader: FileReader::new().ok()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.file_list.length() as usize
|
||||
}
|
||||
|
||||
fn get(&self, index: usize) -> Option<File> {
|
||||
self.file_list.item(index as u32)
|
||||
}
|
||||
|
||||
fn find(&self, name: &str) -> Option<File> {
|
||||
(0..self.len())
|
||||
.filter_map(|i| self.get(i))
|
||||
.find(|f| f.name() == name)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FileEngine for WebFileEngine {
|
||||
fn files(&self) -> Vec<String> {
|
||||
(0..self.len())
|
||||
.filter_map(|i| self.get(i).map(|f| f.name()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn file_size(&self, file: &str) -> Option<u64> {
|
||||
let file = self.find(file)?;
|
||||
Some(file.size() as u64)
|
||||
}
|
||||
|
||||
// read a file to bytes
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||
let file = self.find(file)?;
|
||||
|
||||
let file_reader = self.file_reader.clone();
|
||||
let (rx, tx) = oneshot::channel();
|
||||
let on_load: Closure<dyn FnMut()> = Closure::new({
|
||||
let mut rx = Some(rx);
|
||||
move || {
|
||||
let result = file_reader.result();
|
||||
let _ = rx
|
||||
.take()
|
||||
.expect("multiple files read without refreshing the channel")
|
||||
.send(result);
|
||||
}
|
||||
});
|
||||
|
||||
self.file_reader
|
||||
.set_onload(Some(on_load.as_ref().unchecked_ref()));
|
||||
on_load.forget();
|
||||
self.file_reader.read_as_array_buffer(&file).ok()?;
|
||||
|
||||
if let Ok(Ok(js_val)) = tx.await {
|
||||
let as_u8_arr = Uint8Array::new(&js_val);
|
||||
let as_u8_vec = as_u8_arr.to_vec();
|
||||
|
||||
Some(as_u8_vec)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// read a file to string
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||
let file = self.find(file)?;
|
||||
|
||||
let file_reader = self.file_reader.clone();
|
||||
let (rx, tx) = oneshot::channel();
|
||||
let on_load: Closure<dyn FnMut()> = Closure::new({
|
||||
let mut rx = Some(rx);
|
||||
move || {
|
||||
let result = file_reader.result();
|
||||
let _ = rx
|
||||
.take()
|
||||
.expect("multiple files read without refreshing the channel")
|
||||
.send(result);
|
||||
}
|
||||
});
|
||||
|
||||
self.file_reader
|
||||
.set_onload(Some(on_load.as_ref().unchecked_ref()));
|
||||
on_load.forget();
|
||||
self.file_reader.read_as_text(&file).ok()?;
|
||||
|
||||
if let Ok(Ok(js_val)) = tx.await {
|
||||
js_val.as_string()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
let file = self.find(file)?;
|
||||
Some(Box::new(file))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait for WebFileEngine
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait WebFileEngineExt {
|
||||
/// returns web_sys::File
|
||||
async fn get_web_file(&self, file: &str) -> Option<web_sys::File>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl WebFileEngineExt for std::sync::Arc<dyn FileEngine> {
|
||||
async fn get_web_file(&self, file: &str) -> Option<web_sys::File> {
|
||||
let native_file = self.get_native_file(file).await?;
|
||||
let ret = native_file.downcast::<web_sys::File>().ok()?;
|
||||
Some(*ret)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
mod events;
|
||||
#[cfg(feature = "file_engine")]
|
||||
mod file_engine;
|
||||
#[cfg(feature = "file_engine")]
|
||||
pub use file_engine::*;
|
||||
Reference in new issue
Block a user