diff --git a/alchemy/src/components/fragment.rs b/alchemy/src/components/fragment.rs
index 80e3e12..d447440 100644
--- a/alchemy/src/components/fragment.rs
+++ b/alchemy/src/components/fragment.rs
@@ -5,7 +5,9 @@
//! just allow returning arbitrary iterators.
use alchemy_lifecycle::ComponentKey;
-use alchemy_lifecycle::traits::Component;
+use alchemy_lifecycle::traits::{Component, Props};
+
+pub struct FragmentProps;
/// Fragments are special - you can do something like the following in cases where you
/// want to render some views without requiring an intermediate view.
@@ -20,8 +22,18 @@ use alchemy_lifecycle::traits::Component;
#[derive(Default, Debug)]
pub struct Fragment;
-impl Component for Fragment {
- fn constructor(_key: ComponentKey) -> Fragment {
- Fragment { }
+impl Fragment {
+ fn default_props() -> FragmentProps {
+ FragmentProps {}
+ }
+}
+
+impl Props for Fragment {
+ fn set_props(&mut self, _: &mut std::any::Any) {}
+}
+
+impl Component for Fragment {
+ fn new(_: ComponentKey) -> Fragment {
+ Fragment {}
}
}
diff --git a/alchemy/src/components/text.rs b/alchemy/src/components/text.rs
index 6bb292f..105a537 100644
--- a/alchemy/src/components/text.rs
+++ b/alchemy/src/components/text.rs
@@ -9,12 +9,14 @@ 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};
+use alchemy_lifecycle::rsx::RSX;
+use alchemy_lifecycle::traits::{Component, Props, PlatformSpecificNodeType};
#[cfg(feature = "cocoa")]
use alchemy_cocoa::text::{Text as PlatformTextBridge};
+pub struct TextProps;
+
/// Text rendering is a complicated mess, and being able to defer to the
/// backing platform for this is amazing. This is a very common Component.
///
@@ -23,59 +25,60 @@ use alchemy_cocoa::text::{Text as PlatformTextBridge};
/// ```
///
/// ```
-pub struct Text {
- text: String,
- bridge: Mutex
-}
+pub struct Text(Mutex);
impl Text {
+ pub fn default_props() -> TextProps { TextProps {} }
// This is very naive for now, but it's fine - we probably
// want to do some fun stuff here later with stylized text
// rendering anyway.
- fn compare_and_update_text(&mut self, props: &Props) {
- let text = props.children.iter().map(|child| match child {
- RSX::VirtualText(s) => s.0.clone(),
- _ => String::new()
- }).collect::();
-
- if self.text != text {
- let mut bridge = self.bridge.lock().unwrap();
- bridge.set_text(&text);
- self.text = text;
- }
- }
+ //fn compare_and_update_text(&mut self, props: &Props) {
+ /*let text = props.*/
+ //}
+}
+
+impl Props for Text {
+ fn set_props(&mut self, _: &mut std::any::Any) {}
}
impl Component for Text {
- fn constructor(_: ComponentKey) -> Text {
- Text {
- text: "".into(),
- bridge: Mutex::new(PlatformTextBridge::new())
- }
+ fn new(_: ComponentKey) -> Text {
+ Text(Mutex::new(PlatformTextBridge::new()))
}
fn has_native_backing_node(&self) -> bool { true }
fn borrow_native_backing_node(&self) -> Option {
- let bridge = self.bridge.lock().unwrap();
+ let bridge = self.0.lock().unwrap();
Some(bridge.borrow_native_backing_node())
}
// Shouldn't be allowed to have child elements... or, should it?
// Panic might not be right here, but eh, should probably do something.
- fn append_child_component(&self, _component: &Component) {}
+ //fn append_child_component(&self, _component: &Component) {}
fn apply_styles(&self, appearance: &Appearance, layout: &Layout) {
- let mut bridge = self.bridge.lock().unwrap();
+ let mut bridge = self.0.lock().unwrap();
bridge.apply_styles(appearance, layout);
}
- fn component_did_mount(&mut self, props: &Props) {
- self.compare_and_update_text(props);
+ fn component_did_mount(&mut self) {
+ let mut bridge = self.0.lock().unwrap();
+ bridge.render();
}
- fn render(&self, _props: &Props) -> Result {
+ // This one is a bit tricky, due to the way we have to do props + children in Rust.
+ // Here, we set it as the new text on render(), and then ensure it gets rendered on
+ // `component_did_update()` and `component_did_mount()`.
+ fn render(&self, children: Vec) -> Result {
+ let text = children.iter().map(|child| match child {
+ RSX::VirtualText(s) => s.0.to_owned(),
+ _ => String::new()
+ }).collect::();
+
+ let mut bridge = self.0.lock().unwrap();
+ bridge.set_text(text);
+
Ok(RSX::None)
}
}
-
diff --git a/alchemy/src/components/view.rs b/alchemy/src/components/view.rs
index 9ddc064..b9213e1 100644
--- a/alchemy/src/components/view.rs
+++ b/alchemy/src/components/view.rs
@@ -9,14 +9,16 @@ 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};
+use alchemy_lifecycle::rsx::RSX;
+use alchemy_lifecycle::traits::{Component, Props, PlatformSpecificNodeType};
use crate::components::Fragment;
#[cfg(feature = "cocoa")]
use alchemy_cocoa::view::{View as PlatformViewBridge};
+pub struct ViewProps;
+
/// Views are the most basic piece of the API. If you want to display something, you'll
/// probably be reaching for a View first and foremost.
///
@@ -25,44 +27,53 @@ use alchemy_cocoa::view::{View as PlatformViewBridge};
/// ```
///
/// ```
-pub struct View(Mutex);
+pub struct View {
+ bridge: Mutex
+}
impl Default for View {
fn default() -> View {
- View(Mutex::new(PlatformViewBridge::new()))
+ View {
+ bridge: Mutex::new(PlatformViewBridge::new())
+ }
}
}
+impl View {
+ pub fn default_props() -> ViewProps {
+ ViewProps {}
+ }
+}
+
+impl Props for View {
+ fn set_props(&mut self, _: &mut std::any::Any) {}
+}
+
impl Component for View {
- fn constructor(_key: ComponentKey) -> View {
- View(Mutex::new(PlatformViewBridge::new()))
+ fn new(_: ComponentKey) -> View {
+ View::default()
}
fn has_native_backing_node(&self) -> bool { true }
fn borrow_native_backing_node(&self) -> Option {
- let bridge = self.0.lock().unwrap();
+ let bridge = self.bridge.lock().unwrap();
Some(bridge.borrow_native_backing_node())
}
- fn append_child_component(&self, component: &Component) {
- if let Some(child) = component.borrow_native_backing_node() {
- let mut bridge = self.0.lock().unwrap();
- bridge.append_child(child);
- }
+ fn append_child_node(&self, node: PlatformSpecificNodeType) {
+ let mut bridge = self.bridge.lock().unwrap();
+ bridge.append_child(node);
}
fn apply_styles(&self, appearance: &Appearance, layout: &Layout) {
- let mut bridge = self.0.lock().unwrap();
+ let mut bridge = self.bridge.lock().unwrap();
bridge.apply_styles(appearance, layout);
}
- fn render(&self, props: &Props) -> Result {
- 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()
- }))
+ fn render(&self, children: Vec) -> Result {
+ Ok(RSX::node("Fragment", "".into(), |key| {
+ Box::new(::new(key))
+ }, Box::new(ViewProps {}), children))
}
}
diff --git a/alchemy/src/lib.rs b/alchemy/src/lib.rs
index 1591c17..ce9b625 100644
--- a/alchemy/src/lib.rs
+++ b/alchemy/src/lib.rs
@@ -11,12 +11,12 @@ use proc_macro_hack::proc_macro_hack;
pub use alchemy_lifecycle::ComponentKey;
pub use alchemy_lifecycle::traits::{
- AppDelegate, Component, WindowDelegate
+ AppDelegate, Component, Props, WindowDelegate
};
pub use alchemy_lifecycle::error::Error;
pub use alchemy_lifecycle::rsx::{
- Props, RSX, VirtualNode, VirtualText
+ RSX, VirtualNode, VirtualText
};
#[proc_macro_hack(support_nested)]
diff --git a/alchemy/src/window/window.rs b/alchemy/src/window/window.rs
index 1bc95a7..9c4a84b 100644
--- a/alchemy/src/window/window.rs
+++ b/alchemy/src/window/window.rs
@@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex};
use alchemy_lifecycle::{ComponentKey, RENDER_ENGINE};
use alchemy_lifecycle::rsx::RSX;
-use alchemy_lifecycle::traits::WindowDelegate;
+use alchemy_lifecycle::traits::{Component, WindowDelegate};
use alchemy_styles::{Appearance, Style, StylesList, THEME_ENGINE};
@@ -92,7 +92,11 @@ impl Window {
let window_id = SHARED_APP.windows.allocate_new_window_id();
let view = View::default();
let shared_app_ptr: *const App = &**SHARED_APP;
- let bridge = PlatformWindowBridge::new(window_id, &view, shared_app_ptr);
+
+ // This unwrap() is fine, since we implement View ourselves in Alchemy
+ let backing_node = view.borrow_native_backing_node().unwrap();
+ let bridge = PlatformWindowBridge::new(window_id, backing_node, shared_app_ptr);
+
let key = match RENDER_ENGINE.register_root_component(view) {
Ok(key) => key,
Err(_e) => { panic!("Uhhhh this really messed up"); }
diff --git a/cocoa/src/text.rs b/cocoa/src/text.rs
index 30261f4..2845955 100644
--- a/cocoa/src/text.rs
+++ b/cocoa/src/text.rs
@@ -24,6 +24,7 @@ static ALCHEMY_DELEGATE: &str = "alchemyDelegate";
/// colors and so forth.
#[derive(Debug)]
pub struct Text {
+ text: String,
inner_mut: Id