From 3136ecb90f220ef8359c31fa49492fcaeffd58b1 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 18 Nov 2024 22:43:56 +0100 Subject: [PATCH 01/18] Initial dirty hack to get transforms per widget working --- masonry/src/contexts.rs | 10 ++++-- masonry/src/passes/compose.rs | 28 +++++++++-------- masonry/src/passes/paint.rs | 4 +-- masonry/src/testing/helper_widgets.rs | 8 +++++ masonry/src/widget/button.rs | 21 +++++++++++-- masonry/src/widget/flex.rs | 17 +++++++++++ masonry/src/widget/widget.rs | 16 ++++++++-- masonry/src/widget/widget_ref.rs | 3 +- masonry/src/widget/widget_state.rs | 12 +++++--- xilem/examples/mason.rs | 44 +++++++++++++++++++++------ xilem/src/view/button.rs | 21 +++++++++++-- xilem/src/view/flex.rs | 18 +++++++++-- xilem/src/view/mod.rs | 32 +++++++++++++++++++ 13 files changed, 194 insertions(+), 40 deletions(-) diff --git a/masonry/src/contexts.rs b/masonry/src/contexts.rs index 823ba89eb..d470109bf 100644 --- a/masonry/src/contexts.rs +++ b/masonry/src/contexts.rs @@ -537,6 +537,12 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, { self.widget_state.request_compose = true; } + pub fn transform_changed(&mut self) { + trace!("transform_changed"); + self.widget_state.transform_changed = true; + self.request_compose(); + } + /// Request an animation frame. pub fn request_anim_frame(&mut self) { trace!("request_anim_frame"); @@ -1092,7 +1098,7 @@ impl LayoutCtx<'_> { } if origin != self.get_child_state_mut(child).origin { self.get_child_state_mut(child).origin = origin; - self.get_child_state_mut(child).translation_changed = true; + self.get_child_state_mut(child).transform_changed = true; } self.get_child_state_mut(child) .is_expecting_place_child_call = false; @@ -1133,7 +1139,7 @@ impl ComposeCtx<'_> { let child = self.get_child_state_mut(child); if translation != child.translation { child.translation = translation; - child.translation_changed = true; + child.transform_changed = true; } } } diff --git a/masonry/src/passes/compose.rs b/masonry/src/passes/compose.rs index 88cf7edc0..41558ecbc 100644 --- a/masonry/src/passes/compose.rs +++ b/masonry/src/passes/compose.rs @@ -3,7 +3,7 @@ use tracing::info_span; use tree_arena::ArenaMut; -use vello::kurbo::Vec2; +use vello::kurbo::Affine; use crate::passes::{enter_span_if, recurse_on_children}; use crate::render_root::{RenderRoot, RenderRootSignal, RenderRootState}; @@ -14,8 +14,8 @@ fn compose_widget( global_state: &mut RenderRootState, mut widget: ArenaMut<'_, Box>, mut state: ArenaMut<'_, WidgetState>, - parent_moved: bool, - parent_translation: Vec2, + parent_transformed: bool, + parent_window_transform: Affine, ) { let _span = enter_span_if( global_state.trace.compose, @@ -24,14 +24,17 @@ fn compose_widget( state.reborrow(), ); - let moved = parent_moved || state.item.translation_changed; - let translation = parent_translation + state.item.translation + state.item.origin.to_vec2(); - state.item.window_origin = translation.to_point(); + let transformed = parent_transformed || state.item.transform_changed; - if !parent_moved && !state.item.translation_changed && !state.item.needs_compose { + if !transformed && !state.item.needs_compose { return; } + state.item.window_transform = parent_window_transform + .then_translate(state.item.translation + state.item.origin.to_vec2()) + * widget.item.transform(); + state.item.window_origin = state.item.window_transform.translation().to_point(); + let mut ctx = ComposeCtx { global_state, widget_state: state.item, @@ -43,7 +46,7 @@ fn compose_widget( } // TODO - Add unit tests for this. - if moved && state.item.accepts_text_input && global_state.is_focused(state.item.id) { + if transformed && state.item.accepts_text_input && global_state.is_focused(state.item.id) { let ime_area = state.item.get_ime_area(); global_state.emit_signal(RenderRootSignal::new_ime_moved_signal(ime_area)); } @@ -55,9 +58,10 @@ fn compose_widget( state.item.needs_compose = false; state.item.request_compose = false; - state.item.translation_changed = false; + state.item.transform_changed = false; let id = state.item.id; + let parent_transform = state.item.window_transform; let parent_state = state.item; recurse_on_children( id, @@ -68,8 +72,8 @@ fn compose_widget( global_state, widget, state.reborrow_mut(), - moved, - translation, + transformed, + parent_transform, ); parent_state.merge_up(state.item); }, @@ -92,6 +96,6 @@ pub(crate) fn run_compose_pass(root: &mut RenderRoot) { root_widget, root_state, false, - Vec2::ZERO, + Affine::IDENTITY, ); } diff --git a/masonry/src/passes/paint.rs b/masonry/src/passes/paint.rs index fb059b44c..9686b7f9b 100644 --- a/masonry/src/passes/paint.rs +++ b/masonry/src/passes/paint.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use tracing::{info_span, trace}; use tree_arena::ArenaMut; -use vello::kurbo::{Affine, Stroke}; +use vello::kurbo::Stroke; use vello::peniko::Mix; use vello::Scene; @@ -53,7 +53,7 @@ fn paint_widget( let clip = state.item.clip_path; let has_clip = clip.is_some(); - let transform = Affine::translate(state.item.window_origin.to_vec2()); + let transform = state.item.window_transform; let scene = scenes.get(&id).unwrap(); if let Some(clip) = clip { diff --git a/masonry/src/testing/helper_widgets.rs b/masonry/src/testing/helper_widgets.rs index cf69eb506..b2b4903e0 100644 --- a/masonry/src/testing/helper_widgets.rs +++ b/masonry/src/testing/helper_widgets.rs @@ -413,6 +413,10 @@ impl Widget for ModularWidget { fn as_mut_any(&mut self) -> &mut dyn std::any::Any { self.as_mut_dyn_any() } + + fn transform(&self) -> Affine { + Affine::IDENTITY + } } impl ReplaceChild { @@ -613,4 +617,8 @@ impl Widget for Recorder { fn as_mut_any(&mut self) -> &mut dyn std::any::Any { self.child.as_mut_any() } + + fn transform(&self) -> Affine { + self.child.transform() + } } diff --git a/masonry/src/widget/button.rs b/masonry/src/widget/button.rs index 551727e09..0eead488f 100644 --- a/masonry/src/widget/button.rs +++ b/masonry/src/widget/button.rs @@ -6,6 +6,7 @@ use accesskit::{Node, Role}; use smallvec::{smallvec, SmallVec}; use tracing::{trace, trace_span, Span}; +use vello::kurbo::Affine; use vello::Scene; use crate::action::Action; @@ -28,6 +29,7 @@ const LABEL_INSETS: Insets = Insets::uniform_xy(8., 2.); /// Emits [`Action::ButtonPressed`] when pressed. pub struct Button { label: WidgetPod