Skip to content

Commit

Permalink
Merged xilem_svg into xilem_html
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp-M committed Nov 7, 2023
1 parent c84319d commit 35f0910
Show file tree
Hide file tree
Showing 13 changed files with 738 additions and 2 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"crates/xilem_html/web_examples/counter",
"crates/xilem_html/web_examples/counter_custom_element",
"crates/xilem_html/web_examples/todomvc",
"crates/xilem_html/web_examples/svgtoy",
"crates/xilem_svg",
"crates/xilem_svg/web_examples/svgtoy",
]
Expand Down
8 changes: 8 additions & 0 deletions crates/xilem_html/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ wasm-bindgen = "0.2.87"
paste = "1"
log = "0.4.19"
gloo = { version = "0.8.1", default-features = false, features = ["events", "utils"] }
peniko = { git = "https://github.com/linebender/peniko", rev = "629fc3325b016a8c98b1cd6204cb4ddf1c6b3daa" }

[dependencies.web-sys]
version = "0.3.4"
Expand All @@ -37,6 +38,13 @@ features = [
"Node",
"NodeList",
"SvgElement",
"SvgGraphicsElement",
"SvggElement",
"SvgGeometryElement",
"SvgCircleElement",
"SvgLineElement",
"SvgRectElement",
"SvgPathElement",
"Text",
"Window",
"FocusEvent",
Expand Down
5 changes: 5 additions & 0 deletions crates/xilem_html/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,9 @@ define_elements!(
(MATHML_NS, Semantics, semantics, Element),
// SVG (TODO all SVG elements and their interfaces)
(SVG_NS, Svg, svg, SvgElement),
(SVG_NS, Group, g, SvggElement), // TODO `group` or `g`? (for consistency `g` seems to be the better option, xilem_svg uses `group`)
(SVG_NS, Rect, rect, SvgRectElement),
(SVG_NS, Path, path, SvgPathElement),
(SVG_NS, Circle, circle, SvgCircleElement),
(SVG_NS, Line, line, SvgLineElement),
);
32 changes: 30 additions & 2 deletions crates/xilem_html/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,35 @@ dom_interface_macro_and_trait_definitions!(
}
},
SvgElement {
methods: {},
child_interfaces: {}
methods: {
// TODO consider stateful event views like this in general
fn pointer<F: Fn(&mut T, crate::svg::pointer::PointerMsg)>(self, f: F) -> crate::svg::pointer::Pointer<T, A, Self, F> {
crate::svg::pointer::pointer(self, f)
}
},
child_interfaces: {
SvgGraphicsElement {
methods: { },
child_interfaces: {
SvggElement { methods: {}, child_interfaces: {} },
SvgGeometryElement {
methods: {
fn fill(self, brush: impl Into<peniko::Brush>) -> crate::svg::common_attrs::Fill<T, A, Self> {
crate::svg::common_attrs::fill(self, brush)
}
fn stroke(self, brush: impl Into<peniko::Brush>, style: peniko::kurbo::Stroke) -> crate::svg::common_attrs::Stroke<T, A, Self> {
crate::svg::common_attrs::stroke(self, brush, style)
}
},
child_interfaces: {
SvgRectElement { methods: {}, child_interfaces: {} },
SvgPathElement { methods: {}, child_interfaces: {} },
SvgCircleElement { methods: {}, child_interfaces: {} },
SvgLineElement { methods: {}, child_interfaces: {} },
}
},
}
},
}
},
);
1 change: 1 addition & 0 deletions crates/xilem_html/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod events;
pub mod interfaces;
mod one_of;
mod optional_action;
pub mod svg;
mod vecmap;
mod view;
mod view_ext;
Expand Down
170 changes: 170 additions & 0 deletions crates/xilem_html/src/svg/common_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2023 the Druid Authors.
// SPDX-License-Identifier: Apache-2.0

use std::borrow::Cow;
use std::{any::Any, marker::PhantomData};

use peniko::Brush;
use xilem_core::{Id, MessageResult};

use crate::IntoAttributeValue;
use crate::{
context::{ChangeFlags, Cx},
view::{View, ViewMarker},
};

pub struct Fill<T, A, V> {
child: V,
// This could reasonably be static Cow also, but keep things simple
brush: Brush,
phantom: PhantomData<fn() -> (T, A)>,
}

