From f1d5cf9209d8f8855e6f7e7059fa9551d9c9f94f Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Mon, 4 Nov 2024 21:34:10 -0700 Subject: [PATCH] event transform and rotation --- src/context.rs | 83 +++++++++++++++++++++++--------------------- src/event.rs | 50 ++++---------------------- src/style.rs | 22 ++++++++---- src/view_state.rs | 4 ++- src/window_handle.rs | 15 ++++---- tiny_skia/src/lib.rs | 58 +++++++++++++++++++++---------- vello/src/lib.rs | 2 +- 7 files changed, 114 insertions(+), 120 deletions(-) diff --git a/src/context.rs b/src/context.rs index 5ee3af97..dee9b813 100644 --- a/src/context.rs +++ b/src/context.rs @@ -427,12 +427,15 @@ impl<'a> EventCx<'a> { pub(crate) fn offset_event(&self, id: ViewId, event: Event) -> Event { let state = id.state(); let viewport = state.borrow().viewport; + let transform = state.borrow().transform; if let Some(layout) = id.get_layout() { - event.offset(( - layout.location.x as f64 - viewport.map(|rect| rect.x0).unwrap_or(0.0), - layout.location.y as f64 - viewport.map(|rect| rect.y0).unwrap_or(0.0), - )) + event.transform( + Affine::translate(( + layout.location.x as f64 - viewport.map(|rect| rect.x0).unwrap_or(0.0), + layout.location.y as f64 - viewport.map(|rect| rect.y0).unwrap_or(0.0), + )) * transform, + ) } else { event } @@ -634,6 +637,40 @@ impl<'a> StyleCx<'a> { view_state.borrow_mut().combined_style = modified; + let mut transform = Affine::IDENTITY; + + let transform_x = match view_state.borrow().layout_props.translate_x() { + crate::unit::PxPct::Px(px) => px, + crate::unit::PxPct::Pct(pct) => pct / 100., + }; + let transform_y = match view_state.borrow().layout_props.translate_y() { + crate::unit::PxPct::Px(px) => px, + crate::unit::PxPct::Pct(pct) => pct / 100., + }; + transform *= Affine::translate(Vec2 { + x: transform_x, + y: transform_y, + }); + + let scale_x = view_state.borrow().layout_props.scale_x().0 / 100.; + let scale_y = view_state.borrow().layout_props.scale_y().0 / 100.; + let size = view_id.layout_rect(); + let center_x = size.width() / 2.; + let center_y = size.height() / 2.; + transform *= Affine::translate(Vec2 { + x: center_x, + y: center_y, + }); + transform *= Affine::scale_non_uniform(scale_x, scale_y); + let rotation = view_state.borrow().layout_props.rotation().0; + transform *= Affine::rotate(rotation); + transform *= Affine::translate(Vec2 { + x: -center_x, + y: -center_y, + }); + + view_state.borrow_mut().transform = transform; + self.restore(); } @@ -800,6 +837,7 @@ impl<'a> ComputeLayoutCx<'a> { }; view_state.borrow_mut().layout_rect = layout_rect; + self.restore(); Some(layout_rect) @@ -1095,43 +1133,8 @@ impl<'a> PaintCx<'a> { x: offset.x as f64, y: offset.y as f64, }); + self.transform *= id.state().borrow().transform; - let view_state = id.state(); - let transform_x = match view_state.borrow().layout_props.translate_x() { - crate::unit::PxPct::Px(px) => px, - crate::unit::PxPct::Pct(pct) => pct / 100., - }; - let transform_y = match view_state.borrow().layout_props.translate_y() { - crate::unit::PxPct::Px(px) => px, - crate::unit::PxPct::Pct(pct) => pct / 100., - }; - self.transform *= Affine::translate(Vec2 { - x: transform_x, - y: transform_y, - }); - - let scale_x = match view_state.borrow().layout_props.scale_x() { - crate::unit::PxPct::Px(px) => px / layout.size.width as f64, - crate::unit::PxPct::Pct(pct) => pct / 100., - }; - let scale_y = match view_state.borrow().layout_props.scale_y() { - crate::unit::PxPct::Px(px) => px / layout.size.height as f64, - crate::unit::PxPct::Pct(pct) => pct / 100., - }; - let center_x = layout.size.width as f64 / 2.0; - let center_y = layout.size.height as f64 / 2.0; - self.transform *= Affine::translate(Vec2 { - x: center_x, - y: center_y, - }); - self.transform *= Affine::scale_non_uniform(scale_x, scale_y); - self.transform *= Affine::translate(Vec2 { - x: -center_x, - y: -center_y, - }); - - // let rotation = view_state.borrow().layout_props.rotation().0; - // self.transform *= Affine::rotate(rotation); self.paint_state.renderer_mut().transform(self.transform); if let Some(rect) = self.clip.as_mut() { diff --git a/src/event.rs b/src/event.rs index 8cb520ff..88b67e75 100644 --- a/src/event.rs +++ b/src/event.rs @@ -2,7 +2,7 @@ use floem_winit::{ keyboard::{KeyCode, PhysicalKey}, window::Theme, }; -use peniko::kurbo::{Point, Size}; +use peniko::kurbo::{Affine, Point, Size}; use crate::{ dropped_file::DroppedFileEvent, @@ -247,57 +247,19 @@ impl Event { } } - pub fn scale(mut self, scale: f64) -> Event { + pub fn transform(mut self, transform: Affine) -> Event { match &mut self { Event::PointerDown(pointer_event) | Event::PointerUp(pointer_event) => { - pointer_event.pos.x /= scale; - pointer_event.pos.y /= scale; + pointer_event.pos = transform.inverse() * pointer_event.pos; } Event::PointerMove(pointer_event) => { - pointer_event.pos.x /= scale; - pointer_event.pos.y /= scale; + pointer_event.pos = transform.inverse() * pointer_event.pos; } Event::PointerWheel(pointer_event) => { - pointer_event.pos.x /= scale; - pointer_event.pos.y /= scale; + pointer_event.pos = transform.inverse() * pointer_event.pos; } Event::DroppedFile(event) => { - event.pos.x /= scale; - event.pos.y /= scale; - } - Event::PointerLeave - | Event::KeyDown(_) - | Event::KeyUp(_) - | Event::FocusGained - | Event::FocusLost - | Event::ImeEnabled - | Event::ImeDisabled - | Event::ImePreedit { .. } - | Event::ThemeChanged(_) - | Event::ImeCommit(_) - | Event::WindowClosed - | Event::WindowResized(_) - | Event::WindowMoved(_) - | Event::WindowMaximizeChanged(_) - | Event::WindowGotFocus - | Event::WindowLostFocus => {} - } - self - } - - pub fn offset(mut self, offset: (f64, f64)) -> Event { - match &mut self { - Event::PointerDown(pointer_event) | Event::PointerUp(pointer_event) => { - pointer_event.pos -= offset; - } - Event::PointerMove(pointer_event) => { - pointer_event.pos -= offset; - } - Event::PointerWheel(pointer_event) => { - pointer_event.pos -= offset; - } - Event::DroppedFile(event) => { - event.pos -= offset; + event.pos = transform.inverse() * event.pos; } Event::PointerLeave | Event::KeyDown(_) diff --git a/src/style.rs b/src/style.rs index 7268dc71..2cff19fc 100644 --- a/src/style.rs +++ b/src/style.rs @@ -34,7 +34,7 @@ use taffy::{ use crate::context::InteractionState; use crate::easing::*; use crate::responsive::{ScreenSize, ScreenSizeBp}; -use crate::unit::{Px, PxPct, PxPctAuto, UnitExt}; +use crate::unit::{Pct, Px, PxPct, PxPctAuto, UnitExt}; use crate::view::{IntoView, View}; use crate::views::{empty, stack, text, Decorators}; @@ -164,6 +164,14 @@ impl StylePropValue for Px { self.0.interpolate(&other.0, value).map(Px) } } +impl StylePropValue for Pct { + fn debug_view(&self) -> Option> { + Some(text(format!("{}%", self.0)).into_any()) + } + fn interpolate(&self, other: &Self, value: f64) -> Option { + self.0.interpolate(&other.0, value).map(Pct) + } +} impl StylePropValue for PxPctAuto { fn debug_view(&self) -> Option> { let label = match self { @@ -1651,11 +1659,11 @@ define_builtin_props!( AspectRatio aspect_ratio: Option {} = None, ColGap col_gap nocb: PxPct {} = PxPct::Px(0.), RowGap row_gap nocb: PxPct {} = PxPct::Px(0.), - ScaleX scale_x: PxPct {} = PxPct::Pct(100.), - ScaleY scale_y: PxPct {} = PxPct::Pct(100.), + ScaleX scale_x: Pct {} = Pct(100.), + ScaleY scale_y: Pct {} = Pct(100.), TranslateX translate_x: PxPct {} = PxPct::Px(0.), TranslateY translate_y: PxPct {} = PxPct::Px(0.), - // Rotation rotate: Px {} = Px(0.), + Rotation rotate: Px {} = Px(0.), ); prop_extractor! { @@ -1711,9 +1719,11 @@ prop_extractor! { pub translate_x: TranslateX, pub translate_y: TranslateY, - // pub rotation: Rotation, + pub rotation: Rotation, + } } + impl LayoutProps { pub fn to_style(&self) -> Style { Style::new() @@ -2261,7 +2271,7 @@ impl Style { self.set(ZIndex, Some(z_index)) } - pub fn scale(self, scale: impl Into) -> Self { + pub fn scale(self, scale: impl Into) -> Self { let val = scale.into(); self.scale_x(val).scale_y(val) } diff --git a/src/view_state.rs b/src/view_state.rs index 85a53567..e07b18e9 100644 --- a/src/view_state.rs +++ b/src/view_state.rs @@ -14,7 +14,7 @@ use crate::{ }; use bitflags::bitflags; use im::HashSet; -use peniko::kurbo::{Point, Rect}; +use peniko::kurbo::{Affine, Point, Rect}; use smallvec::SmallVec; use std::{cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc}; use taffy::tree::NodeId; @@ -174,6 +174,7 @@ pub struct ViewState { pub(crate) is_hidden_state: IsHiddenState, pub(crate) num_waiting_animations: u16, pub(crate) disable_default_events: HashSet, + pub(crate) transform: Affine, pub(crate) debug_name: SmallVec<[String; 1]>, } @@ -205,6 +206,7 @@ impl ViewState { is_hidden_state: IsHiddenState::None, num_waiting_animations: 0, disable_default_events: HashSet::new(), + transform: Affine::IDENTITY, debug_name: Default::default(), } } diff --git a/src/window_handle.rs b/src/window_handle.rs index f09eb4f1..8a6292ac 100644 --- a/src/window_handle.rs +++ b/src/window_handle.rs @@ -201,7 +201,7 @@ impl WindowHandle { pub fn event(&mut self, event: Event) { set_current_view(self.id); - let event = event.scale(self.app_state.scale); + let event = event.transform(Affine::scale(self.app_state.scale)); let mut cx = EventCx { app_state: &mut self.app_state, @@ -297,14 +297,11 @@ impl WindowHandle { let window_origin = id.state().borrow().window_origin; let layout = id.get_layout().unwrap_or_default(); let viewport = id.state().borrow().viewport.unwrap_or_default(); - cx.unconditional_view_event( - id, - event.clone().offset(( - window_origin.x - layout.location.x as f64 + viewport.x0, - window_origin.y - layout.location.y as f64 + viewport.y0, - )), - true, - ); + let transform = Affine::translate(( + window_origin.x - layout.location.x as f64 + viewport.x0, + window_origin.y - layout.location.y as f64 + viewport.y0, + )); + cx.unconditional_view_event(id, event.clone().transform(transform), true); } if let Event::PointerUp(_) = &event { diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index b0ae95d8..ea3aec86 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -7,7 +7,7 @@ use floem_renderer::tiny_skia::{ }; use floem_renderer::Img; use floem_renderer::Renderer; -use peniko::kurbo::{PathEl, Size}; +use peniko::kurbo::{self, PathEl, Size, Vec2}; use peniko::{ kurbo::{Affine, Point, Rect, Shape}, BrushRef, Color, GradientKind, @@ -112,7 +112,7 @@ impl f64 { + pub const fn scale(&self) -> f64 { self.scale } @@ -218,8 +218,8 @@ impl TinySkiaRenderer { clip.intersect(&rect) } - /// Renders the pixmap at the position without transforming it. - fn render_pixmap_direct(&mut self, pixmap: &Pixmap, x: f32, y: f32) { + /// Renders the pixmap at the position and transforms it with the given transform. + fn render_pixmap_direct(&mut self, pixmap: &Pixmap, x: f32, y: f32, transform: kurbo::Affine) { let rect = try_ret!(tiny_skia::Rect::from_xywh( x, y, @@ -237,9 +237,19 @@ impl TinySkiaRenderer { ..Default::default() }; + let transform = transform.as_coeffs(); + let scale = self.scale as f32; + let transform = Transform::from_row( + transform[0] as f32, + transform[1] as f32, + transform[2] as f32, + transform[3] as f32, + transform[4] as f32, + transform[5] as f32, + ) + .post_scale(scale, scale); if let Some(rect) = self.clip_rect(rect) { - self.pixmap - .fill_rect(rect, &paint, Transform::identity(), None); + self.pixmap.fill_rect(rect, &paint, transform, None); } } @@ -423,6 +433,11 @@ impl Color::rgba8(c.r(), c.g(), c.b(), c.a()), - None => Color::BLACK, - }; let pixmap = self.cache_glyph(cache_key, color); - if let Some(glyph) = pixmap { self.render_pixmap_direct( &glyph.pixmap, - glyph_x + glyph.left, - glyph_y - glyph.top, + new_x as f32 + glyph.left, + new_y as f32 - glyph.top, + transform, ); } } @@ -555,8 +566,17 @@ impl