Skip to content

Commit

Permalink
xilem_web: Add Rotate and Scale (CSS) transform views (#621)
Browse files Browse the repository at this point in the history
This sketches/implements (parts of) the transform modifier API described
in [Add `Affine` transform to `Widget`
trait?](https://xi.zulipchat.com/#narrow/stream/317477-masonry/topic/Add.20.60Affine.60.20transform.20to.20.60Widget.60.20trait.3F/near/472076600)
for xilem_web.

This currently includes `rotate` and `scale`, because there are less
cases to handle for these [CSS transform
functions](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function).
In `rotate` I think we can reduce this to just radians (there are more
units for `<angle>`: `turn`, `grad`, `deg` and `rad` (used here) but
they can all be derived from radians), and percent in scale is kinda
redundant (as `200%` == `2.0`)), for example `translate` has more cases
as it includes all kinds of units (like `px`, `em`, `%` etc.), so this
may require more thought (and is thus probably better for a future PR).

This can be combined with untyped `transform: ...` as style, these
modifier views extend the `transform` style (while the untyped
`style(..)` overwrites previous set values).

The `svgtoy` example is updated to include these views.
  • Loading branch information
Philipp-M authored Oct 10, 2024
1 parent da92ba6 commit a4f88b7
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 23 deletions.
77 changes: 64 additions & 13 deletions xilem_web/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
attribute::{Attr, WithAttributes},
class::{AsClassIter, Class, WithClasses},
events,
style::{IntoStyles, Style, WithStyle},
style::{IntoStyles, Rotate, Scale, ScaleValue, Style, WithStyle},
DomNode, DomView, IntoAttributeValue, OptionalAction, Pointer, PointerMsg,
};
use wasm_bindgen::JsCast;
Expand Down Expand Up @@ -146,6 +146,59 @@ pub trait Element<State, Action = ()>:
Attr::new(self, Cow::from("id"), value.into_attr_value())
}

/// Set a style attribute
fn style(self, style: impl IntoStyles) -> Style<Self, State, Action>
where
<Self::DomNode as DomNode>::Props: WithStyle,
{
let mut styles = vec![];
style.into_styles(&mut styles);
Style::new(self, styles)
}

/// Add a `rotate(<radians>rad)` [transform-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function) to the current CSS `transform`
/// # Examples
///
/// ```
/// use xilem_web::{style as s, interfaces::Element, svg::kurbo::Rect};
///
/// # fn component() -> impl Element<()> {
/// Rect::from_origin_size((0.0, 10.0), (20.0, 30.0))
/// .style(s("transform", "translate(10px, 0)")) // can be combined with untyped `transform`
/// .rotate(std::f64::consts::PI / 4.0)
/// // results in the following html:
/// // <rect width="20" height="30" x="0.0" y="10.0" style="transform: translate(10px, 0) rotate(0.78539rad);"></rect>
/// # }
/// ```
fn rotate(self, radians: f64) -> Rotate<Self, State, Action>
where
<Self::DomNode as DomNode>::Props: WithStyle,
{
Rotate::new(self, radians)
}

/// Add a `scale(<scale>)` [transform-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function) to the current CSS `transform`
/// # Examples
///
/// ```
/// use xilem_web::{style as s, interfaces::Element, svg::kurbo::Circle};
///
/// # fn component() -> impl Element<()> {
/// Circle::new((10.0, 20.0), 30.0)
/// .style(s("transform", "translate(10px, 0)")) // can be combined with untyped `transform`
/// .scale(1.5)
/// .scale((1.5, 2.0))
/// // results in the following html:
/// // <circle r="30" cy="20" cx="10" style="transform: translate(10px, 0) scale(1.5) scale(1.5, 2);"></circle>
/// # }
/// ```
fn scale(self, scale: impl Into<ScaleValue>) -> Scale<Self, State, Action>
where
<Self::DomNode as DomNode>::Props: WithStyle,
{
Scale::new(self, scale)
}

// event list from
// https://html.spec.whatwg.org/multipage/webappapis.html#idl-definitions
//
Expand Down Expand Up @@ -498,16 +551,19 @@ where
{
}

// pub trait StyleExt {
// }

// /// Keep this shared code in sync between `HtmlElement` and `SvgElement`
// macro_rules! style_impls {
// () => {};
// }

// #[cfg(feature = "HtmlElement")]
pub trait HtmlElement<State, Action = ()>:
Element<State, Action, DomNode: DomNode<Props: WithStyle> + AsRef<web_sys::HtmlElement>>
{
/// Set a style attribute
fn style(self, style: impl IntoStyles) -> Style<Self, State, Action> {
let mut styles = vec![];
style.into_styles(&mut styles);
Style::new(self, styles)
}
// style_impls!();
}

// #[cfg(feature = "HtmlElement")]
Expand Down Expand Up @@ -1479,12 +1535,7 @@ where
pub trait SvgElement<State, Action = ()>:
Element<State, Action, DomNode: DomNode<Props: WithStyle> + AsRef<web_sys::SvgElement>>
{
/// Set a style attribute
fn style(self, style: impl IntoStyles) -> Style<Self, State, Action> {
let mut styles = vec![];
style.into_styles(&mut styles);
Style::new(self, styles)
}
// style_impls!();
}

// #[cfg(feature = "SvgElement")]
Expand Down
36 changes: 36 additions & 0 deletions xilem_web/src/one_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,14 @@ impl WithStyle for Noop {
fn mark_end_of_style_modifier(&mut self) {
unreachable!()
}

fn get_style(&self, _name: &str) -> Option<&CowStr> {
unreachable!()
}

fn was_updated(&self, _name: &str) -> bool {
unreachable!()
}
}

impl<T> AsRef<T> for Noop {
Expand Down Expand Up @@ -425,6 +433,34 @@ impl<
OneOf::I(e) => e.mark_end_of_style_modifier(),
}
}

fn get_style(&self, name: &str) -> Option<&CowStr> {
match self {
OneOf::A(e) => e.get_style(name),
OneOf::B(e) => e.get_style(name),
OneOf::C(e) => e.get_style(name),
OneOf::D(e) => e.get_style(name),
OneOf::E(e) => e.get_style(name),
OneOf::F(e) => e.get_style(name),
OneOf::G(e) => e.get_style(name),
OneOf::H(e) => e.get_style(name),
OneOf::I(e) => e.get_style(name),
}
}

fn was_updated(&self, name: &str) -> bool {
match self {
OneOf::A(e) => e.was_updated(name),
OneOf::B(e) => e.was_updated(name),
OneOf::C(e) => e.was_updated(name),
OneOf::D(e) => e.was_updated(name),
OneOf::E(e) => e.was_updated(name),
OneOf::F(e) => e.was_updated(name),
OneOf::G(e) => e.was_updated(name),
OneOf::H(e) => e.was_updated(name),
OneOf::I(e) => e.was_updated(name),
}
}
}

impl<N1, N2, N3, N4, N5, N6, N7, N8, N9> DomNode for OneOf<N1, N2, N3, N4, N5, N6, N7, N8, N9>
Expand Down
Loading

0 comments on commit a4f88b7

Please sign in to comment.