pub struct Stroke<T, A, V> {
child: V,
// This could reasonably be static Cow also, but keep things simple
brush: Brush,
style: peniko::kurbo::Stroke,
phantom: PhantomData<fn() -> (T, A)>,
}

pub fn fill<T, A, V>(child: V, brush: impl Into<Brush>) -> Fill<T, A, V> {
Fill {
child,
brush: brush.into(),
phantom: Default::default(),
}
}

pub fn stroke<T, A, V>(
child: V,
brush: impl Into<Brush>,
style: peniko::kurbo::Stroke,
) -> Stroke<T, A, V> {
Stroke {
child,
brush: brush.into(),
style,
phantom: Default::default(),
}
}

fn brush_to_string(brush: &Brush) -> String {
match brush {
Brush::Solid(color) => {
if color.a == 0 {
"none".into()
} else {
format!("#{:02x}{:02x}{:02x}", color.r, color.g, color.b)
}
}
_ => todo!("gradients not implemented"),
}
}

crate::interfaces::impl_dom_interfaces_for_ty!(SvgGeometryElement, Fill);

impl<T, A, V> ViewMarker for Fill<T, A, V> {}
impl<T, A, V> crate::interfaces::sealed::Sealed for Fill<T, A, V> {}

// TODO: make generic over A (probably requires Phantom)
impl<T, A, V: View<T, A>> View<T, A> for Fill<T, A, V> {
type State = (Cow<'static, str>, V::State);
type Element = V::Element;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
let brush_svg_repr = Cow::from(brush_to_string(&self.brush));
cx.add_new_attribute_to_current_element(
&"fill".into(),
&brush_svg_repr.clone().into_attribute_value(),
);
let (id, child_state, element) = self.child.build(cx);
(id, (brush_svg_repr, child_state), element)
}

fn rebuild(
&self,
cx: &mut Cx,
prev: &Self,
id: &mut Id,
(brush_svg_repr, child_state): &mut Self::State,
element: &mut V::Element,
) -> ChangeFlags {
if self.brush != prev.brush {
*brush_svg_repr = Cow::from(brush_to_string(&self.brush));
}
cx.add_new_attribute_to_current_element(
&"fill".into(),
&brush_svg_repr.clone().into_attribute_value(),
);
self.child
.rebuild(cx, &prev.child, id, child_state, element)
}

fn message(
&self,
id_path: &[Id],
(_, child_state): &mut Self::State,
message: Box<dyn Any>,
app_state: &mut T,
) -> MessageResult<A> {
self.child.message(id_path, child_state, message, app_state)
}
}

crate::interfaces::impl_dom_interfaces_for_ty!(SvgGeometryElement, Stroke);

impl<T, A, V> ViewMarker for Stroke<T, A, V> {}
impl<T, A, V> crate::interfaces::sealed::Sealed for Stroke<T, A, V> {}

impl<T, A, V: View<T, A>> View<T, A> for Stroke<T, A, V> {
type State = (Cow<'static, str>, V::State);
type Element = V::Element;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
let brush_svg_repr = Cow::from(brush_to_string(&self.brush));
cx.add_new_attribute_to_current_element(
&"stroke".into(),
&brush_svg_repr.clone().into_attribute_value(),
);
cx.add_new_attribute_to_current_element(
&"stroke-width".into(),
&self.style.width.into_attribute_value(),
);
let (id, child_state, element) = self.child.build(cx);
(id, (brush_svg_repr, child_state), element)
}

fn rebuild(
&self,
cx: &mut Cx,
prev: &Self,
id: &mut Id,
(brush_svg_repr, child_state): &mut Self::State,
element: &mut V::Element,
) -> ChangeFlags {
if self.brush != prev.brush {
*brush_svg_repr = Cow::from(brush_to_string(&self.brush));
}
cx.add_new_attribute_to_current_element(
&"stroke".into(),
&brush_svg_repr.clone().into_attribute_value(),
);
cx.add_new_attribute_to_current_element(
&"stroke-width".into(),
&self.style.width.into_attribute_value(),
);
self.child
.rebuild(cx, &prev.child, id, child_state, element)
}

fn message(
&self,
id_path: &[Id],
(_, child_state): &mut Self::State,
message: Box<dyn Any>,
app_state: &mut T,
) -> MessageResult<A> {
self.child.message(id_path, child_state, message, app_state)
}
}
Loading

0 comments on commit 35f0910

Please sign in to comment.