Move the reconciler into the lifecycle crate, so that we can (properly) support state changes. Wrap the reconciliation phase in a RenderEngine, which root-level objects (windows, etc) can subscribe to and interface with. Migrate a few things relating to themes out of alchemy core and into styles, because the reconciliation phase requires them and they fit well enough there... solving a circular dependency.

This commit is contained in:
Ryan McGrath 2019-05-26 00:27:07 -07:00
parent f1cb5fea93
commit 6833e39d52
No known key found for this signature in database
GPG key ID: 811674B62B666830
16 changed files with 57 additions and 1098 deletions

View file

@ -17,3 +17,4 @@ alchemy-styles = { version = "0.1", path = "../styles" }
objc = { version = "0.2.6", optional = true }
objc_id = { version = "0.1.1", optional = true }
serde_json = "1"
uuid = { version = "0.7", features = ["v4"] }

View file

@ -7,7 +7,19 @@
//! It also includes the `RSX` enum, which is what `render()` methods generally
//! return. It's common enough to multiple crates, and is intricately linked to the
//! `Component` lifecycle, so it'll live here.
//!
//! This crate also includes the diffing and patching system for the widget tree -
//! it needs to live with the `Component` lifecycle to enable state updating.
use alchemy_styles::lazy_static;
pub mod error;
pub mod rsx;
pub mod traits;
mod reconciler;
use reconciler::RenderEngine;
lazy_static! {
pub static ref RENDER_ENGINE: RenderEngine = RenderEngine::new();
}

View file

@ -15,17 +15,6 @@ pub use virtual_text::VirtualText;
mod props;
pub use props::Props;
mod style_keys;
pub use self::style_keys::StyleKey;
mod spacedlist;
pub use self::spacedlist::SpacedList;
mod spacedset;
pub use self::spacedset::SpacedSet;
pub type StylesList = SpacedSet<StyleKey>;
use crate::traits::Component;
/// An enum representing the types of nodes that the

View file

@ -5,8 +5,10 @@ use std::sync::{Arc, RwLock};
use serde_json::Value;
use std::collections::HashMap;
use alchemy_styles::StylesList;
use crate::rsx::RSX;
use crate::traits::{Component};
use crate::rsx::{RSX, StylesList};
/// A value stored inside the `attributes` field on a `Props` instance.
/// It shadows `serde_json::Value`, but also allows for some other value

View file

@ -1,262 +0,0 @@
//! A space separated list of values.
//!
//! This type represents a list of non-unique values represented as a string of
//! values separated by spaces in HTML attributes. This is rarely used; a
//! SpacedSet of unique values is much more common.
use std::fmt::{Debug, Display, Error, Formatter};
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
/// A space separated list of values.
///
/// This type represents a list of non-unique values represented as a string of
/// values separated by spaces in HTML attributes. This is rarely used; a
/// SpacedSet of unique values is much more common.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SpacedList<A>(Vec<A>);
impl<A> SpacedList<A> {
/// Construct an empty `SpacedList`.
pub fn new() -> Self {
SpacedList(Vec::new())
}
}
impl<A> Default for SpacedList<A> {
fn default() -> Self {
Self::new()
}
}
impl<A> FromIterator<A> for SpacedList<A> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = A>,
{
SpacedList(iter.into_iter().collect())
}
}
impl<'a, A: 'a + Clone> FromIterator<&'a A> for SpacedList<A> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = &'a A>,
{
SpacedList(iter.into_iter().cloned().collect())
}
}
impl<'a, A: FromStr> From<&'a str> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: &'a str) -> Self {
Self::from_iter(s.split_whitespace().map(|s| FromStr::from_str(s).unwrap()))
}
}
impl<A> Deref for SpacedList<A> {
type Target = Vec<A>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<A> DerefMut for SpacedList<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<A: Display> Display for SpacedList<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let mut it = self.0.iter().peekable();
while let Some(class) = it.next() {
Display::fmt(class, f)?;
if it.peek().is_some() {
Display::fmt(" ", f)?;
}
}
Ok(())
}
}
impl<A: Debug> Debug for SpacedList<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
f.debug_list().entries(self.0.iter()).finish()
}
}
impl<'a, 'b, A: FromStr> From<(&'a str, &'b str)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list
}
}
impl<'a, 'b, 'c, A: FromStr> From<(&'a str, &'b str, &'c str)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, A: FromStr> From<(&'a str, &'b str, &'c str, &'d str)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list.push(FromStr::from_str(s.3).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, A: FromStr> From<(&'a str, &'b str, &'c str, &'d str, &'e str)>
for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list.push(FromStr::from_str(s.3).unwrap());
list.push(FromStr::from_str(s.4).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, A: FromStr>
From<(&'a str, &'b str, &'c str, &'d str, &'e str, &'f str)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list.push(FromStr::from_str(s.3).unwrap());
list.push(FromStr::from_str(s.4).unwrap());
list.push(FromStr::from_str(s.5).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, 'g, A: FromStr>
From<(
&'a str,
&'b str,
&'c str,
&'d str,
&'e str,
&'f str,
&'g str,
)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list.push(FromStr::from_str(s.3).unwrap());
list.push(FromStr::from_str(s.4).unwrap());
list.push(FromStr::from_str(s.5).unwrap());
list.push(FromStr::from_str(s.6).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, A: FromStr>
From<(
&'a str,
&'b str,
&'c str,
&'d str,
&'e str,
&'f str,
&'g str,
&'h str,
)> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.push(FromStr::from_str(s.0).unwrap());
list.push(FromStr::from_str(s.1).unwrap());
list.push(FromStr::from_str(s.2).unwrap());
list.push(FromStr::from_str(s.3).unwrap());
list.push(FromStr::from_str(s.4).unwrap());
list.push(FromStr::from_str(s.5).unwrap());
list.push(FromStr::from_str(s.6).unwrap());
list.push(FromStr::from_str(s.7).unwrap());
list
}
}
macro_rules! spacedlist_from_array {
($num:tt) => {
impl<'a, A: FromStr> From<[&'a str; $num]> for SpacedList<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: [&str; $num]) -> Self {
Self::from_iter(s.into_iter().map(|s| FromStr::from_str(*s).unwrap()))
}
}
};
}
spacedlist_from_array!(1);
spacedlist_from_array!(2);
spacedlist_from_array!(3);
spacedlist_from_array!(4);
spacedlist_from_array!(5);
spacedlist_from_array!(6);
spacedlist_from_array!(7);
spacedlist_from_array!(8);
spacedlist_from_array!(9);
spacedlist_from_array!(10);
spacedlist_from_array!(11);
spacedlist_from_array!(12);
spacedlist_from_array!(13);
spacedlist_from_array!(14);
spacedlist_from_array!(15);
spacedlist_from_array!(16);
spacedlist_from_array!(17);
spacedlist_from_array!(18);
spacedlist_from_array!(19);
spacedlist_from_array!(20);
spacedlist_from_array!(21);
spacedlist_from_array!(22);
spacedlist_from_array!(23);
spacedlist_from_array!(24);
spacedlist_from_array!(25);
spacedlist_from_array!(26);
spacedlist_from_array!(27);
spacedlist_from_array!(28);
spacedlist_from_array!(29);
spacedlist_from_array!(30);
spacedlist_from_array!(31);
spacedlist_from_array!(32);

