Skip to content

Commit

Permalink
Create web counter example
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Dec 13, 2023
1 parent 9b3d88c commit 0b6f677
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 8 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ repository = "https://github.com/matthunz/viewbuilder"
[workspace]
members = [
".",
"macros"
"macros",
"web-examples"
]

[features]
native = []
web = []
full = ["native"]
web = ["dep:web-sys"]
full = ["native", "web"]
web-sys = ["dep:web-sys"]

[dependencies]
kurbo = "0.9.5"
Expand All @@ -24,11 +26,12 @@ slotmap = "1.0.7"
winit = { version = "0.28.1" }
concoct = { git = "https://github.com/concoct-rs/concoct" }
viewbuilder-macros = { path = "macros" }
web-sys = { version = "0.3.66", optional = true, features = ["Document", "HtmlElement", "Text", "Window"] }

[package.metadata.docs.rs]
features = ["full"]
rustdoc-args = ["--cfg", "docsrs"]

[[example]]
name = "app"
required-features = ["native"]
required-features = ["native"]
2 changes: 1 addition & 1 deletion examples/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use concoct::{Context, Handle, Object, Slot};
use viewbuilder::native::{
view::{LinearLayout, Text},
window, UserInterface, Window,
window, Window,
};
use winit::dpi::PhysicalSize;

Expand Down
6 changes: 3 additions & 3 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ pub fn main(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let stmts = input.block.stmts;
let expanded = quote! {
fn main() {
let ui = UserInterface::default();
let _guard = ui.enter();
let viewbuilder_macros_ui = viewbuilder::native::UserInterface::default();
let _guard = viewbuilder_macros_ui.enter();

#(#stmts)*

ui.run()
viewbuilder_macros_ui.run()
}
};
expanded.into()
Expand Down
151 changes: 151 additions & 0 deletions src/web/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,152 @@
use concoct::{Handle, Object, Slot};
use web_sys::wasm_bindgen::JsCast;

pub trait View {
fn node(&self) -> &web_sys::Node;

fn set_parent(&mut self, parent: web_sys::Element);
}

pub trait IntoView {
type View: View;

fn into_view(self) -> Handle<Self::View>;
}

impl<V> IntoView for V
where
V: View + Object + 'static,
{
type View = V;

fn into_view(self) -> Handle<Self::View> {
self.spawn()
}
}

impl<V: View> IntoView for Handle<V> {
type View = V;

fn into_view(self) -> Handle<Self::View> {
self
}
}

pub trait ViewGroup {
type Handles;

fn view_group(self) -> Self::Handles;
}

impl<V: IntoView> ViewGroup for V {
type Handles = Handle<V::View>;

fn view_group(self) -> Self::Handles {
self.into_view()
}
}

impl<A: IntoView, B: IntoView, C: IntoView> ViewGroup for (A, B, C) {
type Handles = (Handle<A::View>, Handle<B::View>, Handle<C::View>);

fn view_group(self) -> Self::Handles {
(self.0.into_view(), self.1.into_view(), self.2.into_view())
}
}

pub struct MouseEvent;

pub struct ElementBuilder {}

impl ElementBuilder {
pub fn on_click(&mut self, _f: Handle<impl Slot<MouseEvent>>) -> &mut Self {
self
}

pub fn child(&mut self, _view: impl ViewGroup) -> &mut Self {
self
}

pub fn build(&mut self) -> Element {
todo!()
}
}

pub struct Element {
pub element: web_sys::Element,
parent: Option<web_sys::Element>,
}

impl Element {
pub fn new(tag: &str) -> Self {
Self {
element: web_sys::window()
.unwrap()
.document()
.unwrap()
.create_element(tag)
.unwrap(),
parent: None,
}
}

pub fn builder() -> ElementBuilder {
ElementBuilder {}
}
}

impl Object for Element {}

impl View for Element {
fn node(&self) -> &web_sys::Node {
self.element.unchecked_ref()
}

fn set_parent(&mut self, parent: web_sys::Element) {
self.parent = Some(parent.clone());
parent.append_child(&self.element).unwrap();
}
}

pub struct AppendChild<V>(pub Handle<V>);

impl<V: View + 'static> Slot<AppendChild<V>> for Element {
fn handle(&mut self, _handle: concoct::Context<Self>, msg: AppendChild<V>) {
self.element.append_child(msg.0.borrow().node()).unwrap();
}
}

pub struct Text {
pub node: web_sys::Text,
parent: Option<web_sys::Element>,
}

impl Text {
pub fn new(content: &str) -> Self {
Self {
node: web_sys::window()
.unwrap()
.document()
.unwrap()
.create_text_node(content),
parent: None,
}
}
}

impl View for Text {
fn node(&self) -> &web_sys::Node {
&self.node
}

fn set_parent(&mut self, parent: web_sys::Element) {
self.parent = Some(parent.clone());
parent.append_child(&self.node).unwrap();
}
}

impl Object for Text {}

impl Slot<String> for Text {
fn handle(&mut self, _handle: concoct::Context<Self>, _msg: String) {}
}
10 changes: 10 additions & 0 deletions web-examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "web-examples"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
viewbuilder = { path = "../", features = ["full"] }
concoct = { git = "https://github.com/concoct-rs/concoct" }
75 changes: 75 additions & 0 deletions web-examples/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use concoct::{Handle, Object, Slot};
use viewbuilder::web::{self, Element, Text};

#[derive(Clone, Copy)]
enum Message {
Increment,
Decrement,
}

struct Counter {
value: i32,
text: Handle<Text>,
}

impl Object for Counter {}

impl Slot<Message> for Counter {
fn handle(&mut self, _cx: concoct::Context<Self>, msg: Message) {
match msg {
Message::Increment => self.value += 1,
Message::Decrement => self.value -= 1,
};
self.text.send(self.value.to_string());
}
}

struct CounterButton {
msg: Message,
counter: Handle<Counter>,
}

impl Object for CounterButton {}

impl Slot<web::MouseEvent> for CounterButton {
fn handle(&mut self, _cx: concoct::Context<Self>, _msg: web::MouseEvent) {
self.counter.send(self.msg);
}
}

#[viewbuilder::main]
fn main() {
let text = Text::new("0").spawn();

let counter = Counter {
value: 0,
text: text.clone(),
}
.spawn();

let increment_button = CounterButton {
msg: Message::Increment,
counter: counter.clone(),
}
.spawn();
let decrement_button = CounterButton {
msg: Message::Decrement,
counter,
}
.spawn();

Element::builder()
.child((
text,
Element::builder()
.on_click(increment_button)
.child(Text::new("Up High!"))
.build(),
Element::builder()
.on_click(decrement_button)
.child(Text::new("Down Low!"))
.build(),
))
.build()
.spawn();
}

0 comments on commit 0b6f677

Please sign in to comment.