From 54f83dbb6cc5a36390d340463e44340e6bff25b6 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Tue, 22 Oct 2024 15:38:34 +0200 Subject: [PATCH] TMP --- masonry/src/event.rs | 10 +- masonry/src/passes/anim.rs | 27 +++++ masonry/src/passes/compose.rs | 117 ++++++++++++++++++++++ masonry/src/testing/helper_widgets.rs | 54 ++++++---- masonry/src/widget/tests/safety_rails.rs | 14 +-- masonry/src/widget/tests/status_change.rs | 4 +- 6 files changed, 187 insertions(+), 39 deletions(-) diff --git a/masonry/src/event.rs b/masonry/src/event.rs index 9df8c87fe..8fe0c530b 100644 --- a/masonry/src/event.rs +++ b/masonry/src/event.rs @@ -182,7 +182,7 @@ impl From for PointerButtons { // TODO - Touchpad, Touch, AxisMotion // TODO - How to handle CursorEntered? // Note to self: Events like "pointerenter", "pointerleave" are handled differently at the Widget level. But that's weird because WidgetPod can distribute them. Need to think about this again. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum PointerEvent { PointerDown(PointerButton, PointerState), PointerUp(PointerButton, PointerState), @@ -198,7 +198,7 @@ pub enum PointerEvent { // TODO - Clipboard Paste? // TODO skip is_synthetic=true events -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TextEvent { KeyboardKey(KeyEvent, ModifiersState), Ime(Ime), @@ -207,13 +207,13 @@ pub enum TextEvent { FocusChange(bool), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct AccessEvent { pub action: accesskit::Action, pub data: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct PointerState { // TODO // pub device_id: DeviceId, @@ -242,7 +242,7 @@ pub enum WindowTheme { /// may occur during an [`on_event`](crate::Widget::on_event) pass, if some /// widget has been added then. #[non_exhaustive] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] #[allow(variant_size_differences)] pub enum Update { /// Sent to a `Widget` when it is added to the widget tree. This should be diff --git a/masonry/src/passes/anim.rs b/masonry/src/passes/anim.rs index f670e86b5..ed9c7804c 100644 --- a/masonry/src/passes/anim.rs +++ b/masonry/src/passes/anim.rs @@ -64,3 +64,30 @@ pub(crate) fn run_update_anim_pass(root: &mut RenderRoot, elapsed_ns: u64) { elapsed_ns, ); } + +// --- MARK: TESTS --- +#[cfg(test)] +mod tests { + use crate::testing::{Record, Recording, TestHarness, TestWidgetExt as _}; + use crate::widget::SizedBox; + use crate::WidgetId; + + #[test] + fn test_update_anim_pass() { + let record = Recording::default(); + + let id = WidgetId::next(); + let widget = SizedBox::new_with_id(SizedBox::empty().record(&record), id); + + let mut harness = TestHarness::create(widget); + + record.clear(); + + harness.edit_widget(id, |mut widget| { + widget.ctx.request_anim_frame(); + }); + harness.animate_ms(20); + + assert_eq!(record.next(), Some(Record::AnimFrame(20_000_000))); + } +} diff --git a/masonry/src/passes/compose.rs b/masonry/src/passes/compose.rs index 06f3c37cc..e34743ff1 100644 --- a/masonry/src/passes/compose.rs +++ b/masonry/src/passes/compose.rs @@ -93,3 +93,120 @@ pub(crate) fn run_compose_pass(root: &mut RenderRoot) { Vec2::ZERO, ); } + +// --- MARK: TESTS --- +#[cfg(test)] +mod tests { + use smallvec::smallvec; + use vello::kurbo::{Point, Size}; + + use crate::testing::{ + widget_ids, ModularWidget, Record, Recording, TestHarness, TestWidgetExt as _, + }; + use crate::widget::SizedBox; + use crate::WidgetPod; + + use super::*; + + #[test] + fn test_compose_pass() { + let record = Recording::default(); + let [parent_id, recorder_id] = widget_ids(); + let inner = SizedBox::new_with_id(SizedBox::empty().record(&record), recorder_id); + let parent = ModularWidget::new((WidgetPod::new(inner), Point::ZERO, Vec2::ZERO)) + .layout_fn(|state, ctx, bc| { + let (child, pos, _) = state; + ctx.run_layout(child, bc); + ctx.place_child(child, *pos); + Size::ZERO + }) + .compose_fn(|state, ctx| { + let (child, _, translation) = state; + ctx.set_child_translation(child, *translation); + }) + .register_children_fn(move |state, ctx| { + let (child, _, _) = state; + ctx.register_child(child); + }) + .children_fn(|(child, _, _)| smallvec![child.id()]); + let root = SizedBox::new_with_id(parent, parent_id); + + let mut harness = TestHarness::create(root); + record.clear(); + + harness.edit_widget(parent_id, |mut widget| { + // TODO - Find better way to express this + let mut widget = widget.downcast::, Point, Vec2)>>(); + widget.widget.state.1 = Point::new(30., 30.); + widget.ctx.request_layout(); + }); + assert_eq!( + record.drain(), + vec![ + Record::Layout(Size::new(400., 400.)), + Record::Compose(Point::new(30., 30.)), + ] + ); + + harness.edit_widget(parent_id, |mut widget| { + // TODO - Find better way to express this + let mut widget = widget.downcast::, Point, Vec2)>>(); + widget.widget.state.2 = Vec2::new(8., 8.); + widget.ctx.request_compose(); + }); + + // TODO - Should changing a parent transform call the child's compose method? + assert_eq!(record.drain(), vec![]); + } + + #[test] + fn test_move_text_input() { + let record = Recording::default(); + let [parent_id, recorder_id] = widget_ids(); + let inner = SizedBox::new_with_id(SizedBox::empty().record(&record), recorder_id); + let parent = ModularWidget::new((WidgetPod::new(inner), Point::ZERO, Vec2::ZERO)) + .layout_fn(|state, ctx, bc| { + let (child, pos, _) = state; + ctx.run_layout(child, bc); + ctx.place_child(child, *pos); + Size::ZERO + }) + .compose_fn(|state, ctx| { + let (child, _, translation) = state; + ctx.set_child_translation(child, *translation); + }) + .register_children_fn(move |state, ctx| { + let (child, _, _) = state; + ctx.register_child(child); + }) + .children_fn(|(child, _, _)| smallvec![child.id()]); + let root = SizedBox::new_with_id(parent, parent_id); + + let mut harness = TestHarness::create(root); + record.clear(); + + harness.edit_widget(parent_id, |mut widget| { + // TODO - Find better way to express this + let mut widget = widget.downcast::, Point, Vec2)>>(); + widget.widget.state.1 = Point::new(30., 30.); + widget.ctx.request_layout(); + }); + assert_eq!( + record.drain(), + vec![ + Record::Layout(Size::new(400., 400.)), + Record::Compose(Point::new(30., 30.)), + ] + ); + + harness.edit_widget(parent_id, |mut widget| { + // TODO - Find better way to express this + let mut widget = widget.downcast::, Point, Vec2)>>(); + widget.widget.state.2 = Vec2::new(8., 8.); + widget.ctx.request_compose(); + }); + + // TODO - Should changing a parent transform call the child's compose method? + assert_eq!(record.drain(), vec![]); + } +} diff --git a/masonry/src/testing/helper_widgets.rs b/masonry/src/testing/helper_widgets.rs index accb1f5e2..0fd988220 100644 --- a/masonry/src/testing/helper_widgets.rs +++ b/masonry/src/testing/helper_widgets.rs @@ -13,7 +13,7 @@ use std::collections::VecDeque; use std::rc::Rc; use accesskit::{NodeBuilder, Role}; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use tracing::trace_span; use vello::Scene; use widget::widget::get_child_at_pos; @@ -43,7 +43,7 @@ pub const REPLACE_CHILD: Selector = Selector::new("masonry-test.replace-child"); /// /// This widget is generic over its state, which is passed in at construction time. pub struct ModularWidget { - state: S, + pub state: S, accepts_pointer_interaction: bool, accepts_focus: bool, accepts_text_input: bool, @@ -102,18 +102,18 @@ pub struct Recording(Rc>>); /// A recording of a method call on a widget. /// /// Each member of the enum corresponds to one of the methods on `Widget`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Record { - PE(PointerEvent), - TE(TextEvent), - AE(AccessEvent), - AF(u64), - RC, - U(Update), + PointerEvent(PointerEvent), + TextEvent(TextEvent), + AccessEvent(AccessEvent), + AnimFrame(u64), + RegisterChildren, + Update(Update), Layout(Size), - Compose, + Compose(Point), Paint, - Access, + Accessibilty, } /// External trait implemented for all widgets. @@ -161,6 +161,22 @@ impl ModularWidget { } } +impl ModularWidget> { + pub fn new_parent(child: W) -> ModularWidget> { + let child = WidgetPod::new(child); + ModularWidget::new(child) + .register_children_fn(move |child, ctx| { + ctx.register_child(child); + }) + .layout_fn(move |child, ctx, bc| { + let size = ctx.run_layout(child, bc); + ctx.place_child(child, Point::ZERO); + size + }) + .children_fn(|child| smallvec![child.id()]) + } +} + /// Builder methods. /// /// Each method takes a flag which is then returned by the matching Widget method. @@ -508,32 +524,32 @@ impl Recording { #[warn(clippy::missing_trait_methods)] impl Widget for Recorder { fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &event::PointerEvent) { - self.recording.push(Record::PE(event.clone())); + self.recording.push(Record::PointerEvent(event.clone())); self.child.on_pointer_event(ctx, event); } fn on_text_event(&mut self, ctx: &mut EventCtx, event: &event::TextEvent) { - self.recording.push(Record::TE(event.clone())); + self.recording.push(Record::TextEvent(event.clone())); self.child.on_text_event(ctx, event); } fn on_access_event(&mut self, ctx: &mut EventCtx, event: &AccessEvent) { - self.recording.push(Record::AE(event.clone())); + self.recording.push(Record::AccessEvent(event.clone())); self.child.on_access_event(ctx, event); } fn on_anim_frame(&mut self, ctx: &mut UpdateCtx, interval: u64) { - self.recording.push(Record::AF(interval)); + self.recording.push(Record::AnimFrame(interval)); self.child.on_anim_frame(ctx, interval); } fn register_children(&mut self, ctx: &mut RegisterCtx) { - self.recording.push(Record::RC); + self.recording.push(Record::RegisterChildren); self.child.register_children(ctx); } fn update(&mut self, ctx: &mut UpdateCtx, event: &Update) { - self.recording.push(Record::U(event.clone())); + self.recording.push(Record::Update(event.clone())); self.child.update(ctx, event); } @@ -544,7 +560,7 @@ impl Widget for Recorder { } fn compose(&mut self, ctx: &mut ComposeCtx) { - self.recording.push(Record::Compose); + self.recording.push(Record::Compose(ctx.window_origin())); self.child.compose(ctx); } @@ -558,7 +574,7 @@ impl Widget for Recorder { } fn accessibility(&mut self, ctx: &mut AccessCtx, node: &mut NodeBuilder) { - self.recording.push(Record::Access); + self.recording.push(Record::Accessibilty); self.child.accessibility(ctx, node); } diff --git a/masonry/src/widget/tests/safety_rails.rs b/masonry/src/widget/tests/safety_rails.rs index 5cf442ccb..d81a04f6f 100644 --- a/masonry/src/widget/tests/safety_rails.rs +++ b/masonry/src/widget/tests/safety_rails.rs @@ -1,24 +1,12 @@ // Copyright 2022 the Xilem Authors // SPDX-License-Identifier: Apache-2.0 -use smallvec::smallvec; - use crate::testing::{ModularWidget, TestHarness, TestWidgetExt}; use crate::widget::Flex; use crate::{Point, PointerButton, Size, Update, Widget, WidgetId, WidgetPod}; fn make_parent_widget(child: W) -> ModularWidget> { - let child = WidgetPod::new(child); - ModularWidget::new(child) - .register_children_fn(move |child, ctx| { - ctx.register_child(child); - }) - .layout_fn(move |child, ctx, bc| { - let size = ctx.run_layout(child, bc); - ctx.place_child(child, Point::ZERO); - size - }) - .children_fn(|child| smallvec![child.id()]) + ModularWidget::new_parent(child) } #[cfg(FALSE)] diff --git a/masonry/src/widget/tests/status_change.rs b/masonry/src/widget/tests/status_change.rs index 7b355a9bb..1daad2d63 100644 --- a/masonry/src/widget/tests/status_change.rs +++ b/masonry/src/widget/tests/status_change.rs @@ -11,7 +11,7 @@ use crate::*; fn next_pointer_event(recording: &Recording) -> Option { while let Some(event) = recording.next() { match event { - Record::PE(event) => { + Record::PointerEvent(event) => { return Some(event); } _ => {} @@ -27,7 +27,7 @@ fn is_hovered(harness: &TestHarness, id: WidgetId) -> bool { fn next_hovered_changed(recording: &Recording) -> Option { while let Some(event) = recording.next() { match event { - Record::U(Update::HoveredChanged(hovered)) => return Some(hovered), + Record::Update(Update::HoveredChanged(hovered)) => return Some(hovered), _ => {} } }