View file

@ -1,293 +0,0 @@
//! A space separated set of unique values.
//!
//! This type represents a set of unique values represented as a string of
//! values separated by spaces in HTML attributes.
use std::collections::BTreeSet;
use std::fmt::{Debug, Display, Error, Formatter};
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
/// A space separated set of unique values.
///
/// This type represents a set of unique values represented as a string of
/// values separated by spaces in HTML attributes.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SpacedSet<A: Ord>(pub BTreeSet<A>);
impl<A: Ord> SpacedSet<A> {
/// Construct an empty `SpacedSet`.
pub fn new() -> Self {
SpacedSet(BTreeSet::new())
}
/// Add a value to the `SpacedSet`.
pub fn add<T: Into<A>>(&mut self, value: T) -> bool {
self.0.insert(value.into())
}
}
impl<A: Ord> Default for SpacedSet<A> {
fn default() -> Self {
Self::new()
}
}
impl<A: Ord> FromIterator<A> for SpacedSet<A> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = A>,
{
SpacedSet(iter.into_iter().collect())
}
}
impl<'a, A: 'a + Ord + Clone> FromIterator<&'a A> for SpacedSet<A> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = &'a A>,
{
SpacedSet(iter.into_iter().cloned().collect())
}
}
impl<'a, A: Ord + FromStr> FromStr for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
type Err = <A as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let result: Result<Vec<A>, Self::Err> =
s.split_whitespace().map(|s| FromStr::from_str(s)).collect();
result.map(Self::from_iter)
}
}
impl<'a, A: Ord + FromStr> From<&'a str> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: &'a str) -> Self {
Self::from_iter(s.split_whitespace().map(|s| FromStr::from_str(s).unwrap()))
}
}
impl<A: Ord> Deref for SpacedSet<A> {
type Target = BTreeSet<A>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<A: Ord> DerefMut for SpacedSet<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<A: Ord + Display> Display for SpacedSet<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let mut it = self.0.iter().peekable();
while let Some(class) = it.next() {
Display::fmt(class, f)?;
if it.peek().is_some() {
Display::fmt(" ", f)?;
}
}
Ok(())
}
}
impl<A: Ord + Debug> Debug for SpacedSet<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
f.debug_list().entries(self.0.iter()).finish()
}
}
impl<'a, A: Ord + FromStr> From<Vec<&'a str>> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: Vec<&'a str>) -> Self {
let mut list = Self::new();
for key in s {
list.insert(FromStr::from_str(key).unwrap());
}
list
}
}
impl<'a, 'b, A: Ord + FromStr> From<(&'a str, &'b str)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list
}
}
impl<'a, 'b, 'c, A: Ord + FromStr> From<(&'a str, &'b str, &'c str)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, A: Ord + FromStr> From<(&'a str, &'b str, &'c str, &'d str)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list.insert(FromStr::from_str(s.3).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, A: Ord + FromStr> From<(&'a str, &'b str, &'c str, &'d str, &'e str)>
for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list.insert(FromStr::from_str(s.3).unwrap());
list.insert(FromStr::from_str(s.4).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, A: Ord + FromStr>
From<(&'a str, &'b str, &'c str, &'d str, &'e str, &'f str)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list.insert(FromStr::from_str(s.3).unwrap());
list.insert(FromStr::from_str(s.4).unwrap());
list.insert(FromStr::from_str(s.5).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, 'g, A: Ord + FromStr>
From<(
&'a str,
&'b str,
&'c str,
&'d str,
&'e str,
&'f str,
&'g str,
)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list.insert(FromStr::from_str(s.3).unwrap());
list.insert(FromStr::from_str(s.4).unwrap());
list.insert(FromStr::from_str(s.5).unwrap());
list.insert(FromStr::from_str(s.6).unwrap());
list
}
}
impl<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, A: Ord + FromStr>
From<(
&'a str,
&'b str,
&'c str,
&'d str,
&'e str,
&'f str,
&'g str,
&'h str,
)> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
let mut list = Self::new();
list.insert(FromStr::from_str(s.0).unwrap());
list.insert(FromStr::from_str(s.1).unwrap());
list.insert(FromStr::from_str(s.2).unwrap());
list.insert(FromStr::from_str(s.3).unwrap());
list.insert(FromStr::from_str(s.4).unwrap());
list.insert(FromStr::from_str(s.5).unwrap());
list.insert(FromStr::from_str(s.6).unwrap());
list.insert(FromStr::from_str(s.7).unwrap());
list
}
}
macro_rules! spacedlist_from_array {
($num:tt) => {
impl<'a, A: Ord + FromStr> From<[&'a str; $num]> for SpacedSet<A>
where
<A as FromStr>::Err: Debug,
{
fn from(s: [&str; $num]) -> Self {
Self::from_iter(s.into_iter().map(|s| FromStr::from_str(*s).unwrap()))
}
}
};
}
spacedlist_from_array!(1);
spacedlist_from_array!(2);
spacedlist_from_array!(3);
spacedlist_from_array!(4);
spacedlist_from_array!(5);
spacedlist_from_array!(6);
spacedlist_from_array!(7);
spacedlist_from_array!(8);
spacedlist_from_array!(9);
spacedlist_from_array!(10);
spacedlist_from_array!(11);
spacedlist_from_array!(12);
spacedlist_from_array!(13);
spacedlist_from_array!(14);
spacedlist_from_array!(15);
spacedlist_from_array!(16);
spacedlist_from_array!(17);
spacedlist_from_array!(18);
spacedlist_from_array!(19);
spacedlist_from_array!(20);
spacedlist_from_array!(21);
spacedlist_from_array!(22);
spacedlist_from_array!(23);
spacedlist_from_array!(24);
spacedlist_from_array!(25);
spacedlist_from_array!(26);
spacedlist_from_array!(27);
spacedlist_from_array!(28);
spacedlist_from_array!(29);
spacedlist_from_array!(30);
spacedlist_from_array!(31);
spacedlist_from_array!(32);

