mirror of https://github.com/linebender/xilem
TMP
This commit is contained in:
parent
9c397da91f
commit
54f83dbb6c
|
@ -182,7 +182,7 @@ impl From<PointerButton> 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<accesskit::ActionData>,
|
||||
}
|
||||
|
||||
#[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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::<ModularWidget<(WidgetPod<SizedBox>, 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::<ModularWidget<(WidgetPod<SizedBox>, 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::<ModularWidget<(WidgetPod<SizedBox>, 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::<ModularWidget<(WidgetPod<SizedBox>, 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![]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<S> {
|
||||
state: S,
|
||||
pub state: S,
|
||||
accepts_pointer_interaction: bool,
|
||||
accepts_focus: bool,
|
||||
accepts_text_input: bool,
|
||||
|
@ -102,18 +102,18 @@ pub struct Recording(Rc<RefCell<VecDeque<Record>>>);
|
|||
/// 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<S> ModularWidget<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> ModularWidget<WidgetPod<W>> {
|
||||
pub fn new_parent(child: W) -> ModularWidget<WidgetPod<W>> {
|
||||
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<W: Widget> Widget for Recorder<W> {
|
||||
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<W: Widget> Widget for Recorder<W> {
|
|||
}
|
||||
|
||||
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<W: Widget> Widget for Recorder<W> {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<W: Widget>(child: W) -> ModularWidget<WidgetPod<W>> {
|
||||
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)]
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::*;
|
|||
fn next_pointer_event(recording: &Recording) -> Option<PointerEvent> {
|
||||
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<bool> {
|
||||
while let Some(event) = recording.next() {
|
||||
match event {
|
||||
Record::U(Update::HoveredChanged(hovered)) => return Some(hovered),
|
||||
Record::Update(Update::HoveredChanged(hovered)) => return Some(hovered),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue