Merge branch 'master' into tui_focus

This commit is contained in:
Demonthos 2022-05-06 21:19:13 -05:00 committed by GitHub
commit a3abe3965a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 303 additions and 45 deletions

2
.github/FUNDING.yml vendored
View file

@ -1,4 +1,4 @@
# These are supported funding model platforms
github: jkelleyrtp # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: DioxusLabs # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
open_collective: dioxus-labs # Replace with a single Open Collective username

View file

@ -44,7 +44,6 @@ html = ["dioxus-html"]
ssr = ["dioxus-ssr"]
web = ["dioxus-web", "dioxus-router/web"]
desktop = ["dioxus-desktop"]
ayatana = ["dioxus-desktop/ayatana"]
router = ["dioxus-router"]
tui = ["dioxus-tui"]
liveview = ["dioxus-liveview"]

View file

@ -102,7 +102,7 @@ For keen Rustaceans: notice how we don't actually call `collect` on the name lis
## Keeping list items in order with `key`
The examples above demonstrate the power of iterators in `rsx!` but all share the same issue: if your array items move (e.g. due to sorting), get inserted, or get deleted, Dioxus has no way of knowing what happened. This can cause Elements to be unnecessarily removed, changed and rebuilt when all that was needed was to change their position this is inneficient.
The examples above demonstrate the power of iterators in `rsx!` but all share the same issue: if your array items move (e.g. due to sorting), get inserted, or get deleted, Dioxus has no way of knowing what happened. This can cause Elements to be unnecessarily removed, changed and rebuilt when all that was needed was to change their position this is inefficient.
To solve this problem, each item in the list must be **uniquely identifiable**. You can achieve this by giving it a unique, fixed "key". In Dioxus, a key is a string that identifies an item among others in the list.

View file

@ -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);
// }

View file

@ -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::*;

View file

@ -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,
});

View file

@ -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,

View file

@ -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]

View file

@ -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())

View file

@ -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());

View file

@ -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)?
}
}

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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)
}
}