View file

@ -1,83 +0,0 @@
//! A valid CSS class.
//!
//! A CSS class is a non-empty string that starts with an alphanumeric character
//! and is followed by any number of alphanumeric characters and the
//! `_`, `-` and `.` characters.
use std::fmt::{Display, Error, Formatter};
use std::ops::Deref;
use std::str::FromStr;
/// A valid CSS class.
///
/// A CSS class is a non-empty string that starts with an alphanumeric character
/// and is followed by any number of alphanumeric characters and the
/// `_`, `-` and `.` characters.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct StyleKey(String);
impl StyleKey {
/// Construct a new styles list from a string.
///
/// Returns `Err` if the provided string is invalid.
pub fn try_new<S: Into<String>>(id: S) -> Result<Self, &'static str> {
let id = id.into();
{
let mut chars = id.chars();
match chars.next() {
None => return Err("style keys cannot be empty"),
Some(c) if !c.is_alphabetic() => {
return Err("style keys must start with an alphabetic character")
}
_ => (),
}
for c in chars {
if !c.is_alphanumeric() && c != '-' {
return Err(
"style keys can only contain alphanumerics (dash included)",
);
}
}
}
Ok(StyleKey(id))
}
/// Construct a new class name from a string.
///
/// Panics if the provided string is invalid.
pub fn new<S: Into<String>>(id: S) -> Self {
let id = id.into();
Self::try_new(id.clone()).unwrap_or_else(|err| {
panic!(
"alchemy::dom::types::StyleKey: {:?} is not a valid class name: {}",
id, err
)
})
}
}
impl FromStr for StyleKey {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
StyleKey::try_new(s)
}
}
impl<'a> From<&'a str> for StyleKey {
fn from(str: &'a str) -> Self {
StyleKey::from_str(str).unwrap()
}
}
impl Display for StyleKey {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
Display::fmt(&self.0, f)
}
}
impl Deref for StyleKey {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}