use std::marker::PhantomData;
use crate::core::{MessageContext, Mut, View, ViewMarker};
use crate::{Affine, Pod, ViewCtx, WidgetView};
pub fn transformed<Child, State, Action>(child: Child) -> Transformed<Child, State, Action>
where
Child: WidgetView<State, Action>,
{
Transformed {
child,
transform: Affine::IDENTITY,
phantom: PhantomData,
}
}
pub struct Transformed<V, State, Action> {
child: V,
transform: Affine,
phantom: PhantomData<(State, Action)>,
}
impl<V, State, Action> Transformed<V, State, Action> {
#[must_use]
pub fn rotate(mut self, radians: f64) -> Self {
self.transform = self.transform.then_rotate(radians);
self
}
#[must_use]
pub fn scale(mut self, uniform: f64) -> Self {
self.transform = self.transform.then_scale(uniform);
self
}
#[must_use]
pub fn scale_non_uniform(mut self, x: f64, y: f64) -> Self {
self.transform = self.transform.then_scale_non_uniform(x, y);
self
}
#[must_use]
pub fn translate(mut self, v: impl Into<crate::Vec2>) -> Self {
self.transform = self.transform.then_translate(v.into());
self
}
#[must_use]
pub fn transform(mut self, v: impl Into<Affine>) -> Self {
self.transform *= v.into();
self
}
}
mod private {
use crate::Affine;
#[expect(
unnameable_types,
reason = "This type has no public API, and is only public due to trait visibility rules"
)]
pub struct TransformedState<ChildState> {
pub(super) child: ChildState,
pub(super) previous_transform: Affine,
}
}
impl<V, State, Action> ViewMarker for Transformed<V, State, Action> {}
impl<Child, State, Action> View<State, Action, ViewCtx> for Transformed<Child, State, Action>
where
Child: WidgetView<State, Action>,
State: 'static,
Action: 'static,
{
type Element = Pod<Child::Widget>;
type ViewState = private::TransformedState<Child::ViewState>;
fn build(&self, ctx: &mut ViewCtx, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let (mut child_pod, child_state) = self.child.build(ctx, app_state);
let state = private::TransformedState {
child: child_state,
previous_transform: child_pod.new_widget.options.transform,
};
child_pod.new_widget.options.transform = self.transform * state.previous_transform;
(child_pod, state)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
self.child.rebuild(
&prev.child,
&mut view_state.child,
ctx,
element.reborrow_mut(),
app_state,
);
let transform_changed = element.ctx.transform_has_changed();
if transform_changed {
view_state.previous_transform = element.ctx.transform();
}
if self.transform != prev.transform || transform_changed {
element
.ctx
.set_transform(self.transform * view_state.previous_transform);
}
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: Mut<'_, Self::Element>,
) {
self.child.teardown(&mut view_state.child, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> xilem_core::MessageResult<Action> {
self.child
.message(&mut view_state.child, message, element, app_state)
}
}