Skip to content

Commit

Permalink
add class and style interfaces (#193)
Browse files Browse the repository at this point in the history
* add class interface

* add style interface

* remove parse style/class logic, and panic instead (in dev)

* combine attributes, styles, classes into single struct

* make `class` and `style` methods polymorphic

* fix clippy

* Update Cargo.toml

Co-authored-by: Philipp Mildenberger <[email protected]>

* Update crates/xilem_web/src/context.rs

Co-authored-by: Philipp Mildenberger <[email protected]>

* Update crates/xilem_web/src/context.rs

Co-authored-by: Philipp Mildenberger <[email protected]>

* Update crates/xilem_web/src/class.rs

Co-authored-by: Philipp Mildenberger <[email protected]>

---------

Co-authored-by: Philipp Mildenberger <[email protected]>
  • Loading branch information
richard-uk1 and Philipp-M authored Mar 31, 2024
1 parent 072358e commit 74bc6fc
Show file tree
Hide file tree
Showing 11 changed files with 575 additions and 118 deletions.
2 changes: 2 additions & 0 deletions crates/xilem_web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ peniko = { git = "https://github.com/linebender/peniko", rev = "629fc3325b016a8c
version = "0.3.4"
features = [
"console",
"CssStyleDeclaration",
"Document",
"DomTokenList",
"Element",
"Event",
"HtmlElement",
Expand Down
124 changes: 124 additions & 0 deletions crates/xilem_web/src/class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use std::{borrow::Cow, marker::PhantomData};

use xilem_core::{Id, MessageResult};

use crate::{
interfaces::{sealed::Sealed, Element},
ChangeFlags, Cx, View, ViewMarker,
};

/// A trait to make the class adding functions generic over collection type
pub trait IntoClasses {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>);
}

impl IntoClasses for String {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self.into());
}
}

impl IntoClasses for &'static str {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self.into());
}
}

impl IntoClasses for Cow<'static, str> {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self);
}
}

impl<T> IntoClasses for Option<T>
where
T: IntoClasses,
{
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
if let Some(t) = self {
t.into_classes(classes);
}
}
}

impl<T> IntoClasses for Vec<T>
where
T: IntoClasses,
{
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
for itm in self {
itm.into_classes(classes);
}
}
}

macro_rules! impl_tuple_intoclasses {
($($name:ident : $type:ident),* $(,)?) => {
impl<$($type),*> IntoClasses for ($($type,)*)
where
$($type: IntoClasses),*
{
#[allow(unused_variables)]
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
let ($($name,)*) = self;
$(
$name.into_classes(classes);
)*
}
}
};
}

impl_tuple_intoclasses!();
impl_tuple_intoclasses!(t1: T1);
impl_tuple_intoclasses!(t1: T1, t2: T2);
impl_tuple_intoclasses!(t1: T1, t2: T2, t3: T3);
impl_tuple_intoclasses!(t1: T1, t2: T2, t3: T3, t4: T4);

/// Applies a class to the underlying element.
pub struct Class<E, T, A> {
pub(crate) element: E,
pub(crate) class_names: Vec<Cow<'static, str>>,
pub(crate) phantom: PhantomData<fn() -> (T, A)>,
}

impl<E, T, A> ViewMarker for Class<E, T, A> {}
impl<E, T, A> Sealed for Class<E, T, A> {}

impl<E: Element<T, A>, T, A> View<T, A> for Class<E, T, A> {
type State = E::State;
type Element = E::Element;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
for class_name in &self.class_names {
cx.add_class_to_element(class_name);
}
self.element.build(cx)
}

fn rebuild(
&self,
cx: &mut Cx,
prev: &Self,
id: &mut Id,
state: &mut Self::State,
element: &mut Self::Element,
) -> ChangeFlags {
for class_name in &self.class_names {
cx.add_class_to_element(class_name);
}
self.element.rebuild(cx, &prev.element, id, state, element)
}

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

crate::interfaces::impl_dom_interfaces_for_ty!(Element, Class);
Loading

0 comments on commit 74bc6fc

Please sign in to comment.