Merge branch 'master' into tui_focus
This commit is contained in:
16 files changed
+303
-45
No files matched your search
@@ -0,0 +1,232 @@
|
||||
use std::fmt::Formatter;
|
||||
|
||||
// trying to keep values at 3 bytes
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AttributeValue<'a> {
|
||||
Text(&'a str),
|
||||
Float32(f32),
|
||||
Float64(f64),
|
||||
Int32(i32),
|
||||
Int64(i64),
|
||||
Uint32(u32),
|
||||
Uint64(u64),
|
||||
Bool(bool),
|
||||
|
||||
Vec3Float(f32, f32, f32),
|
||||
Vec3Int(i32, i32, i32),
|
||||
Vec3Uint(u32, u32, u32),
|
||||
|
||||
Vec4Float(f32, f32, f32, f32),
|
||||
Vec4Int(i32, i32, i32, i32),
|
||||
Vec4Uint(u32, u32, u32, u32),
|
||||
|
||||
Bytes(&'a [u8]),
|
||||
Any(ArbitraryAttributeValue<'a>),
|
||||
}
|
||||
|
||||
impl<'a> AttributeValue<'a> {
|
||||
pub fn is_truthy(&self) -> bool {
|
||||
match self {
|
||||
AttributeValue::Text(t) => *t == "true",
|
||||
AttributeValue::Bool(t) => *t,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_falsy(&self) -> bool {
|
||||
match self {
|
||||
AttributeValue::Text(t) => *t == "false",
|
||||
AttributeValue::Bool(t) => !(*t),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for AttributeValue<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AttributeValue::Text(a) => write!(f, "{}", a),
|
||||
AttributeValue::Float32(a) => write!(f, "{}", a),
|
||||
AttributeValue::Float64(a) => write!(f, "{}", a),
|
||||
AttributeValue::Int32(a) => write!(f, "{}", a),
|
||||
AttributeValue::Int64(a) => write!(f, "{}", a),
|
||||
AttributeValue::Uint32(a) => write!(f, "{}", a),
|
||||
AttributeValue::Uint64(a) => write!(f, "{}", a),
|
||||
AttributeValue::Bool(a) => write!(f, "{}", a),
|
||||
AttributeValue::Vec3Float(_, _, _) => todo!(),
|
||||
AttributeValue::Vec3Int(_, _, _) => todo!(),
|
||||
AttributeValue::Vec3Uint(_, _, _) => todo!(),
|
||||
AttributeValue::Vec4Float(_, _, _, _) => todo!(),
|
||||
AttributeValue::Vec4Int(_, _, _, _) => todo!(),
|
||||
AttributeValue::Vec4Uint(_, _, _, _) => todo!(),
|
||||
AttributeValue::Bytes(_) => todo!(),
|
||||
AttributeValue::Any(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ArbitraryAttributeValue<'a> {
|
||||
pub value: &'a dyn std::any::Any,
|
||||
pub cmp: fn(&'a dyn std::any::Any, &'a dyn std::any::Any) -> bool,
|
||||
}
|
||||
|
||||
impl PartialEq for ArbitraryAttributeValue<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.cmp)(self.value, other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ArbitraryAttributeValue<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ArbitraryAttributeValue").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> serde::Serialize for ArbitraryAttributeValue<'a> {
|
||||
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
panic!("ArbitraryAttributeValue should not be serialized")
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de, 'a> serde::Deserialize<'de> for &'a ArbitraryAttributeValue<'a> {
|
||||
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("ArbitraryAttributeValue is not deserializable!")
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de, 'a> serde::Deserialize<'de> for ArbitraryAttributeValue<'a> {
|
||||
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("ArbitraryAttributeValue is not deserializable!")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AttributeValue<'a> {
|
||||
pub fn as_text(&self) -> Option<&'a str> {
|
||||
match self {
|
||||
AttributeValue::Text(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float32(&self) -> Option<f32> {
|
||||
match self {
|
||||
AttributeValue::Float32(f) => Some(*f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float64(&self) -> Option<f64> {
|
||||
match self {
|
||||
AttributeValue::Float64(f) => Some(*f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int32(&self) -> Option<i32> {
|
||||
match self {
|
||||
AttributeValue::Int32(i) => Some(*i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int64(&self) -> Option<i64> {
|
||||
match self {
|
||||
AttributeValue::Int64(i) => Some(*i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_uint32(&self) -> Option<u32> {
|
||||
match self {
|
||||
AttributeValue::Uint32(i) => Some(*i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_uint64(&self) -> Option<u64> {
|
||||
match self {
|
||||
AttributeValue::Uint64(i) => Some(*i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
match self {
|
||||
AttributeValue::Bool(b) => Some(*b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> {
|
||||
match self {
|
||||
AttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec3_int(&self) -> Option<(i32, i32, i32)> {
|
||||
match self {
|
||||
AttributeValue::Vec3Int(x, y, z) => Some((*x, *y, *z)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec3_uint(&self) -> Option<(u32, u32, u32)> {
|
||||
match self {
|
||||
AttributeValue::Vec3Uint(x, y, z) => Some((*x, *y, *z)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec4_float(&self) -> Option<(f32, f32, f32, f32)> {
|
||||
match self {
|
||||
AttributeValue::Vec4Float(x, y, z, w) => Some((*x, *y, *z, *w)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec4_int(&self) -> Option<(i32, i32, i32, i32)> {
|
||||
match self {
|
||||
AttributeValue::Vec4Int(x, y, z, w) => Some((*x, *y, *z, *w)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec4_uint(&self) -> Option<(u32, u32, u32, u32)> {
|
||||
match self {
|
||||
AttributeValue::Vec4Uint(x, y, z, w) => Some((*x, *y, *z, *w)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
AttributeValue::Bytes(b) => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
|
||||
match self {
|
||||
AttributeValue::Any(a) => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_attribute_value_size() {
|
||||
// assert_eq!(std::mem::size_of::<AttributeValue<'_>>(), 24);
|
||||
// }
|
||||
@@ -2,6 +2,7 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub(crate) mod arbitrary_value;
|
||||
pub(crate) mod diff;
|
||||
pub(crate) mod events;
|
||||
pub(crate) mod lazynodes;
|
||||
@@ -13,6 +14,7 @@ pub(crate) mod util;
|
||||
pub(crate) mod virtual_dom;
|
||||
|
||||
pub(crate) mod innerlude {
|
||||
pub use crate::arbitrary_value::*;
|
||||
pub use crate::events::*;
|
||||
pub use crate::lazynodes::*;
|
||||
pub use crate::mutations::*;
|
||||
|
||||
@@ -167,8 +167,9 @@ pub enum DomEdit<'bump> {
|
||||
field: &'static str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: &'bump str,
|
||||
value: AttributeValue<'bump>,
|
||||
|
||||
// value: &'bump str,
|
||||
/// The (optional) namespace of the attribute.
|
||||
/// For instance, "style" is in the "style" namespace.
|
||||
ns: Option<&'bump str>,
|
||||
@@ -286,7 +287,7 @@ impl<'a> Mutations<'a> {
|
||||
self.edits.push(SetText { text, root });
|
||||
}
|
||||
|
||||
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
|
||||
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: u64) {
|
||||
let Attribute {
|
||||
name,
|
||||
value,
|
||||
@@ -296,7 +297,7 @@ impl<'a> Mutations<'a> {
|
||||
|
||||
self.edits.push(SetAttribute {
|
||||
field: name,
|
||||
value,
|
||||
value: value.clone(),
|
||||
ns: *namespace,
|
||||
root,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! cheap and *very* fast to construct - building a full tree should be quick.
|
||||
|
||||
use crate::{
|
||||
innerlude::{ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
|
||||
innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
|
||||
lazynodes::LazyNodes,
|
||||
AnyEvent, Component,
|
||||
};
|
||||
@@ -339,7 +339,7 @@ pub struct Attribute<'a> {
|
||||
pub name: &'static str,
|
||||
|
||||
/// The value of the attribute.
|
||||
pub value: &'a str,
|
||||
pub value: AttributeValue<'a>,
|
||||
|
||||
/// An indication if this attribute can be ignored during diffing
|
||||
///
|
||||
@@ -610,6 +610,24 @@ impl<'a> NodeFactory<'a> {
|
||||
is_volatile: bool,
|
||||
) -> Attribute<'a> {
|
||||
let (value, is_static) = self.raw_text(val);
|
||||
Attribute {
|
||||
name,
|
||||
value: AttributeValue::Text(value),
|
||||
is_static,
|
||||
namespace,
|
||||
is_volatile,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`Attribute`] using non-arguments
|
||||
pub fn custom_attr(
|
||||
&self,
|
||||
name: &'static str,
|
||||
value: AttributeValue<'a>,
|
||||
namespace: Option<&'static str>,
|
||||
is_volatile: bool,
|
||||
is_static: bool,
|
||||
) -> Attribute<'a> {
|
||||
Attribute {
|
||||
name,
|
||||
value,
|
||||
|
||||
@@ -20,7 +20,7 @@ serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
thiserror = "1.0.30"
|
||||
log = "0.4.14"
|
||||
wry = { version = "0.13.1" }
|
||||
wry = { version = "0.16.0" }
|
||||
futures-channel = "0.3.21"
|
||||
tokio = { version = "1.16.1", features = [
|
||||
"sync",
|
||||
@@ -28,7 +28,7 @@ tokio = { version = "1.16.1", features = [
|
||||
"rt",
|
||||
"time",
|
||||
], optional = true, default-features = false }
|
||||
webbrowser = "0.6.0"
|
||||
webbrowser = "0.7.1"
|
||||
mime_guess = "2.0.3"
|
||||
dunce = "1.0.2"
|
||||
|
||||
@@ -38,13 +38,9 @@ core-foundation = "0.9.3"
|
||||
[features]
|
||||
default = ["tokio_runtime"]
|
||||
tokio_runtime = ["tokio"]
|
||||
|
||||
devtool = ["wry/devtool"]
|
||||
fullscreen = ["wry/fullscreen"]
|
||||
transparent = ["wry/transparent"]
|
||||
|
||||
tray = ["wry/tray"]
|
||||
ayatana = ["wry/ayatana"]
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -191,7 +191,7 @@ pub(super) fn handler(
|
||||
SetTitle(content) => window.set_title(&content),
|
||||
SetDecorations(state) => window.set_decorations(state),
|
||||
|
||||
DevTool => webview.devtool(),
|
||||
DevTool => {}
|
||||
|
||||
Eval(code) => webview
|
||||
.evaluate_script(code.as_str())
|
||||
|
||||
@@ -200,7 +200,7 @@ pub fn launch_with_props<P: 'static + Send>(
|
||||
)
|
||||
} else {
|
||||
// in debug, we are okay with the reload menu showing and dev tool
|
||||
webview = webview.with_dev_tool(true);
|
||||
webview = webview.with_devtools(true);
|
||||
}
|
||||
|
||||
desktop.webviews.insert(window_id, webview.build().unwrap());
|
||||
|
||||
@@ -186,7 +186,9 @@ impl<'a> TextRenderer<'a, '_> {
|
||||
while let Some(attr) = attr_iter.next() {
|
||||
match attr.namespace {
|
||||
None => match attr.name {
|
||||
"dangerous_inner_html" => inner_html = Some(attr.value),
|
||||
"dangerous_inner_html" => {
|
||||
inner_html = Some(attr.value.as_text().unwrap())
|
||||
}
|
||||
"allowfullscreen"
|
||||
| "allowpaymentrequest"
|
||||
| "async"
|
||||
@@ -213,7 +215,7 @@ impl<'a> TextRenderer<'a, '_> {
|
||||
| "reversed"
|
||||
| "selected"
|
||||
| "truespeed" => {
|
||||
if attr.value != "false" {
|
||||
if attr.value.is_truthy() {
|
||||
write!(f, " {}=\"{}\"", attr.name, attr.value)?
|
||||
}
|
||||
}
|
||||
|
||||
+24
-20
@@ -259,33 +259,37 @@ impl InnerInputState {
|
||||
let old_pos = previous_mouse
|
||||
.as_ref()
|
||||
.map(|m| (m.0.screen_x, m.0.screen_y));
|
||||
let clicked =
|
||||
(!mouse.0.buttons & previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0;
|
||||
// the a mouse button is pressed if a button was not down and is now down
|
||||
let pressed =
|
||||
(mouse.0.buttons & !previous_mouse.as_ref().map(|m| m.0.buttons).unwrap_or(0)) > 0;
|
||||
// the a mouse button is pressed if a button was down and is now not down
|
||||
let released =
|
||||
(mouse.0.buttons & !previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0;
|
||||
(!mouse.0.buttons & previous_mouse.map(|m| m.0.buttons).unwrap_or(0)) > 0;
|
||||
let wheel_delta = self.wheel.as_ref().map_or(0.0, |w| w.delta_y);
|
||||
let mouse_data = &mouse.0;
|
||||
let wheel_data = &self.wheel;
|
||||
|
||||
{
|
||||
// mousemove
|
||||
let mut will_bubble = FxHashSet::default();
|
||||
for node in dom.get_listening_sorted("mousemove") {
|
||||
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
|
||||
let previously_contained = old_pos
|
||||
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||
.is_some();
|
||||
let currently_contains = layout_contains_point(node_layout, new_pos);
|
||||
if old_pos != Some(new_pos) {
|
||||
let mut will_bubble = FxHashSet::default();
|
||||
for node in dom.get_listening_sorted("mousemove") {
|
||||
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
|
||||
let previously_contained = old_pos
|
||||
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||
.is_some();
|
||||
let currently_contains = layout_contains_point(node_layout, new_pos);
|
||||
|
||||
if currently_contains && previously_contained {
|
||||
try_create_event(
|
||||
"mousemove",
|
||||
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
|
||||
&mut will_bubble,
|
||||
resolved_events,
|
||||
node,
|
||||
dom,
|
||||
);
|
||||
if currently_contains && previously_contained {
|
||||
try_create_event(
|
||||
"mousemove",
|
||||
Arc::new(prepare_mouse_data(mouse_data, node_layout)),
|
||||
&mut will_bubble,
|
||||
resolved_events,
|
||||
node,
|
||||
dom,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,7 +341,7 @@ impl InnerInputState {
|
||||
}
|
||||
|
||||
// mousedown
|
||||
if clicked {
|
||||
if pressed {
|
||||
let mut will_bubble = FxHashSet::default();
|
||||
for node in dom.get_listening_sorted("mousedown") {
|
||||
let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
|
||||
|
||||
@@ -79,9 +79,11 @@ impl ChildDepState for StretchLayout {
|
||||
}
|
||||
} else {
|
||||
// gather up all the styles from the attribute list
|
||||
for &Attribute { name, value, .. } in node.attributes() {
|
||||
assert!(SORTED_LAYOUT_ATTRS.binary_search(&name).is_ok());
|
||||
apply_layout_attributes(name, value, &mut style);
|
||||
for Attribute { name, value, .. } in node.attributes() {
|
||||
assert!(SORTED_LAYOUT_ATTRS.binary_search(name).is_ok());
|
||||
if let Some(text) = value.as_text() {
|
||||
apply_layout_attributes(name, text, &mut style);
|
||||
}
|
||||
}
|
||||
|
||||
// the root node fills the entire area
|
||||
|
||||
@@ -78,8 +78,10 @@ impl ParentDepState for StyleModifier {
|
||||
}
|
||||
|
||||
// gather up all the styles from the attribute list
|
||||
for &Attribute { name, value, .. } in node.attributes() {
|
||||
apply_style_attributes(name, value, &mut new);
|
||||
for Attribute { name, value, .. } in node.attributes() {
|
||||
if let Some(text) = value.as_text() {
|
||||
apply_style_attributes(name, text, &mut new);
|
||||
}
|
||||
}
|
||||
|
||||
// keep the text styling from the parent element
|
||||
|
||||
@@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
|
||||
dioxus-core = { path = "../core", version = "^0.2.1" }
|
||||
dioxus-html = { path = "../html", version = "^0.2.1", features = ["wasm-bind"] }
|
||||
dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features = [
|
||||
"web",
|
||||
"web"
|
||||
] }
|
||||
|
||||
js-sys = "0.3.56"
|
||||
|
||||
@@ -146,7 +146,7 @@ impl WebsysDom {
|
||||
value,
|
||||
ns,
|
||||
} => {
|
||||
let value = serde_wasm_bindgen::to_value(value).unwrap();
|
||||
let value = serde_wasm_bindgen::to_value(&value).unwrap();
|
||||
self.interpreter.SetAttribute(root, field, value, ns)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user