diff --git a/alchemy/src/components/fragment.rs b/alchemy/src/components/fragment.rs
index 2f2a6e9..80e3e12 100644
--- a/alchemy/src/components/fragment.rs
+++ b/alchemy/src/components/fragment.rs
@@ -4,6 +4,7 @@
//! but as the language stabilizes even further I'd love to get rid of this and
//! just allow returning arbitrary iterators.
+use alchemy_lifecycle::ComponentKey;
use alchemy_lifecycle::traits::Component;
/// Fragments are special - you can do something like the following in cases where you
@@ -19,4 +20,8 @@ use alchemy_lifecycle::traits::Component;
#[derive(Default, Debug)]
pub struct Fragment;
-impl Component for Fragment {}
+impl Component for Fragment {
+ fn constructor(_key: ComponentKey) -> Fragment {
+ Fragment { }
+ }
+}
diff --git a/alchemy/src/components/text.rs b/alchemy/src/components/text.rs
index 23f8a7c..1c20ed8 100644
--- a/alchemy/src/components/text.rs
+++ b/alchemy/src/components/text.rs
@@ -5,8 +5,9 @@
use std::sync::{Mutex};
-use alchemy_styles::styles::{Layout, Style};
+use alchemy_styles::styles::{Appearance, Layout};
+use alchemy_lifecycle::ComponentKey;
use alchemy_lifecycle::error::Error;
use alchemy_lifecycle::rsx::{Props, RSX};
use alchemy_lifecycle::traits::{Component, PlatformSpecificNodeType};
@@ -23,19 +24,11 @@ use alchemy_cocoa::text::{Text as PlatformTextBridge};
///
/// ```
pub struct Text {
+ key: ComponentKey,
text: String,
bridge: Mutex
}
-impl Default for Text {
- fn default() -> Text {
- Text {
- text: "".into(),
- bridge: Mutex::new(PlatformTextBridge::new())
- }
- }
-}
-
impl Text {
// This is very naive for now, but it's fine - we probably
// want to do some fun stuff here later with stylized text
@@ -55,6 +48,14 @@ impl Text {
}
impl Component for Text {
+ fn constructor(key: ComponentKey) -> Text {
+ Text {
+ key: key,
+ text: "".into(),
+ bridge: Mutex::new(PlatformTextBridge::new())
+ }
+ }
+
fn has_native_backing_node(&self) -> bool { true }
fn borrow_native_backing_node(&self) -> Option {
@@ -66,9 +67,9 @@ impl Component for Text {
// Panic might not be right here, but eh, should probably do something.
fn append_child_component(&self, _component: &Component) {}
- fn apply_styles(&self, layout: &Layout, style: &Style) {
+ fn apply_styles(&self, appearance: &Appearance, layout: &Layout) {
let mut bridge = self.bridge.lock().unwrap();
- bridge.apply_styles(layout, style);
+ bridge.apply_styles(appearance, layout);
}
fn component_did_mount(&mut self, props: &Props) {
diff --git a/alchemy/src/components/view.rs b/alchemy/src/components/view.rs
index f572ada..af122df 100644
--- a/alchemy/src/components/view.rs
+++ b/alchemy/src/components/view.rs
@@ -3,10 +3,11 @@
//! hence why they're all (somewhat annoyingly, but lovingly) re-implemented
//! as bridges.
-use std::sync::{Arc, Mutex, RwLock};
+use std::sync::Mutex;
-use alchemy_styles::{Layout, Style, StylesList};
+use alchemy_styles::{Appearance, Layout, StylesList};
+use alchemy_lifecycle::ComponentKey;
use alchemy_lifecycle::error::Error;
use alchemy_lifecycle::rsx::{Props, RSX};
use alchemy_lifecycle::traits::{Component, PlatformSpecificNodeType};
@@ -33,6 +34,10 @@ impl Default for View {
}
impl Component for View {
+ fn constructor(_key: ComponentKey) -> View {
+ View(Mutex::new(PlatformViewBridge::new()))
+ }
+
fn has_native_backing_node(&self) -> bool { true }
fn borrow_native_backing_node(&self) -> Option {
@@ -47,17 +52,17 @@ impl Component for View {
}
}
- fn apply_styles(&self, layout: &Layout, style: &Style) {
+ fn apply_styles(&self, appearance: &Appearance, layout: &Layout) {
let mut bridge = self.0.lock().unwrap();
- bridge.apply_styles(layout, style);
+ bridge.apply_styles(appearance, layout);
}
fn render(&self, props: &Props) -> Result {
- Ok(RSX::node("Fragment", || Arc::new(RwLock::new(Fragment::default())), Props {
+ Ok(RSX::node("Fragment", |key| Box::new(Fragment::constructor(key)), Props {
attributes: std::collections::HashMap::new(),
key: "".into(),
styles: StylesList::new(),
- children: props.children.clone()
- }))
+ children: vec![]
+ }, props.children.clone()))
}
}
diff --git a/alchemy/src/lib.rs b/alchemy/src/lib.rs
index 3152c39..1591c17 100644
--- a/alchemy/src/lib.rs
+++ b/alchemy/src/lib.rs
@@ -9,6 +9,7 @@ use std::sync::Arc;
pub use lazy_static::lazy_static;
use proc_macro_hack::proc_macro_hack;
+pub use alchemy_lifecycle::ComponentKey;
pub use alchemy_lifecycle::traits::{
AppDelegate, Component, WindowDelegate
};
diff --git a/alchemy/src/window/window.rs b/alchemy/src/window/window.rs
index b22c781..4412e59 100644
--- a/alchemy/src/window/window.rs
+++ b/alchemy/src/window/window.rs
@@ -1,15 +1,11 @@
//! Implements the Window API. It attempts to provide a nice, common interface across
//! per-platform Window APIs.
-use std::sync::{Arc, Mutex, RwLock};
+use std::sync::{Arc, Mutex};
-use alchemy_lifecycle::{Uuid, RENDER_ENGINE};
-use alchemy_lifecycle::rsx::{Props, RSX};
-use alchemy_lifecycle::traits::{Component, WindowDelegate};
-
-use alchemy_styles::number::Number;
-use alchemy_styles::geometry::Size;
-use alchemy_styles::styles::{Style, Dimension};
+use alchemy_lifecycle::{ComponentKey, RENDER_ENGINE};
+use alchemy_lifecycle::rsx::RSX;
+use alchemy_lifecycle::traits::WindowDelegate;
use crate::{App, SHARED_APP};
use crate::components::View;
@@ -25,7 +21,7 @@ pub struct AppWindow {
pub title: String,
pub bridge: PlatformWindowBridge,
pub delegate: Box,
- pub render_key: Uuid
+ pub render_key: ComponentKey
}
impl AppWindow {
@@ -74,7 +70,10 @@ impl Window {
let view = View::default();
let shared_app_ptr: *const App = &**SHARED_APP;
let bridge = PlatformWindowBridge::new(window_id, title, dimensions, &view, shared_app_ptr);
- let key = RENDER_ENGINE.register_root_component(view);
+ let key = match RENDER_ENGINE.register_root_component(view) {
+ Ok(key) => key,
+ Err(_e) => { panic!("Uhhhh this really messed up"); }
+ };
Window(Arc::new(Mutex::new(AppWindow {
id: window_id,
diff --git a/cocoa/src/text.rs b/cocoa/src/text.rs
index 64df5f6..30261f4 100644
--- a/cocoa/src/text.rs
+++ b/cocoa/src/text.rs
@@ -13,9 +13,7 @@ use cocoa::foundation::{NSRect, NSPoint, NSSize, NSString};
use crate::color::IntoNSColor;
-use alchemy_styles::color::Color;
-use alchemy_styles::styles::Style;
-use alchemy_styles::result::Layout;
+use alchemy_styles::{Color, Layout, Appearance};
use alchemy_lifecycle::traits::PlatformSpecificNodeType;
@@ -74,15 +72,15 @@ impl Text {
/// Given a `&Style`, will set the frame, background color, borders and so forth. It then
/// calls `setNeedsDisplay:YES` on the Objective-C side, so that Cocoa will re-render this
/// view.
- pub fn apply_styles(&mut self, layout: &Layout, style: &Style) {
+ pub fn apply_styles(&mut self, appearance: &Appearance, layout: &Layout) {
unsafe {
let rect = NSRect::new(
NSPoint::new(layout.location.x.into(), layout.location.y.into()),
NSSize::new(layout.size.width.into(), layout.size.height.into())
);
- self.background_color = style.background_color.into_nscolor();
- self.text_color = style.text_color.into_nscolor();
+ self.background_color = appearance.background_color.into_nscolor();
+ self.text_color = appearance.text_color.into_nscolor();
msg_send![&*self.inner_mut, setFrame:rect];
msg_send![&*self.inner_mut, setBackgroundColor:&*self.background_color];
diff --git a/cocoa/src/view.rs b/cocoa/src/view.rs
index 58899e8..5b123cb 100644
--- a/cocoa/src/view.rs
+++ b/cocoa/src/view.rs
@@ -13,9 +13,7 @@ use cocoa::foundation::{NSRect, NSPoint, NSSize};
use crate::color::IntoNSColor;
-use alchemy_styles::color::Color;
-use alchemy_styles::styles::Style;
-use alchemy_styles::result::Layout;
+use alchemy_styles::{Appearance, Color, Layout};
use alchemy_lifecycle::traits::PlatformSpecificNodeType;
@@ -70,14 +68,14 @@ impl View {
/// Given a `&Style`, will set the frame, background color, borders and so forth. It then
/// calls `setNeedsDisplay:YES` on the Objective-C side, so that Cocoa will re-render this
/// view.
- pub fn apply_styles(&mut self, layout: &Layout, style: &Style) {
+ pub fn apply_styles(&mut self, appearance: &Appearance, layout: &Layout) {
unsafe {
let rect = NSRect::new(
NSPoint::new(layout.location.x.into(), layout.location.y.into()),
NSSize::new(layout.size.width.into(), layout.size.height.into())
);
- self.background_color = style.background_color.into_nscolor();
+ self.background_color = appearance.background_color.into_nscolor();
self.inner_mut.set_ivar(BACKGROUND_COLOR, &*self.background_color);
msg_send![&*self.inner_mut, setFrame:rect];
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
index 96a5ec1..f0b8fc1 100644
--- a/examples/layout/src/main.rs
+++ b/examples/layout/src/main.rs
@@ -8,7 +8,7 @@
/// @created March 26th, 2019
use alchemy::{
- AppDelegate, Component, Fragment, Props, Error, rsx, RSX, styles,
+ AppDelegate, Component, ComponentKey, Fragment, Props, Error, rsx, RSX, styles,
Text, View, Window, WindowDelegate
};
@@ -26,6 +26,10 @@ impl AppDelegate for AppState {
pub struct Banner;
impl Component for Banner {
+ fn constructor(_key: ComponentKey) -> Banner {
+ Banner {}
+ }
+
fn render(&self, props: &Props) -> Result {
Ok(rsx! {
diff --git a/lifecycle/Cargo.toml b/lifecycle/Cargo.toml
index e178598..a4694db 100644
--- a/lifecycle/Cargo.toml
+++ b/lifecycle/Cargo.toml
@@ -17,4 +17,3 @@ 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"] }
diff --git a/lifecycle/src/lib.rs b/lifecycle/src/lib.rs
index f2d3a60..8fdc032 100644
--- a/lifecycle/src/lib.rs
+++ b/lifecycle/src/lib.rs
@@ -11,7 +11,7 @@
//! 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.
-pub use uuid::Uuid;
+pub use std::sync::Arc;
use alchemy_styles::lazy_static;
@@ -21,6 +21,7 @@ pub mod traits;
mod reconciler;
use reconciler::RenderEngine;
+pub use reconciler::key::ComponentKey;
lazy_static! {
pub static ref RENDER_ENGINE: RenderEngine = RenderEngine::new();
diff --git a/lifecycle/src/reconciler/error.rs b/lifecycle/src/reconciler/error.rs
index 8c2bbc4..ea72825 100644
--- a/lifecycle/src/reconciler/error.rs
+++ b/lifecycle/src/reconciler/error.rs
@@ -2,29 +2,33 @@
//! run. These are mostly internal to the rendering engine itself, but could potentially
//! show up elsewhere.
-use std::fmt;
-use std::error::Error;
+use core::any::Any;
+use crate::reconciler::key::ComponentKey;
+
+#[derive(Debug)]
pub enum RenderEngineError {
- InvalidKeyError
+ InvalidKey,
+ InvalidRootComponent,
+ InvalidComponentKey(ComponentKey)
}
-impl fmt::Display for RenderEngineError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- RenderEngineError::InvalidKeyError => write!(f, "An invalid key was passed to the render engine.")
+impl std::fmt::Display for RenderEngineError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match *self {
+ RenderEngineError::InvalidComponentKey(ref node) => write!(f, "Invalid component key {:?}", node),
+ RenderEngineError::InvalidRootComponent => write!(f, "Invalid component type! Root nodes must be a natively backed node."),
+ RenderEngineError::InvalidKey => write!(f, "An invalid key was passed to the render engine.")
}
}
}
-impl fmt::Debug for RenderEngineError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- RenderEngineError::InvalidKeyError => write!(f, "An invalid key was passed to the render engine: {{ file: {}, line: {} }}", file!(), line!())
+impl std::error::Error for RenderEngineError {
+ fn description(&self) -> &str {
+ match *self {
+ RenderEngineError::InvalidComponentKey(_) => "The key is not part of the component storage instance",
+ RenderEngineError::InvalidRootComponent => "The root component must be a natively backed Component instance.",
+ RenderEngineError::InvalidKey => "An invalid key was passed to the render engine."
}
}
}
-
-impl Error for RenderEngineError {
-
-}
diff --git a/lifecycle/src/reconciler/key.rs b/lifecycle/src/reconciler/key.rs
new file mode 100644
index 0000000..d348ec8
--- /dev/null
+++ b/lifecycle/src/reconciler/key.rs
@@ -0,0 +1,51 @@
+//! Implements an auto-incrementing ID for Component instances.
+
+use std::sync::Mutex;
+
+use alchemy_styles::lazy_static;
+
+lazy_static! {
+ /// Global stretch instance id allocator.
+ pub(crate) static ref INSTANCE_ALLOCATOR: Mutex = Mutex::new(Allocator::new());
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) struct Id {
+ id: u32
+}
+
+pub(crate) struct Allocator {
+ new_id: u32
+}
+
+impl Allocator {
+ pub fn new() -> Self {
+ Allocator { new_id: 1 }
+ }
+
+ pub fn allocate(&mut self) -> Id {
+ let id = self.new_id;
+ self.new_id += 1;
+ Id { id: id }
+ }
+}
+
+/// Used as a key for Component storage. Component instances receive these
+/// in their constructor methods, and should retain them as a tool to update their
+/// state.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ComponentKey {
+ pub(crate) instance: Id,
+ pub(crate) local: Id,
+}
+
+impl ComponentKey {
+ /// A placeholder value, used purely for ensuring the diffing algorithm remains
+ /// readable by reducing some unwrapping hell.
+ pub fn placeholder() -> ComponentKey {
+ ComponentKey {
+ instance: Id { id: 0 },
+ local: Id { id: 0 }
+ }
+ }
+}
diff --git a/lifecycle/src/reconciler/mod.rs b/lifecycle/src/reconciler/mod.rs
index 344d0e5..0fd48b1 100644
--- a/lifecycle/src/reconciler/mod.rs
+++ b/lifecycle/src/reconciler/mod.rs
@@ -6,71 +6,73 @@ use std::collections::HashMap;
use std::error::Error;
use std::mem::{discriminant, swap};
-use uuid::Uuid;
-
-use alchemy_styles::{Stretch, THEME_ENGINE};
-use alchemy_styles::styles::{Style, Dimension};
-use alchemy_styles::number::Number;
-use alchemy_styles::geometry::Size;
+use alchemy_styles::THEME_ENGINE;
+use alchemy_styles::styles::{Appearance,Dimension, Number, Size, Style};
use crate::traits::Component;
use crate::rsx::{Props, RSX, VirtualNode};
-mod error;
+use alchemy_styles::stretch::node::{Node as StyleNode, Stretch as LayoutStore};
+
+pub mod key;
+use key::ComponentKey;
+
+pub mod storage;
+use storage::ComponentStore;
+
+pub mod error;
use error::RenderEngineError;
// This is never actually created, it's just to satisfy the fact that View
// is defined in the core crate, which we can't import here without creating a
// circular dependency.
struct StubView;
-impl Component for StubView {}
+impl Component for StubView {
+ fn constructor(key: ComponentKey) -> StubView {
+ StubView {}
+ }
+}
pub struct RenderEngine {
- pending_state_updates: Mutex>,
- trees: Mutex>
+ queued_state_updates: Mutex>,
+ components: Mutex,
+ layouts: Mutex
}
impl RenderEngine {
pub(crate) fn new() -> RenderEngine {
RenderEngine {
- pending_state_updates: Mutex::new(vec![]),
- trees: Mutex::new(HashMap::new())
+ queued_state_updates: Mutex::new(vec![]),
+ components: Mutex::new(ComponentStore::new()),
+ layouts: Mutex::new(LayoutStore::new())
}
}
+ // pub fn queue_update_for(&self, component_ptr: usize, updater: Box Component + Send + Sync + 'static>) {
+ // }
+
/// `Window`'s (or anything "root" in nature) need to register with the
/// reconciler for things like setState to work properly. When they do so,
/// they get a key back. When they want to instruct the global `RenderEngine`
/// to re-render or update their tree, they pass that key and whatever the new tree
/// should be.
- pub fn register_root_component(&self, instance: C) -> Uuid {
- let mut root_node = RSX::node("root", || {
- Arc::new(RwLock::new(StubView {}))
- }, {
- let mut props = Props::default();
- props.styles = "root".into();
- props
- });
-
- let mut stretch = Stretch::new();
- if let RSX::VirtualNode(root) = &mut root_node {
- let mut style = Style::default();
- style.size = Size {
- width: Dimension::Points(600.),
- height: Dimension::Points(600.)
- };
-
- root.instance = Some(Arc::new(RwLock::new(instance)));
- root.layout_node = match stretch.new_node(style, vec![]) {
- Ok(node) => Some(node),
- Err(e) => { None }
- }
+ pub fn register_root_component(&self, instance: C) -> Result> {
+ // Conceivably, this doesn't NEED to be a thing... but for now it is. If you've stumbled
+ // upon here, wayward traveler, in need of a non-native-root-component, please open an
+ // issue to discuss. :)
+ if !instance.has_native_backing_node() {
+ return Err(Box::new(RenderEngineError::InvalidRootComponent {}));
}
-
- let key = Uuid::new_v4();
- let mut trees = self.trees.lock().unwrap();
- trees.insert(key, (root_node, stretch));
- key
+
+ let layout_key = {
+ let style = Style::default();
+ let mut layouts = self.layouts.lock().unwrap();
+ Some(layouts.new_node(style, vec![])?)
+ };
+
+ let mut components = self.components.lock().unwrap();
+ let component_key = components.new_node(instance, layout_key, vec![])?;
+ Ok(component_key)
}
/// Given a key, and a new root tree, will diff the tree structure (position, components,
@@ -80,36 +82,37 @@ impl RenderEngine {
/// the new tree before discarding the old tree.
///
/// This calls the necessary component lifecycles per-component.
- pub fn diff_and_render_root(&self, key: &Uuid, child: RSX) -> Result<(), Box> {
+ pub fn diff_and_render_root(&self, key: &ComponentKey, child: RSX) -> Result<(), Box> {
+ /*
let mut new_root = RSX::node("root", || {
- Arc::new(RwLock::new(StubView {}))
+ Box::new(StubView {})
}, {
let mut props = Props::default();
props.styles = "root".into();
props
+ }, match child {
+ RSX::VirtualNode(mut child) => {
+ let mut children = vec![];
+
+ if child.tag == "Fragment" {
+ children.append(&mut child.children);
+ } else {
+ children.push(RSX::VirtualNode(child));
+ }
+
+ children
+ },
+
+ // If it's an RSX::None or RSX::VirtualText, we'll just do nothing, as... one
+ // requires nothing, and one isn't supported unless it's inside a tag, and
+ // we know the root element isn't a if we're here.
+ _ => vec![]
});
- // If it's an RSX::None, or a RSX::VirtualText, we do nothing, as... one
- // requires nothing, and one isn't supported unless it's inside a tag.
- if let RSX::VirtualNode(mut child) = child {
- if let RSX::VirtualNode(new_root_node) = &mut new_root {
- if child.tag == "Fragment" {
- new_root_node.children.append(&mut child.children);
- } else {
- new_root_node.children.push(RSX::VirtualNode(child));
- }
- }
- }
-
let mut trees = self.trees.lock().unwrap();
- let (old_root, mut stretch) = trees.remove(key).ok_or_else(|| RenderEngineError::InvalidKeyError {})?;
+ let (old_root, mut stretch) = trees.remove(key).ok_or_else(|| RenderEngineError::InvalidKey {})?;
let patched_new_root = diff_and_patch_trees(old_root, new_root, &mut stretch, 0)?;
- /*let window_size = Size {
- width: Number::Defined(600.),
- height: Number::Defined(600.)
- };*/
-
if let RSX::VirtualNode(node) = &patched_new_root {
if let Some(layout_node) = &node.layout_node {
stretch.compute_layout(*layout_node, Size {
@@ -120,375 +123,7 @@ impl RenderEngine {
}
}
- trees.insert(*key, (patched_new_root, stretch));
+ trees.insert(*key, (patched_new_root, stretch));*/
Ok(())
}
}
-
-/// Given two node trees, will compare, diff, and apply changes in a recursive fashion.
-pub fn diff_and_patch_trees(old: RSX, new: RSX, stretch: &mut Stretch, depth: usize) -> Result> {
- // Whether we replace or not depends on a few things. If we're working on two different node
- // types (text vs node), if the node tags are different, or if the key (in some cases) is
- // different.
- let is_replace = match discriminant(&old) != discriminant(&new) {
- true => true,
- false => {
- if let (RSX::VirtualNode(old_element), RSX::VirtualNode(new_element)) = (&old, &new) {
- old_element.tag != new_element.tag
- } else {
- false
- }
- }
- };
-
- match (old, new) {
- (RSX::VirtualNode(mut old_element), RSX::VirtualNode(mut new_element)) => {
- if is_replace {
- // Do something different in here...
- //let mut mounted = mount_component_tree(new_tree);
- // unmount_component_tree(old_tree);
- // Swap them in memory, copy any layout + etc as necessary
- // append, link layout nodes, etc
- return Ok(RSX::VirtualNode(new_element));
- }
-
- // If we get here, it's an update to an existing element. This means a cached Component
- // instance might exist, and we want to keep it around and reuse it if possible. Let's check
- // and do some swapping action to handle it.
- //
- // These need to move to the new tree, since we always keep 'em. We also wanna cache a
- // reference to our content view.
- swap(&mut old_element.instance, &mut new_element.instance);
- swap(&mut old_element.layout_node, &mut new_element.layout_node);
-
- // For the root tag, which is usually the content view of the Window, we don't want to
- // perform the whole render/component lifecycle routine. It's a special case element,
- // where the Window (or other root element) patches in the output of a render method
- // specific to that object. An easy way to handle this is the depth parameter - in
- // fact, it's why it exists. Depth 0 should be considered special and skip the
- // rendering phase.
- if depth > 0 {
- // diff props, set new props
- // instance.get_derived_state_from_props()
-
- if let Some(instance) = &mut new_element.instance {
- // diff props, set new props
- // instance.get_derived_state_from_props()
-
- //if instance.should_component_update() {
- // instance.render() { }
- // instance.get_snapshot_before_update()
- // apply changes
- //instance.component_did_update();
- //} else {
- // If should_component_update() returns false, then we want to take the
- // children from the old node, move them to the new node, and recurse into
- // that tree instead.
- //}
- }
- }
-
- // This None path should never be hit, we just need to use a rather verbose pattern
- // here. It's unsightly, I know.
- let is_native_backed = match &new_element.instance {
- Some(instance) => {
- let lock = instance.read().unwrap();
- lock.has_native_backing_node()
- },
- None => false
- };
-
- // There is probably a nicer way to do this that doesn't allocate as much, and I'm open
- // to revisiting it. Platforms outside of Rust allocate far more than this, though, and
- // in general the whole "avoid allocations" thing is fear mongering IMO. Revisit later.
- //
- // tl;dr we allocate a new Vec that's equal to the length of our new children, and
- // then swap it on our (owned) node... it's safe, as we own it. This allows us to
- // iterate and dodge the borrow checker.
- let mut children: Vec = Vec::with_capacity(new_element.children.len());
- std::mem::swap(&mut children, &mut new_element.children);
-
- old_element.children.reverse();
- for new_child_tree in children {
- match old_element.children.pop() {
- // A matching child in the old tree means we can recurse right back into the
- // update phase.
- Some(old_child_tree) => {
- let updated = diff_and_patch_trees(old_child_tree, new_child_tree, stretch, depth + 1)?;
- new_element.children.push(updated);
- },
-
- // If there's no matching child in the old tree, this is a new Component and we
- // can feel free to mount/connect it.
- None => {
- if let RSX::VirtualNode(new_el) = new_child_tree {
- let mut mounted = mount_component_tree(new_el, stretch)?;
-
- // Link the layout nodes, handle the appending, etc.
- // This happens inside mount_component_tree, but that only handles that
- // specific tree. Think of this step as joining two trees in the graph.
- if is_native_backed {
- find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
- }
-
- new_element.children.push(RSX::VirtualNode(mounted));
- }
- }
- }
- }
-
- // Trim the fat - more children in the old tree than the new one means we gonna be
- // droppin'. We need to send unmount lifecycle calls to these, and break any links we
- // have (e.g, layout, backing view tree, etc).
- loop {
- match old_element.children.pop() {
- Some(child) => {
- if let RSX::VirtualNode(mut old_child) = child {
- unmount_component_tree(&mut old_child, stretch)?;
- }
- },
-
- None => { break; }
- }
- }
-
- Ok(RSX::VirtualNode(new_element))
- }
-
- // We're comparing two text nodes. Realistically... this requires nothing from us, because
- // the tag (or any other component instance, if it desires) should handle it.
- (RSX::VirtualText(_), RSX::VirtualText(text)) => {
- Ok(RSX::VirtualText(text))
- }
-
- // These are all edge cases that shouldn't get hit. In particular:
- //
- // - VirtualText being replaced by VirtualNode should be caught by the discriminant check
- // in the beginning of this function, which registers as a replace/mount.
- // - VirtualNode being replaced with VirtualText is the same scenario as above.
- // - The (RSX::None, ...) checks are to shut the compiler up; we never store the RSX::None
- // return value, as it's mostly a value in place for return signature usability. Thus,
- // these should quite literally never register.
- //
- // This goes without saying, but: never ever store RSX::None lol
- (RSX::VirtualText(_), RSX::VirtualNode(_)) | (RSX::VirtualNode(_), RSX::VirtualText(_)) |
- (RSX::None, RSX::VirtualText(_)) | (RSX::None, RSX::VirtualNode(_)) | (RSX::None, RSX::None) |
- (RSX::VirtualNode(_), RSX::None) | (RSX::VirtualText(_), RSX::None) => {
- unreachable!("Unequal variant discriminants should already have been handled.");
- }
- }
-}
-
-/// Walks the tree and applies styles. This happens after a layout computation, typically.
-pub(crate) fn walk_and_apply_styles(node: &VirtualNode, layout_manager: &mut Stretch) -> Result<(), Box> {
- if let (Some(layout_node), Some(instance)) = (node.layout_node, &node.instance) {
- let component = instance.write().unwrap();
- component.apply_styles(
- layout_manager.layout(layout_node)?,
- layout_manager.style(layout_node)?
- );
- }
-
- for child in &node.children {
- if let RSX::VirtualNode(child_node) = child {
- walk_and_apply_styles(child_node, layout_manager)?;
- }
- }
-
- Ok(())
-}
-
-/// Given a tree, will walk the branches until it finds the next root nodes to connect.
-/// While this sounds slow, in practice it rarely has to go far in any direction.
-fn find_and_link_layout_nodes(parent_node: &mut VirtualNode, child_tree: &mut VirtualNode, stretch: &mut Stretch) -> Result<(), Box> {
- if let (Some(parent_instance), Some(child_instance)) = (&mut parent_node.instance, &mut child_tree.instance) {
- if let (Some(parent_layout_node), Some(child_layout_node)) = (&parent_node.layout_node, &child_tree.layout_node) {
- stretch.add_child(*parent_layout_node, *child_layout_node)?;
-
- let parent_component = parent_instance.write().unwrap();
- let child_component = child_instance.read().unwrap();
- parent_component.append_child_component(&*child_component);
-
- return Ok(());
- }
- }
-
- for child in child_tree.children.iter_mut() {
- if let RSX::VirtualNode(child_tree) = child {
- find_and_link_layout_nodes(parent_node, child_tree, stretch)?;
- }
- }
-
- Ok(())
-}
-
-/// Recursively constructs a Component tree. This entails adding it to the backing
-/// view tree, firing various lifecycle methods, and ensuring that nodes for layout
-/// passes are configured.
-///
-/// In the future, this would ideally return patch-sets for the backing layer or something.
-fn mount_component_tree(mut new_element: VirtualNode, stretch: &mut Stretch) -> Result> {
- let instance = (new_element.create_component_fn)();
-
- let mut is_native_backed = false;
-
- let rendered = {
- let component = instance.read().unwrap();
- // instance.get_derived_state_from_props(props)
-
- is_native_backed = component.has_native_backing_node();
-
- if is_native_backed {
- let mut style = Style::default();
- THEME_ENGINE.configure_style_for_keys(&new_element.props.styles, &mut style);
-
- let layout_node = stretch.new_node(style, vec![])?;
- new_element.layout_node = Some(layout_node);
- }
-
- component.render(&new_element.props)
- };
-
- // instance.get_snapshot_before_update()
-
- new_element.instance = Some(instance);
-
- let mut children = match rendered {
- Ok(opt) => match opt {
- RSX::VirtualNode(child) => {
- let mut children = vec![];
-
- // We want to support Components being able to return arbitrary iteratable
- // elements, but... well, it's not quite that simple. Thus we'll offer a
- // tag similar to what React does, which just hoists the children out of it and
- // discards the rest.
- if child.tag == "Fragment" {
- for child_node in child.props.children {
- if let RSX::VirtualNode(node) = child_node {
- let mut mounted = mount_component_tree(node, stretch)?;
-
- if is_native_backed {
- find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
- }
-
- children.push(RSX::VirtualNode(mounted));
- }
- }
- } else {
- let mut mounted = mount_component_tree(child, stretch)?;
-
- if is_native_backed {
- find_and_link_layout_nodes(&mut new_element, &mut mounted, stretch)?;
- }
-
- children.push(RSX::VirtualNode(mounted));
- }
-
- children
- },
-
- // If a Component renders nothing (or this is a Text string, which we do nothing with)
- // that's totally fine.
- _ => vec![]
- },
-
- Err(e) => {
- // return an RSX::VirtualNode(ErrorComponentView) or something?
- /* instance.get_derived_state_from_error(e) */
- // render error state or something I guess?
- /* instance.component_did_catch(e, info) */
- eprintln!("Error rendering: {}", e);
- vec![]
- }
- };
-
- new_element.children.append(&mut children);
-
- if let Some(instance) = &mut new_element.instance {
- let mut component = instance.write().unwrap();
- component.component_did_mount(&new_element.props);
- }
-
- Ok(new_element)
-}
-
-/// Walk the tree and unmount Component instances. This means we fire the
-/// `component_will_unmount` hook and remove the node(s) from their respective trees.
-///
-/// This fires the hooks from a recursive inward-out pattern; that is, the deepest nodes in the tree
-/// are the first to go, ensuring that everything is properly cleaned up.
-fn unmount_component_tree(old_element: &mut VirtualNode, stretch: &mut Stretch) -> Result<(), Box> {
- // We only need to recurse on VirtualNodes. Text and so on will automagically drop
- // because we don't support freeform text, it has to be inside a at all times.
- for child in old_element.children.iter_mut() {
- if let RSX::VirtualNode(child_element) = child {
- unmount_component_tree(child_element, stretch)?;
- }
- }
-
- // Fire the appropriate lifecycle method and then remove the node from the underlying
- // graph. Remember that a Component can actually not necessarily have a native backing
- // node, hence our necessary check.
- if let Some(old_component) = &mut old_element.instance {
- let mut component = old_component.write().unwrap();
- component.component_will_unmount(&old_element.props);
-
- /*if let Some(view) = old_component.get_native_backing_node() {
- if let Some(native_view) = replace_native_view {
- //replace_view(&view, &native_view);
- } else {
- //remove_view(&view);
- }
- }*/
- }
-
- // Rather than try to keep track of parent/child stuff for removal... just obliterate it,
- // the underlying library does a good job of killing the links anyway.
- if let Some(layout_node) = &mut old_element.layout_node {
- stretch.set_children(*layout_node, vec![])?;
- }
-
- Ok(())
-}
-
-/*let mut add_attributes: HashMap<&str, &str> = HashMap::new();
-let mut remove_attributes: Vec<&str> = vec![];
-
-// TODO: -> split out into func
-for (new_attr_name, new_attr_val) in new_element.attrs.iter() {
- match old_element.attrs.get(new_attr_name) {
- Some(ref old_attr_val) => {
- if old_attr_val != &new_attr_val {
- add_attributes.insert(new_attr_name, new_attr_val);
- }
- }
- None => {
- add_attributes.insert(new_attr_name, new_attr_val);
- }
- };
-}
-
-// TODO: -> split out into func
-for (old_attr_name, old_attr_val) in old_element.attrs.iter() {
- if add_attributes.get(&old_attr_name[..]).is_some() {
- continue;
- };
-
- match new_element.attrs.get(old_attr_name) {
- Some(ref new_attr_val) => {
- if new_attr_val != &old_attr_val {
- remove_attributes.push(old_attr_name);
- }
- }
- None => {
- remove_attributes.push(old_attr_name);
- }
- };
-}
-
-if add_attributes.len() > 0 {
- patches.push(Patch::AddAttributes(*cur_node_idx, add_attributes));
-}
-if remove_attributes.len() > 0 {
- patches.push(Patch::RemoveAttributes(*cur_node_idx, remove_attributes));
-}*/
diff --git a/lifecycle/src/reconciler/storage.rs b/lifecycle/src/reconciler/storage.rs
new file mode 100644
index 0000000..3138f13
--- /dev/null
+++ b/lifecycle/src/reconciler/storage.rs
@@ -0,0 +1,163 @@
+//! Implements storage for Component instances, in a way that allows us to
+//! short-circuit the rendering process so we don't have to re-scan entire
+//! tree structures when updating state.
+
+use std::collections::HashMap;
+
+pub use alchemy_styles::Appearance;
+use alchemy_styles::stretch::node::{Node as LayoutNode};
+
+use crate::reconciler::error::{RenderEngineError as Error};
+use crate::reconciler::key::{Allocator, Id, INSTANCE_ALLOCATOR, ComponentKey};
+use crate::traits::Component;
+
+/// This is a clone of a structure you'll also find over in stretch. We do this separately
+/// here for two reasons.
+///
+/// - First, a Component may have children that don't require styles or layout passes. These nodes
+/// should not have `Style` or `Appearance` nodes created, but we do need the correct parent/child
+/// relationships in place.
+/// - The `Storage` pieces of stretch are realistically an implementation detail that we shouldn't
+/// rely on.
+struct Storage(HashMap);
+
+impl Storage {
+ pub fn new() -> Self {
+ Storage(HashMap::new())
+ }
+
+ pub fn get(&self, key: ComponentKey) -> Result<&T, Error> {
+ match self.0.get(&key) {
+ Some(v) => Ok(v),
+ None => Err(Error::InvalidComponentKey(key)),
+ }
+ }
+
+ pub fn get_mut(&mut self, key: ComponentKey) -> Result<&mut T, Error> {
+ match self.0.get_mut(&key) {
+ Some(v) => Ok(v),
+ None => Err(Error::InvalidComponentKey(key)),
+ }
+ }
+
+ pub fn insert(&mut self, key: ComponentKey, value: T) -> Option {
+ self.0.insert(key, value)
+ }
+}
+
+impl std::ops::Index<&ComponentKey> for Storage {
+ type Output = T;
+
+ fn index(&self, idx: &ComponentKey) -> &T {
+ &(self.0)[idx]
+ }
+}
+
+pub struct Instance {
+ component: Box,
+ appearance: Appearance,
+ layout: Option
+}
+
+pub(crate) struct ComponentStore {
+ id: Id,
+ nodes: Allocator,
+ components: Storage,
+ parents: Storage>,
+ children: Storage>
+}
+
+impl ComponentStore {
+ pub fn new() -> Self {
+ ComponentStore {
+ id: INSTANCE_ALLOCATOR.lock().unwrap().allocate(),
+ nodes: Allocator::new(),
+ components: Storage::new(),
+ parents: Storage::new(),
+ children: Storage::new()
+ }
+ }
+
+ fn allocate_node(&mut self) -> ComponentKey {
+ let local = self.nodes.allocate();
+ ComponentKey { instance: self.id, local }
+ }
+
+ pub fn new_node(&mut self, component: C, layout_key: Option, children: Vec) -> Result {
+ let key = self.allocate_node();
+
+ for child in &children {
+ self.parents.get_mut(*child)?.push(key);
+ }
+
+ self.components.insert(key, Instance {
+ component: Box::new(component),
+ appearance: Appearance::default(),
+ layout: layout_key
+ });
+
+ self.parents.insert(key, Vec::with_capacity(1));
+ self.children.insert(key, children);
+
+ Ok(key)
+ }
+
+ pub fn add_child(&mut self, key: ComponentKey, child: ComponentKey) -> Result<(), Error> {
+ self.parents.get_mut(child)?.push(key);
+ self.children.get_mut(key)?.push(child);
+ Ok(())
+ }
+
+ pub fn set_children(&mut self, key: ComponentKey, children: Vec) -> Result<(), Error> {
+ // Remove node as parent from all its current children.
+ for child in self.children.get(key)? {
+ self.parents.get_mut(*child)?.retain(|p| *p != key);
+ }
+
+ *self.children.get_mut(key)? = Vec::with_capacity(children.len());
+
+ // Build up relation node <-> child
+ for child in children {
+ self.parents.get_mut(child)?.push(key);
+ self.children.get_mut(key)?.push(child);
+ }
+
+ Ok(())
+ }
+
+ pub fn remove_child(&mut self, key: ComponentKey, child: ComponentKey) -> Result {
+ match self.children(key)?.iter().position(|n| *n == child) {
+ Some(index) => self.remove_child_at_index(key, index),
+ None => Err(Error::InvalidComponentKey(child)),
+ }
+ }
+
+ pub fn remove_child_at_index(&mut self, key: ComponentKey, index: usize) -> Result {
+ let child = self.children.get_mut(key)?.remove(index);
+ self.parents.get_mut(child)?.retain(|p| *p != key);
+ Ok(child)
+ }
+
+ pub fn replace_child_at_index(&mut self, key: ComponentKey, index: usize, child: ComponentKey) -> Result {
+ self.parents.get_mut(child)?.push(key);
+ let old_child = std::mem::replace(&mut self.children.get_mut(key)?[index], child);
+ self.parents.get_mut(old_child)?.retain(|p| *p != key);
+ Ok(old_child)
+ }
+
+ pub fn children(&self, key: ComponentKey) -> Result, Error> {
+ self.children.get(key).map(Clone::clone)
+ }
+
+ pub fn child_count(&self, key: ComponentKey) -> Result {
+ self.children.get(key).map(Vec::len)
+ }
+
+ pub fn get(&self, key: ComponentKey) -> Result<&Instance, Error> {
+ self.components.get(key)
+ }
+
+ pub fn get_mut(&mut self, key: ComponentKey) -> Result<&mut Instance, Error> {
+ self.components.get_mut(key)
+ }
+}
diff --git a/lifecycle/src/rsx/mod.rs b/lifecycle/src/rsx/mod.rs
index e85338f..ad3bcf0 100644
--- a/lifecycle/src/rsx/mod.rs
+++ b/lifecycle/src/rsx/mod.rs
@@ -3,7 +3,6 @@
//! uses these to build and alter UI; they're typically returned from `render()`
//! methods.
-use std::sync::{Arc, RwLock};
use std::fmt::{Debug, Display};
mod virtual_node;
@@ -15,6 +14,7 @@ pub use virtual_text::VirtualText;
mod props;
pub use props::Props;
+use crate::reconciler::key::ComponentKey;
use crate::traits::Component;
/// An enum representing the types of nodes that the
@@ -31,16 +31,15 @@ impl RSX {
/// this yourself; the `rsx! {}` macro handles this for you.
pub fn node(
tag: &'static str,
- create_fn: fn() -> Arc>,
- props: Props
+ create_fn: fn(key: ComponentKey) -> Box,
+ props: Props,
+ children: Vec
) -> RSX {
RSX::VirtualNode(VirtualNode {
tag: tag,
create_component_fn: create_fn,
- instance: None,
- layout_node: None,
- props: props,
- children: vec![]
+ props: Some(props),
+ children: children
})
}
diff --git a/lifecycle/src/rsx/props.rs b/lifecycle/src/rsx/props.rs
index 580f802..8d297c9 100644
--- a/lifecycle/src/rsx/props.rs
+++ b/lifecycle/src/rsx/props.rs
@@ -39,6 +39,15 @@ pub struct Props {
}
impl Props {
+ pub fn new(key: String, styles: StylesList, attributes: HashMap<&'static str, AttributeType>) -> Props {
+ Props {
+ attributes: attributes,
+ children: vec![],
+ key: key,
+ styles: styles
+ }
+ }
+
/// Returns a Vec of RSX nodes, which are really just cloned pointers for the most part.
pub fn children(&self) -> Vec {
self.children.clone()
diff --git a/lifecycle/src/rsx/virtual_node.rs b/lifecycle/src/rsx/virtual_node.rs
index e8f3be0..df3ec57 100644
--- a/lifecycle/src/rsx/virtual_node.rs
+++ b/lifecycle/src/rsx/virtual_node.rs
@@ -1,17 +1,14 @@
//! Implements the `RSX::VirtualNode` struct, which is a bit of a recursive
//! structure.
-use std::sync::{Arc, RwLock};
use std::fmt::{Display, Debug};
-use alchemy_styles::node::Node;
-
-use crate::traits::Component;
+use crate::reconciler::key::ComponentKey;
use crate::rsx::{RSX, Props};
+use crate::traits::Component;
/// A VirtualNode is akin to an `Element` in React terms. Here, we provide a way
-/// for lazy `Component` instantiation, along with storage for things like layout nodes,
-/// properties, children and so on.
+/// for lazy `Component` instantiation, properties, children and so on.
#[derive(Clone)]
pub struct VirtualNode {
/// Used in debugging/printing/etc.
@@ -19,22 +16,17 @@ pub struct VirtualNode {
/// `Component` instances are created on-demand, if the reconciler deems it be so. This
/// is a closure that should return an instance of the correct type.
- pub create_component_fn: fn() -> Arc>,
+ pub create_component_fn: fn(key: ComponentKey) -> Box,
- /// A cached component instance, which is transferred between trees. Since `Component`
- /// instances are lazily created, this is an `Option`, and defaults to `None`.
- pub instance: Option>>,
-
- /// A cached `Node` for computing `Layout` with `Stretch`. Some components may not have
- /// a need for layout (e.g, if they don't have a backing node), and thus this is optional.
+ /// `Props`, which are to be passed to this `Component` at various lifecycle methods. Once
+ /// the reconciler takes ownership of this VirtualNode, these props are moved to a different
+ /// location - thus, you shouldn't rely on them for anything unless you specifically keep
+ /// ownership of a VirtualNode.
///
- /// The reconciler will handle bridging tree structures as necessary.
- pub layout_node: Option,
+ /// This aspect of functionality may be pulled in a later release if it causes too many issues.
+ pub props: Option,
- /// `Props`, which are to be passed to this `Component` at various lifecycle methods.
- pub props: Props,
-
- /// Computed children get stored here.
+ ///
pub children: Vec
}
diff --git a/lifecycle/src/traits.rs b/lifecycle/src/traits.rs
index c0384b8..0831560 100644
--- a/lifecycle/src/traits.rs
+++ b/lifecycle/src/traits.rs
@@ -1,11 +1,11 @@
//! Traits that are used in Alchemy. Alchemy implements a React-based Component
//! lifecycle, coupled with a delegate pattern inspired by those found in AppKit/UIKit.
-use std::sync::Arc;
-
-use alchemy_styles::styles::{Layout, Style};
+use alchemy_styles::styles::{Appearance, Layout};
+//use crate::RENDER_ENGINE;
use crate::error::Error;
+use crate::reconciler::key::ComponentKey;
use crate::rsx::{RSX, Props};
/// A per-platform wrapped Pointer type, used for attaching views/widgets.
@@ -16,6 +16,11 @@ pub type PlatformSpecificNodeType = objc_id::ShareId;
#[cfg(not(feature = "cocoa"))]
pub type PlatformSpecificNodeType = ();
+/*fn update Box + Send + Sync + 'static>(component: &Component, updater: F) {
+ let component_ptr = component as *const C as usize;
+ RENDER_ENGINE.queue_update_for(component_ptr, Box::new(updater));
+}*/
+
/// Each platform tends to have their own startup routine, their own runloop, and so on.
/// Alchemy recognizes this and provides an `AppDelegate` that receives events at a system
/// level and allows the user to operate within the established framework per-system.
@@ -75,6 +80,8 @@ pub trait State {}
/// doesn't feel comfortable in Rust, in any way I tried). If you think you have an interesting
/// proposal for this, feel free to open an issue!
pub trait Component: Send + Sync {
+ fn constructor(key: ComponentKey) -> Self where Self: Sized;
+
/// Indicates whether a Component instance carries a native backing node. If you return `true`
/// from this, the reconciler will opt-in to the native backing layer. Returns `false` by
/// default.
@@ -89,16 +96,15 @@ pub trait Component: Send + Sync {
/// If you implement a Native-backed component, you'll need to implement this. Given a
/// `component`, you need to instruct the system how to replace it in the tree at your point.
- fn replace_child_component(&self, _component: Arc) {}
+ fn replace_child_component(&self, _component: &Component) {}
/// If you implement a Native-backed component, you'll need to implement this. Given a
/// `component`, you need to instruct the system how to remove it from the tree at your point.
- fn remove_child_component(&self, _component: Arc) {}
+ fn remove_child_component(&self, _component: &Component) {}
- /// Given a computed `layout`, and an accompanying `Style` (which holds appearance-based
- /// styles, like colors), this method should transform them into appropriate calls to the
- /// backing native node.
- fn apply_styles(&self, _layout: &Layout, _style: &Style) {}
+ /// Given a configured 'appearance' and computed `layout`, this method should transform them
+ /// into appropriate calls to the backing native node.
+ fn apply_styles(&self, _appearance: &Appearance, _layout: &Layout) {}
/// Invoked right before calling the render method, both on the initial mount and on subsequent updates.
/// It should return an object to update the state, or null to update nothing.
diff --git a/macros/src/rsx.rs b/macros/src/rsx.rs
index 1ea4818..8dbf15b 100644
--- a/macros/src/rsx.rs
+++ b/macros/src/rsx.rs
@@ -220,21 +220,16 @@ impl Element {
let component_name = Literal::string(&typename.to_string());
Ok(quote!(
- alchemy::RSX::node(#component_name, || {
- std::sync::Arc::new(std::sync::RwLock::new(#typename::default()))
- }, alchemy::Props {
- attributes: {
- let mut attributes = std::collections::HashMap::new();
- #attributes
- attributes
- },
- children: {
- let mut children = vec![];
- #children
- children
- },
- key: "".into(),
- styles: #styles
+ alchemy::RSX::node(#component_name, |key| {
+ Box::new(#typename::constructor(key))
+ }, alchemy::Props::new("".into(), #styles, {
+ let mut attributes = std::collections::HashMap::new();
+ #attributes
+ attributes
+ }), {
+ let mut children = vec![];
+ #children
+ children
})
))
}
diff --git a/styles/src/engine.rs b/styles/src/engine.rs
index 4b00481..da41ee5 100644
--- a/styles/src/engine.rs
+++ b/styles/src/engine.rs
@@ -17,8 +17,10 @@ use std::collections::HashMap;
use toml;
use serde::Deserialize;
+use crate::stretch::style::Style;
+
use crate::StylesList;
-use crate::styles::Style;
+use crate::styles::Appearance;
use crate::stylesheet::StyleSheet;
static CONFIG_FILE_NAME: &str = "alchemy.toml";
@@ -85,13 +87,19 @@ impl ThemeEngine {
/// Given a theme key, style keys, and a style, configures the style for layout
/// and appearance.
- pub fn configure_style_for_keys_in_theme(&self, theme: &str, keys: &StylesList, style: &mut Style) {
+ pub fn configure_style_for_keys_in_theme(
+ &self,
+ theme: &str,
+ keys: &StylesList,
+ style: &mut Style,
+ appearance: &mut Appearance
+ ) {
let themes = self.themes.read().unwrap();
match themes.get(theme) {
Some(theme) => {
for key in &keys.0 {
- theme.apply_styles(key, style);
+ theme.apply_styles(key, style, appearance);
}
},
@@ -102,8 +110,8 @@ impl ThemeEngine {
}
/// The same logic as `configure_style_for_keys_in_theme`, but defaults to the default theme.
- pub fn configure_style_for_keys(&self, keys: &StylesList, style: &mut Style) {
- self.configure_style_for_keys_in_theme("default", keys, style)
+ pub fn configure_styles_for_keys(&self, keys: &StylesList, style: &mut Style, appearance: &mut Appearance) {
+ self.configure_style_for_keys_in_theme("default", keys, style, appearance)
}
}
diff --git a/styles/src/lib.rs b/styles/src/lib.rs
index 1a0b361..71245a6 100644
--- a/styles/src/lib.rs
+++ b/styles/src/lib.rs
@@ -9,10 +9,6 @@ pub use lazy_static::lazy_static;
#[cfg(feature="parser")]
#[macro_use] pub extern crate cssparser;
-mod stretch;
-pub use stretch::{geometry, node, number, result, Stretch, Error};
-pub use stretch::result::Layout;
-
pub mod color;
pub use color::Color;
@@ -25,12 +21,15 @@ pub use spacedlist::SpacedList;
mod spacedset;
pub use spacedset::SpacedSet;
+pub mod stretch;
+pub use stretch::result::Layout;
+
mod style_keys;
pub use style_keys::StyleKey;
pub type StylesList = SpacedSet;
pub mod styles;
-pub use styles::{Style, Styles};
+pub use styles::{Appearance, Styles};
pub mod stylesheet;
pub use stylesheet::StyleSheet;
diff --git a/styles/src/stretch/algo.rs b/styles/src/stretch/algo.rs
index e7ae038..8ef6948 100644
--- a/styles/src/stretch/algo.rs
+++ b/styles/src/stretch/algo.rs
@@ -1,14 +1,15 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
use core::any::Any;
use core::f32;
-use crate::node::{Node, Storage, Stretch};
-use crate::result;
-use crate::styles::*;
-
-use crate::number::Number::*;
-use crate::number::*;
-
-use crate::geometry::{Point, Rect, Size};
+use crate::stretch::node::{Node, Storage, Stretch};
+use crate::stretch::result;
+use crate::stretch::style::*;
+use crate::stretch::number::Number::*;
+use crate::stretch::number::*;
+use crate::stretch::geometry::{Point, Rect, Size};
#[derive(Debug, Clone)]
pub struct ComputeResult {
diff --git a/styles/src/stretch/geometry.rs b/styles/src/stretch/geometry.rs
index a56755d..9984bbc 100644
--- a/styles/src/stretch/geometry.rs
+++ b/styles/src/stretch/geometry.rs
@@ -1,7 +1,10 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
use core::ops::Add;
-use crate::number::Number;
-use crate::styles as style;
+use crate::stretch::number::Number;
+use crate::stretch::style;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Rect {
diff --git a/styles/src/stretch/id.rs b/styles/src/stretch/id.rs
index ce8cb3e..2f5af6f 100644
--- a/styles/src/stretch/id.rs
+++ b/styles/src/stretch/id.rs
@@ -1,6 +1,5 @@
-//! Identifier for a Node
-//!
-//!
+///! This module is included while awaiting an upstream merge in stretch proper.
+///! You should not rely on it, and consider it an implementation detail.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Id {
diff --git a/styles/src/stretch/mod.rs b/styles/src/stretch/mod.rs
index b262030..4eedcf3 100644
--- a/styles/src/stretch/mod.rs
+++ b/styles/src/stretch/mod.rs
@@ -1,13 +1,15 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
pub mod geometry;
pub mod node;
pub mod number;
pub mod result;
+pub mod style;
mod algo;
mod id;
-pub use crate::node::Stretch;
-
use core::any::Any;
#[derive(Debug)]
diff --git a/styles/src/stretch/node.rs b/styles/src/stretch/node.rs
index 3e70ea0..52b9e46 100644
--- a/styles/src/stretch/node.rs
+++ b/styles/src/stretch/node.rs
@@ -1,3 +1,6 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
use core::any::Any;
use std::collections::HashMap;
@@ -6,12 +9,12 @@ use std::sync::Mutex;
use lazy_static::lazy_static;
-use crate::geometry::Size;
+use crate::stretch::geometry::Size;
use crate::stretch::id;
-use crate::number::Number;
-use crate::result::{Cache, Layout};
-use crate::styles::*;
-use crate::Error;
+use crate::stretch::number::Number;
+use crate::stretch::result::{Cache, Layout};
+use crate::stretch::style::*;
+use crate::stretch::Error;
type MeasureFunc = Box) -> Result, Box> + Send + Sync + 'static>;
diff --git a/styles/src/stretch/number.rs b/styles/src/stretch/number.rs
index 1cccedd..5522d44 100644
--- a/styles/src/stretch/number.rs
+++ b/styles/src/stretch/number.rs
@@ -1,3 +1,6 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
use core::ops;
#[derive(Copy, Clone, PartialEq, Debug)]
diff --git a/styles/src/stretch/result.rs b/styles/src/stretch/result.rs
index 6cd60e5..015a554 100644
--- a/styles/src/stretch/result.rs
+++ b/styles/src/stretch/result.rs
@@ -1,6 +1,9 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
use crate::stretch::algo::ComputeResult;
-use crate::geometry::{Point, Size};
-use crate::number::Number;
+use crate::stretch::geometry::{Point, Size};
+use crate::stretch::number::Number;
#[derive(Copy, Debug, Clone)]
pub struct Layout {
diff --git a/styles/src/stretch/style.rs b/styles/src/stretch/style.rs
new file mode 100644
index 0000000..40af68c
--- /dev/null
+++ b/styles/src/stretch/style.rs
@@ -0,0 +1,335 @@
+//! This module is included while awaiting an upstream merge in stretch proper.
+//! You should not rely on it, and consider it an implementation detail.
+
+use crate::stretch::geometry::{Rect, Size};
+use crate::stretch::number::Number;
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AlignItems {
+ FlexStart,
+ FlexEnd,
+ Center,
+ Baseline,
+ Stretch,
+}
+
+impl Default for AlignItems {
+ fn default() -> AlignItems {
+ AlignItems::Stretch
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AlignSelf {
+ Auto,
+ FlexStart,
+ FlexEnd,
+ Center,
+ Baseline,
+ Stretch,
+}
+
+impl Default for AlignSelf {
+ fn default() -> AlignSelf {
+ AlignSelf::Auto
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AlignContent {
+ FlexStart,
+ FlexEnd,
+ Center,
+ Stretch,
+ SpaceBetween,
+ SpaceAround,
+}
+
+impl Default for AlignContent {
+ fn default() -> AlignContent {
+ AlignContent::Stretch
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Direction {
+ Inherit,
+ LTR,
+ RTL,
+}
+
+impl Default for Direction {
+ fn default() -> Direction {
+ Direction::Inherit
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Display {
+ Flex,
+ None,
+}
+
+impl Default for Display {
+ fn default() -> Display {
+ Display::Flex
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum FlexDirection {
+ Row,
+ Column,
+ RowReverse,
+ ColumnReverse,
+}
+
+impl Default for FlexDirection {
+ fn default() -> FlexDirection {
+ FlexDirection::Row
+ }
+}
+
+impl FlexDirection {
+ pub(crate) fn is_row(self) -> bool {
+ self == FlexDirection::Row || self == FlexDirection::RowReverse
+ }
+
+ pub(crate) fn is_column(self) -> bool {
+ self == FlexDirection::Column || self == FlexDirection::ColumnReverse
+ }
+
+ pub(crate) fn is_reverse(self) -> bool {
+ self == FlexDirection::RowReverse || self == FlexDirection::ColumnReverse
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum JustifyContent {
+ FlexStart,
+ FlexEnd,
+ Center,
+ SpaceBetween,
+ SpaceAround,
+ SpaceEvenly,
+}
+
+impl Default for JustifyContent {
+ fn default() -> JustifyContent {
+ JustifyContent::FlexStart
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Overflow {
+ Visible,
+ Hidden,
+ Scroll,
+}
+
+impl Default for Overflow {
+ fn default() -> Overflow {
+ Overflow::Visible
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum PositionType {
+ Relative,
+ Absolute,
+}
+
+impl Default for PositionType {
+ fn default() -> PositionType {
+ PositionType::Relative
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum FlexWrap {
+ NoWrap,
+ Wrap,
+ WrapReverse,
+}
+
+impl Default for FlexWrap {
+ fn default() -> FlexWrap {
+ FlexWrap::NoWrap
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Dimension {
+ Undefined,
+ Auto,
+ Points(f32),
+ Percent(f32),
+}
+
+impl Default for Dimension {
+ fn default() -> Dimension {
+ Dimension::Undefined
+ }
+}
+
+impl Dimension {
+ pub(crate) fn resolve(self, parent_width: Number) -> Number {
+ match self {
+ Dimension::Points(points) => Number::Defined(points),
+ Dimension::Percent(percent) => parent_width * percent,
+ _ => Number::Undefined,
+ }
+ }
+
+ pub(crate) fn is_defined(self) -> bool {
+ match self {
+ Dimension::Points(_) => true,
+ Dimension::Percent(_) => true,
+ _ => false,
+ }
+ }
+}
+
+impl Default for Rect {
+ fn default() -> Rect {
+ Rect { start: Default::default(), end: Default::default(), top: Default::default(), bottom: Default::default() }
+ }
+}
+
+impl Default for Size {
+ fn default() -> Size {
+ Size { width: Dimension::Auto, height: Dimension::Auto }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Style {
+ pub display: Display,
+ pub position_type: PositionType,
+ pub direction: Direction,
+ pub flex_direction: FlexDirection,
+ pub flex_wrap: FlexWrap,
+ pub overflow: Overflow,
+ pub align_items: AlignItems,
+ pub align_self: AlignSelf,
+ pub align_content: AlignContent,
+ pub justify_content: JustifyContent,
+ pub position: Rect,
+ pub margin: Rect,
+ pub padding: Rect,
+ pub border: Rect,
+ pub flex_grow: f32,
+ pub flex_shrink: f32,
+ pub flex_basis: Dimension,
+ pub size: Size,
+ pub min_size: Size,
+ pub max_size: Size,
+ pub aspect_ratio: Number,
+}
+
+impl Default for Style {
+ fn default() -> Style {
+ Style {
+ display: Default::default(),
+ position_type: Default::default(),
+ direction: Default::default(),
+ flex_direction: Default::default(),
+ flex_wrap: Default::default(),
+ overflow: Default::default(),
+ align_items: Default::default(),
+ align_self: Default::default(),
+ align_content: Default::default(),
+ justify_content: Default::default(),
+ position: Default::default(),
+ margin: Default::default(),
+ padding: Default::default(),
+ border: Default::default(),
+ flex_grow: 0.0,
+ flex_shrink: 1.0,
+ flex_basis: Dimension::Auto,
+ size: Default::default(),
+ min_size: Default::default(),
+ max_size: Default::default(),
+ aspect_ratio: Default::default(),
+ }
+ }
+}
+
+impl Style {
+ pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.min_size.width,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.min_size.height,
+ }
+ }
+
+ pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.max_size.width,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.max_size.height,
+ }
+ }
+
+ pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
+ }
+ }
+
+ pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
+ }
+ }
+
+ pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.size.height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.size.width,
+ }
+ }
+
+ pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.min_size.height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.min_size.width,
+ }
+ }
+
+ pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.max_size.height,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.max_size.width,
+ }
+ }
+
+ pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
+ }
+ }
+
+ pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension {
+ match direction {
+ FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
+ FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
+ }
+ }
+
+ pub(crate) fn align_self(&self, parent: &Style) -> AlignSelf {
+ if self.align_self == AlignSelf::Auto {
+ match parent.align_items {
+ AlignItems::FlexStart => AlignSelf::FlexStart,
+ AlignItems::FlexEnd => AlignSelf::FlexEnd,
+ AlignItems::Center => AlignSelf::Center,
+ AlignItems::Baseline => AlignSelf::Baseline,
+ AlignItems::Stretch => AlignSelf::Stretch,
+ }
+ } else {
+ self.align_self
+ }
+ }
+}
diff --git a/styles/src/styles.rs b/styles/src/styles.rs
index cf7bb64..d961744 100644
--- a/styles/src/styles.rs
+++ b/styles/src/styles.rs
@@ -7,224 +7,17 @@ use proc_macro2::{TokenStream, Ident, Span};
#[cfg(feature="tokenize")]
use quote::{quote, ToTokens};
-pub use crate::geometry::{Rect, Size};
-pub use crate::number::Number;
pub use crate::color::Color;
+
+pub use crate::stretch::geometry::{Point, Rect, Size};
+pub use crate::stretch::number::Number;
pub use crate::stretch::result::Layout;
-/// Describes how items should be aligned.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum AlignItems {
- FlexStart,
- FlexEnd,
- Center,
- Baseline,
- Stretch,
-}
-
-impl Default for AlignItems {
- fn default() -> AlignItems {
- AlignItems::Stretch
- }
-}
-
-/// Describes how this item should be aligned.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum AlignSelf {
- Auto,
- FlexStart,
- FlexEnd,
- Center,
- Baseline,
- Stretch,
-}
-
-impl Default for AlignSelf {
- fn default() -> AlignSelf {
- AlignSelf::Auto
- }
-}
-
-/// Describes how content should be aligned.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum AlignContent {
- FlexStart,
- FlexEnd,
- Center,
- Stretch,
- SpaceBetween,
- SpaceAround,
-}
-
-impl Default for AlignContent {
- fn default() -> AlignContent {
- AlignContent::Stretch
- }
-}
-
-/// Describes how things should flow - particularly important for start/end positions.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum Direction {
- Inherit,
- LTR,
- RTL,
-}
-
-impl Default for Direction {
- fn default() -> Direction {
- Direction::Inherit
- }
-}
-
-/// Describes whether an item is visible or not.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum Display {
- Flex,
- None,
-}
-
-impl Default for Display {
- fn default() -> Display {
- Display::Flex
- }
-}
-
-/// Describes how items should be aligned.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum FlexDirection {
- Row,
- Column,
- RowReverse,
- ColumnReverse,
-}
-
-impl Default for FlexDirection {
- fn default() -> FlexDirection {
- FlexDirection::Row
- }
-}
-
-impl FlexDirection {
- /// Checks if this is a row.
- pub(crate) fn is_row(self) -> bool {
- self == FlexDirection::Row || self == FlexDirection::RowReverse
- }
-
- /// Checks if this is a column.
- pub(crate) fn is_column(self) -> bool {
- self == FlexDirection::Column || self == FlexDirection::ColumnReverse
- }
-
- /// Checks if this is a reversed direction.
- pub(crate) fn is_reverse(self) -> bool {
- self == FlexDirection::RowReverse || self == FlexDirection::ColumnReverse
- }
-}
-
-/// Describes how content should be justified.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum JustifyContent {
- FlexStart,
- FlexEnd,
- Center,
- SpaceBetween,
- SpaceAround,
- SpaceEvenly,
-}
-
-impl Default for JustifyContent {
- fn default() -> JustifyContent {
- JustifyContent::FlexStart
- }
-}
-
-/// Describes how content should overflow.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum Overflow {
- Visible,
- Hidden,
- Scroll,
-}
-
-impl Default for Overflow {
- fn default() -> Overflow {
- Overflow::Visible
- }
-}
-
-/// Describes how content should be positioned.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum PositionType {
- Relative,
- Absolute,
-}
-
-impl Default for PositionType {
- fn default() -> PositionType {
- PositionType::Relative
- }
-}
-
-/// Describes how content should wrap.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum FlexWrap {
- NoWrap,
- Wrap,
- WrapReverse,
-}
-
-impl Default for FlexWrap {
- fn default() -> FlexWrap {
- FlexWrap::NoWrap
- }
-}
-
-/// Describes a Dimension; automatic, undefined, or a value.
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum Dimension {
- Undefined,
- Auto,
- Points(f32),
- Percent(f32),
-}
-
-impl Default for Dimension {
- fn default() -> Dimension {
- Dimension::Undefined
- }
-}
-
-impl Dimension {
- /// Internal method for Stretch.
- pub(crate) fn resolve(self, parent_width: Number) -> Number {
- match self {
- Dimension::Points(points) => Number::Defined(points),
- Dimension::Percent(percent) => parent_width * percent,
- _ => Number::Undefined,
- }
- }
-
- /// Whether this Dimension is defined by a value or not.
- pub(crate) fn is_defined(self) -> bool {
- match self {
- Dimension::Points(_) => true,
- Dimension::Percent(_) => true,
- _ => false,
- }
- }
-}
-
-impl Default for Rect {
- fn default() -> Rect {
- Rect { start: Default::default(), end: Default::default(), top: Default::default(), bottom: Default::default() }
- }
-}
-
-impl Default for Size {
- fn default() -> Size {
- Size { width: Dimension::Auto, height: Dimension::Auto }
- }
-}
+pub use crate::stretch::style::{
+ Style,
+ AlignContent, AlignItems, AlignSelf, Dimension, Direction, Display,
+ FlexDirection, JustifyContent, Overflow, PositionType, FlexWrap
+};
/// Describes the backface-visibility for a view. This may be removed in a later release.
#[derive(Copy, Clone, PartialEq, Debug)]
@@ -308,34 +101,10 @@ impl Default for FontFamily {
}
}
-/// `Style` is passed into the Stretch Flexbox rendering system to produce a computed
-/// `Layout`. This is also passed to native nodes, to transform into per-platform style
-/// commands.
-#[derive(Copy, Clone, Debug)]
-pub struct Style {
- pub display: Display,
- pub position_type: PositionType,
- pub direction: Direction,
- pub flex_direction: FlexDirection,
- pub flex_wrap: FlexWrap,
- pub overflow: Overflow,
- pub align_items: AlignItems,
- pub align_self: AlignSelf,
- pub align_content: AlignContent,
- pub justify_content: JustifyContent,
- pub position: Rect,
- pub margin: Rect,
- pub padding: Rect,
- pub border: Rect,
- pub flex_grow: f32,
- pub flex_shrink: f32,
- pub flex_basis: Dimension,
- pub size: Size,
- pub min_size: Size,
- pub max_size: Size,
- pub aspect_ratio: Number,
-
- // Appearance-based styles
+/// When applying layout to a backing view, you'll get two calls - one with a `Layout`,
+/// which contains the computed frame, and one with an `Appearance`, which contains things
+/// like colors, fonts, and so on.
+pub struct Appearance {
pub background_color: Color,
pub font_size: f32,
pub font_style: FontStyle,
@@ -348,35 +117,12 @@ pub struct Style {
pub tint_color: Color
}
-impl Default for Style {
- fn default() -> Style {
- Style {
- display: Default::default(),
- position_type: Default::default(),
- direction: Default::default(),
- flex_direction: Default::default(),
- flex_wrap: Default::default(),
- overflow: Default::default(),
- align_items: Default::default(),
- align_self: Default::default(),
- align_content: Default::default(),
- justify_content: Default::default(),
- position: Default::default(),
- margin: Default::default(),
- padding: Default::default(),
- border: Default::default(),
- flex_grow: 0.0,
- flex_shrink: 1.0,
- flex_basis: Dimension::Auto,
- size: Default::default(),
- min_size: Default::default(),
- max_size: Default::default(),
- aspect_ratio: Default::default(),
+impl Default for Appearance {
+ fn default() -> Appearance {
+ Appearance {
background_color: Color::transparent(),
-
// @TODO: We can definitely judge a default value better here.
font_size: 14.,
-
font_style: FontStyle::default(),
font_weight: FontWeight::default(),
opacity: 1.,
@@ -389,95 +135,6 @@ impl Default for Style {
}
}
-impl Style {
- /// Determines the minimum main size, given flex direction.
- pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.min_size.width,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.min_size.height,
- }
- }
-
- /// Determines the maximum main size, given flex direction.
- pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.max_size.width,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.max_size.height,
- }
- }
-
- /// Determines the main margin start, given flex direction.
- pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.start,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.top,
- }
- }
-
- /// Determines the main margin end, given flex direction.
- pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.end,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.bottom,
- }
- }
-
- /// Determines the cross size, given flex direction.
- pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.size.height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.size.width,
- }
- }
-
- /// Determines the minimum cross size, given flex direction.
- pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.min_size.height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.min_size.width,
- }
- }
-
- /// Determines the maximum cross size, given flex direction.
- pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.max_size.height,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.max_size.width,
- }
- }
-
- /// Determines the cross margin start, given flex direction.
- pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.top,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.start,
- }
- }
-
- /// Determines the cross margin end, given flex direction.
- pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension {
- match direction {
- FlexDirection::Row | FlexDirection::RowReverse => self.margin.bottom,
- FlexDirection::Column | FlexDirection::ColumnReverse => self.margin.end,
- }
- }
-
- /// Determines the inherited align_self style, given a parent `&Style`.
- pub(crate) fn align_self(&self, parent: &Style) -> AlignSelf {
- if self.align_self == AlignSelf::Auto {
- match parent.align_items {
- AlignItems::FlexStart => AlignSelf::FlexStart,
- AlignItems::FlexEnd => AlignSelf::FlexEnd,
- AlignItems::Center => AlignSelf::Center,
- AlignItems::Baseline => AlignSelf::Baseline,
- AlignItems::Stretch => AlignSelf::Stretch,
- }
- } else {
- self.align_self
- }
- }
-}
-
/// These exist purely for use in the parser code.
///
/// A `Style` is what's used for a node; `Styles` are what's parsed and stored.
diff --git a/styles/src/stylesheet.rs b/styles/src/stylesheet.rs
index 6a563e7..2bf54ab 100644
--- a/styles/src/stylesheet.rs
+++ b/styles/src/stylesheet.rs
@@ -7,7 +7,8 @@
use std::collections::HashMap;
-use crate::styles::{Dimension, Rect, Size, Style, Styles};
+use crate::stretch::style::Style;
+use crate::styles::{Appearance, Dimension, Rect, Size, Styles};
/// A `StyleSheet` contains selectors and parsed `Styles` attributes.
/// It also has some logic to apply styles for n keys to a given `Style` node.
@@ -20,9 +21,9 @@ impl StyleSheet {
StyleSheet(styles)
}
- pub fn apply_styles(&self, key: &str, style: &mut Style) {
+ pub fn apply_styles(&self, key: &str, style: &mut Style, appearance: &mut Appearance) {
match self.0.get(key) {
- Some(styles) => { reduce_styles_into_style(styles, style); },
+ Some(styles) => { reduce_styles_into_style(styles, style, appearance); },
None => {}
}
}
@@ -30,14 +31,14 @@ impl StyleSheet {
/// This takes a list of styles, and a mutable style object, and attempts to configure the
/// style object in a way that makes sense given n styles.
-fn reduce_styles_into_style(styles: &Vec, layout: &mut Style) {
+fn reduce_styles_into_style(styles: &Vec, layout: &mut Style, appearance: &mut Appearance) {
for style in styles { match style {
Styles::AlignContent(val) => { layout.align_content = *val; },
Styles::AlignItems(val) => { layout.align_items = *val; },
Styles::AlignSelf(val) => { layout.align_self = *val; },
Styles::AspectRatio(val) => { layout.aspect_ratio = *val; },
Styles::BackfaceVisibility(_val) => { },
- Styles::BackgroundColor(val) => { layout.background_color = *val; },
+ Styles::BackgroundColor(val) => { appearance.background_color = *val; },
Styles::BorderColor(_val) => { },
Styles::BorderEndColor(_val) => { },
@@ -102,9 +103,9 @@ fn reduce_styles_into_style(styles: &Vec, layout: &mut Style) {
Styles::FontFamily(_val) => { },
Styles::FontLineHeight(_val) => { },
- Styles::FontSize(val) => { layout.font_size = *val; },
- Styles::FontStyle(val) => { layout.font_style = *val; },
- Styles::FontWeight(val) => { layout.font_weight = *val; },
+ Styles::FontSize(val) => { appearance.font_size = *val; },
+ Styles::FontStyle(val) => { appearance.font_style = *val; },
+ Styles::FontWeight(val) => { appearance.font_weight = *val; },
Styles::Height(val) => {
layout.size = Size {
@@ -206,7 +207,7 @@ fn reduce_styles_into_style(styles: &Vec, layout: &mut Style) {
};
},
- Styles::Opacity(val) => { layout.opacity = *val; },
+ Styles::Opacity(val) => { appearance.opacity = *val; },
Styles::Overflow(val) => { layout.overflow = *val; },
Styles::PaddingBottom(val) => {
@@ -283,11 +284,11 @@ fn reduce_styles_into_style(styles: &Vec, layout: &mut Style) {
};
},
- Styles::TextAlignment(val) => { layout.text_alignment = *val; },
- Styles::TextColor(val) => { layout.text_color = *val; },
- Styles::TextDecorationColor(val) => { layout.text_decoration_color = *val; },
- Styles::TextShadowColor(val) => { layout.text_shadow_color = *val; },
- Styles::TintColor(val) => { layout.tint_color = *val; },
+ Styles::TextAlignment(val) => { appearance.text_alignment = *val; },
+ Styles::TextColor(val) => { appearance.text_color = *val; },
+ Styles::TextDecorationColor(val) => { appearance.text_decoration_color = *val; },
+ Styles::TextShadowColor(val) => { appearance.text_shadow_color = *val; },
+ Styles::TintColor(val) => { appearance.tint_color = *val; },
Styles::Top(val) => {
layout.position = Rect {