mirror of https://github.com/linebender/xilem
Refactor rewrite passes (#639)
Create run_rewrite_passes function. Remove redundant uses of synthetic WidgetState. Small bugfixes. Move RenderRoot::root_layout code into the layout pass. Rename update_new_widgets pass to update_widget_tree.
This commit is contained in:
parent
9899a700c7
commit
76c808b454
|
@ -235,6 +235,7 @@ impl MasonryState<'_> {
|
|||
use_system_fonts: true,
|
||||
size_policy: WindowSizePolicy::User,
|
||||
scale_factor,
|
||||
test_font: None,
|
||||
},
|
||||
),
|
||||
renderer: None,
|
||||
|
|
|
@ -72,11 +72,9 @@ fn compose_widget(
|
|||
}
|
||||
|
||||
// --- MARK: ROOT ---
|
||||
pub(crate) fn root_compose(root: &mut RenderRoot, global_root_state: &mut WidgetState) {
|
||||
pub(crate) fn root_compose(root: &mut RenderRoot) {
|
||||
let _span = info_span!("compose").entered();
|
||||
|
||||
let (root_widget, root_state) = root.widget_arena.get_pair_mut(root.root.id());
|
||||
compose_widget(&mut root.state, root_widget, root_state, false, Vec2::ZERO);
|
||||
|
||||
global_root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@ use winit::keyboard::{KeyCode, PhysicalKey};
|
|||
|
||||
use crate::passes::merge_state_up;
|
||||
use crate::render_root::RenderRoot;
|
||||
use crate::{
|
||||
AccessEvent, EventCtx, Handled, PointerEvent, TextEvent, Widget, WidgetId, WidgetState,
|
||||
};
|
||||
use crate::{AccessEvent, EventCtx, Handled, PointerEvent, TextEvent, Widget, WidgetId};
|
||||
|
||||
// --- MARK: HELPERS ---
|
||||
fn get_target_widget(
|
||||
|
@ -35,7 +33,6 @@ fn get_target_widget(
|
|||
|
||||
fn run_event_pass<E>(
|
||||
root: &mut RenderRoot,
|
||||
root_state: &mut WidgetState,
|
||||
target: Option<WidgetId>,
|
||||
event: &E,
|
||||
allow_pointer_capture: bool,
|
||||
|
@ -74,20 +71,13 @@ fn run_event_pass<E>(
|
|||
target_widget_id = parent_id;
|
||||
}
|
||||
|
||||
// Merge root widget state with synthetic state created at beginning of pass
|
||||
root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
|
||||
|
||||
Handled::from(is_handled)
|
||||
}
|
||||
|
||||
// TODO - Send synthetic MouseLeave events
|
||||
|
||||
// --- MARK: POINTER_EVENT ---
|
||||
pub(crate) fn root_on_pointer_event(
|
||||
root: &mut RenderRoot,
|
||||
root_state: &mut WidgetState,
|
||||
event: &PointerEvent,
|
||||
) -> Handled {
|
||||
pub(crate) fn root_on_pointer_event(root: &mut RenderRoot, event: &PointerEvent) -> Handled {
|
||||
let _span = info_span!("pointer_event").entered();
|
||||
if !event.is_high_density() {
|
||||
debug!("Running ON_POINTER_EVENT pass with {}", event.short_name());
|
||||
|
@ -99,7 +89,6 @@ pub(crate) fn root_on_pointer_event(
|
|||
|
||||
let handled = run_event_pass(
|
||||
root,
|
||||
root_state,
|
||||
target_widget_id,
|
||||
event,
|
||||
matches!(event, PointerEvent::PointerDown(..)),
|
||||
|
@ -135,11 +124,7 @@ pub(crate) fn root_on_pointer_event(
|
|||
// - If a Widget has focus, then none of its parents is hidden
|
||||
|
||||
// --- MARK: TEXT EVENT ---
|
||||
pub(crate) fn root_on_text_event(
|
||||
root: &mut RenderRoot,
|
||||
root_state: &mut WidgetState,
|
||||
event: &TextEvent,
|
||||
) -> Handled {
|
||||
pub(crate) fn root_on_text_event(root: &mut RenderRoot, event: &TextEvent) -> Handled {
|
||||
let _span = info_span!("text_event").entered();
|
||||
if !event.is_high_density() {
|
||||
debug!("Running ON_TEXT_EVENT pass with {}", event.short_name());
|
||||
|
@ -147,16 +132,9 @@ pub(crate) fn root_on_text_event(
|
|||
|
||||
let target = root.state.focused_widget;
|
||||
|
||||
let mut handled = run_event_pass(
|
||||
root,
|
||||
root_state,
|
||||
target,
|
||||
event,
|
||||
false,
|
||||
|widget, ctx, event| {
|
||||
widget.on_text_event(ctx, event);
|
||||
},
|
||||
);
|
||||
let mut handled = run_event_pass(root, target, event, false, |widget, ctx, event| {
|
||||
widget.on_text_event(ctx, event);
|
||||
});
|
||||
|
||||
// Handle Tab focus
|
||||
if let TextEvent::KeyboardKey(key, mods) = event {
|
||||
|
@ -185,26 +163,15 @@ pub(crate) fn root_on_text_event(
|
|||
}
|
||||
|
||||
// --- MARK: ACCESS EVENT ---
|
||||
pub(crate) fn root_on_access_event(
|
||||
root: &mut RenderRoot,
|
||||
root_state: &mut WidgetState,
|
||||
event: &AccessEvent,
|
||||
) -> Handled {
|
||||
pub(crate) fn root_on_access_event(root: &mut RenderRoot, event: &AccessEvent) -> Handled {
|
||||
let _span = info_span!("access_event").entered();
|
||||
debug!("Running ON_ACCESS_EVENT pass with {}", event.short_name());
|
||||
|
||||
let target = Some(event.target);
|
||||
|
||||
let mut handled = run_event_pass(
|
||||
root,
|
||||
root_state,
|
||||
target,
|
||||
event,
|
||||
false,
|
||||
|widget, ctx, event| {
|
||||
widget.on_access_event(ctx, event);
|
||||
},
|
||||
);
|
||||
let mut handled = run_event_pass(root, target, event, false, |widget, ctx, event| {
|
||||
widget.on_access_event(ctx, event);
|
||||
});
|
||||
|
||||
// Handle focus events
|
||||
match event.action {
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
//! before any translations applied in [`compose`](crate::passes::compose).
|
||||
//! Most of the logic for this pass happens in [`Widget::layout`] implementations.
|
||||
|
||||
use dpi::LogicalSize;
|
||||
use smallvec::SmallVec;
|
||||
use tracing::{info_span, trace};
|
||||
use vello::kurbo::{Point, Rect, Size};
|
||||
|
||||
use crate::render_root::RenderRoot;
|
||||
use crate::render_root::{RenderRoot, RenderRootSignal, WindowSizePolicy};
|
||||
use crate::tree_arena::ArenaRefChildren;
|
||||
use crate::widget::WidgetState;
|
||||
use crate::{BoxConstraints, LayoutCtx, Widget, WidgetPod};
|
||||
|
@ -313,24 +314,37 @@ pub(crate) fn run_layout_on<W: Widget>(
|
|||
}
|
||||
|
||||
// --- MARK: ROOT ---
|
||||
pub(crate) fn root_layout(
|
||||
root: &mut RenderRoot,
|
||||
synthetic_root_state: &mut WidgetState,
|
||||
bc: &BoxConstraints,
|
||||
) -> Size {
|
||||
pub(crate) fn root_layout(root: &mut RenderRoot) {
|
||||
if !root.root_state().needs_layout {
|
||||
return;
|
||||
}
|
||||
|
||||
let _span = info_span!("layout").entered();
|
||||
|
||||
let window_size = root.get_kurbo_size();
|
||||
let bc = match root.size_policy {
|
||||
WindowSizePolicy::User => BoxConstraints::tight(window_size),
|
||||
WindowSizePolicy::Content => BoxConstraints::UNBOUNDED,
|
||||
};
|
||||
|
||||
let mut dummy_state = WidgetState::synthetic(root.root.id(), root.get_kurbo_size());
|
||||
let root_state_token = root.widget_arena.widget_states.root_token_mut();
|
||||
let root_widget_token = root.widget_arena.widgets.root_token_mut();
|
||||
let mut ctx = LayoutCtx {
|
||||
global_state: &mut root.state,
|
||||
widget_state: synthetic_root_state,
|
||||
widget_state: &mut dummy_state,
|
||||
widget_state_children: root_state_token,
|
||||
widget_children: root_widget_token,
|
||||
};
|
||||
|
||||
let size = run_layout_on(&mut ctx, &mut root.root, bc);
|
||||
let size = run_layout_on(&mut ctx, &mut root.root, &bc);
|
||||
ctx.place_child(&mut root.root, Point::ORIGIN);
|
||||
|
||||
size
|
||||
if let WindowSizePolicy::Content = root.size_policy {
|
||||
let new_size = LogicalSize::new(size.width, size.height).to_physical(root.scale_factor);
|
||||
if root.size != new_size {
|
||||
root.size = new_size;
|
||||
root.state.emit_signal(RenderRootSignal::SetSize(new_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use tracing::info_span;
|
|||
use crate::passes::merge_state_up;
|
||||
use crate::render_root::RenderRoot;
|
||||
use crate::widget::WidgetMut;
|
||||
use crate::{MutateCtx, Widget, WidgetId, WidgetState};
|
||||
use crate::{MutateCtx, Widget, WidgetId};
|
||||
|
||||
pub(crate) fn mutate_widget<R>(
|
||||
root: &mut RenderRoot,
|
||||
|
@ -44,15 +44,9 @@ pub(crate) fn mutate_widget<R>(
|
|||
|
||||
// TODO - Add link to mutate pass documentation
|
||||
/// Apply any deferred mutations (created using [`...Ctx::mutate_later`](crate::LayoutCtx::mutate_later)).
|
||||
pub(crate) fn run_mutate_pass(root: &mut RenderRoot, root_state: &mut WidgetState) {
|
||||
// TODO - Factor out into a "pre-event" function?
|
||||
// root.state.next_focused_widget = root.state.focused_widget;
|
||||
|
||||
pub(crate) fn run_mutate_pass(root: &mut RenderRoot) {
|
||||
let callbacks = std::mem::take(&mut root.state.mutate_callbacks);
|
||||
for callback in callbacks {
|
||||
mutate_widget(root, callback.id, callback.callback);
|
||||
}
|
||||
|
||||
root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
|
||||
// root.post_event_processing(&mut root_state);
|
||||
}
|
||||
|
|
|
@ -77,17 +77,15 @@ fn run_single_update_pass(
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
// --- MARK: UPDATE POINTER ---
|
||||
pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot, root_state: &mut WidgetState) {
|
||||
pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot) {
|
||||
let pointer_pos = root.last_mouse_pos.map(|pos| (pos.x, pos.y).into());
|
||||
|
||||
// Release pointer capture if target can no longer hold it.
|
||||
if let Some(id) = root.state.pointer_capture_target {
|
||||
if !root.is_still_interactive(id) {
|
||||
root.state.pointer_capture_target = None;
|
||||
root_on_pointer_event(root, root_state, &PointerEvent::new_pointer_leave());
|
||||
root_on_pointer_event(root, &PointerEvent::new_pointer_leave());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,15 +178,12 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot, root_state: &mut Wi
|
|||
|
||||
root.state.cursor_icon = new_cursor;
|
||||
root.state.hovered_path = next_hovered_path;
|
||||
|
||||
// Merge root widget state with synthetic state created at beginning of pass
|
||||
root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
// --- MARK: UPDATE FOCUS ---
|
||||
pub(crate) fn run_update_focus_pass(root: &mut RenderRoot, root_state: &mut WidgetState) {
|
||||
pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
|
||||
// If the focused widget is disabled, stashed or removed, we set
|
||||
// the focused id to None
|
||||
if let Some(id) = root.state.next_focused_widget {
|
||||
|
@ -282,9 +277,6 @@ pub(crate) fn run_update_focus_pass(root: &mut RenderRoot, root_state: &mut Widg
|
|||
|
||||
root.state.focused_widget = root.state.next_focused_widget;
|
||||
root.state.focused_path = next_focused_path;
|
||||
|
||||
// Merge root widget state with synthetic state created at beginning of pass
|
||||
root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
@ -492,7 +484,7 @@ pub(crate) fn run_update_anim_pass(root: &mut RenderRoot, elapsed_ns: u64) {
|
|||
// ----------------
|
||||
|
||||
// --- MARK: UPDATE TREE ---
|
||||
fn update_new_widgets(
|
||||
fn update_widget_tree(
|
||||
global_state: &mut RenderRootState,
|
||||
mut widget: ArenaMut<'_, Box<dyn Widget>>,
|
||||
mut state: ArenaMut<'_, WidgetState>,
|
||||
|
@ -570,13 +562,13 @@ fn update_new_widgets(
|
|||
widget.reborrow_mut(),
|
||||
state.children,
|
||||
|widget, mut state| {
|
||||
update_new_widgets(global_state, widget, state.reborrow_mut());
|
||||
update_widget_tree(global_state, widget, state.reborrow_mut());
|
||||
parent_state.merge_up(state.item);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn run_update_new_widgets_pass(root: &mut RenderRoot) {
|
||||
pub(crate) fn run_update_widget_tree_pass(root: &mut RenderRoot) {
|
||||
let _span = info_span!("update_new_widgets").entered();
|
||||
|
||||
if root.root.incomplete() {
|
||||
|
@ -590,7 +582,7 @@ pub(crate) fn run_update_new_widgets_pass(root: &mut RenderRoot) {
|
|||
}
|
||||
|
||||
let (root_widget, mut root_state) = root.widget_arena.get_pair_mut(root.root.id());
|
||||
update_new_widgets(&mut root.state, root_widget, root_state.reborrow_mut());
|
||||
update_widget_tree(&mut root.state, root_widget, root_state.reborrow_mut());
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
|
|
@ -27,16 +27,14 @@ use crate::passes::paint::root_paint;
|
|||
use crate::passes::recurse_on_children;
|
||||
use crate::passes::update::{
|
||||
run_update_anim_pass, run_update_disabled_pass, run_update_focus_chain_pass,
|
||||
run_update_focus_pass, run_update_new_widgets_pass, run_update_pointer_pass,
|
||||
run_update_scroll_pass, run_update_stashed_pass,
|
||||
run_update_focus_pass, run_update_pointer_pass, run_update_scroll_pass,
|
||||
run_update_stashed_pass, run_update_widget_tree_pass,
|
||||
};
|
||||
use crate::text::TextBrush;
|
||||
use crate::tree_arena::{ArenaMut, TreeArena};
|
||||
use crate::widget::WidgetArena;
|
||||
use crate::widget::{WidgetMut, WidgetRef, WidgetState};
|
||||
use crate::{
|
||||
AccessEvent, Action, BoxConstraints, CursorIcon, Handled, QueryCtx, Widget, WidgetId, WidgetPod,
|
||||
};
|
||||
use crate::{AccessEvent, Action, CursorIcon, Handled, QueryCtx, Widget, WidgetId, WidgetPod};
|
||||
|
||||
// --- MARK: STRUCTS ---
|
||||
|
||||
|
@ -98,6 +96,15 @@ pub struct RenderRootOptions {
|
|||
pub use_system_fonts: bool,
|
||||
pub size_policy: WindowSizePolicy,
|
||||
pub scale_factor: f64,
|
||||
|
||||
/// Add a font from its raw data for use in tests.
|
||||
/// The font is added to the fallback chain for Latin scripts.
|
||||
/// This is expected to be used with `use_system_fonts = false`
|
||||
/// to ensure rendering is consistent cross-platform.
|
||||
///
|
||||
/// We expect to develop a much more fully-featured font API in the future, but
|
||||
/// this is necessary for our testing of Masonry.
|
||||
pub test_font: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub enum RenderRootSignal {
|
||||
|
@ -120,6 +127,7 @@ impl RenderRoot {
|
|||
use_system_fonts,
|
||||
size_policy,
|
||||
scale_factor,
|
||||
test_font,
|
||||
}: RenderRootOptions,
|
||||
) -> Self {
|
||||
let mut root = RenderRoot {
|
||||
|
@ -159,21 +167,23 @@ impl RenderRoot {
|
|||
rebuild_access_tree: true,
|
||||
};
|
||||
|
||||
// We run a set of passes to initialize the widget tree
|
||||
run_update_new_widgets_pass(&mut root);
|
||||
// TODO - Remove this line
|
||||
let mut dummy_state = WidgetState::synthetic(root.root.id(), root.get_kurbo_size());
|
||||
root.post_event_processing(&mut dummy_state);
|
||||
|
||||
// We run a layout pass right away to have a SetSize signal ready
|
||||
if size_policy == WindowSizePolicy::Content {
|
||||
root.root_layout();
|
||||
if let Some(test_font_data) = test_font {
|
||||
let families = root.register_fonts(test_font_data);
|
||||
// Make sure that all of these fonts are in the fallback chain for the Latin script.
|
||||
// <https://en.wikipedia.org/wiki/Script_(Unicode)#Latn>
|
||||
root.state
|
||||
.font_context
|
||||
.collection
|
||||
.append_fallbacks(*b"Latn", families.iter().map(|(family, _)| *family));
|
||||
}
|
||||
|
||||
// We run a set of passes to initialize the widget tree
|
||||
root.run_rewrite_passes();
|
||||
|
||||
root
|
||||
}
|
||||
|
||||
fn root_state(&mut self) -> &mut WidgetState {
|
||||
pub(crate) fn root_state(&mut self) -> &mut WidgetState {
|
||||
self.widget_arena
|
||||
.widget_states
|
||||
.root_token_mut()
|
||||
|
@ -192,8 +202,9 @@ impl RenderRoot {
|
|||
}
|
||||
WindowEvent::Resize(size) => {
|
||||
self.size = size;
|
||||
self.root_state().request_layout = true;
|
||||
self.root_state().needs_layout = true;
|
||||
self.state.emit_signal(RenderRootSignal::RequestRedraw);
|
||||
self.run_rewrite_passes();
|
||||
Handled::Yes
|
||||
}
|
||||
WindowEvent::AnimFrame => {
|
||||
|
@ -205,7 +216,8 @@ impl RenderRoot {
|
|||
let last = self.last_anim.take();
|
||||
let elapsed_ns = last.map(|t| now.duration_since(t).as_nanos()).unwrap_or(0) as u64;
|
||||
|
||||
self.root_anim_frame(elapsed_ns);
|
||||
run_update_anim_pass(self, elapsed_ns);
|
||||
self.run_rewrite_passes();
|
||||
|
||||
// If this animation will continue, store the time.
|
||||
// If a new animation starts, then it will have zero reported elapsed time.
|
||||
|
@ -231,13 +243,6 @@ impl RenderRoot {
|
|||
self.root_on_text_event(event)
|
||||
}
|
||||
|
||||
pub(crate) fn root_anim_frame(&mut self, elapsed_ns: u64) {
|
||||
run_update_anim_pass(self, elapsed_ns);
|
||||
|
||||
let mut root_state = self.widget_arena.get_state_mut(self.root.id()).item.clone();
|
||||
self.post_event_processing(&mut root_state);
|
||||
}
|
||||
|
||||
/// Registers all fonts that exist in the given data.
|
||||
///
|
||||
/// Returns a list of pairs each containing the family identifier and fonts
|
||||
|
@ -249,31 +254,10 @@ impl RenderRoot {
|
|||
self.state.font_context.collection.register_fonts(data)
|
||||
}
|
||||
|
||||
/// Add a font from its raw data for use in tests.
|
||||
/// The font is added to the fallback chain for Latin scripts.
|
||||
/// This is expected to be used with
|
||||
/// [`RenderRootOptions.use_system_fonts = false`](RenderRootOptions::use_system_fonts)
|
||||
/// to ensure rendering is consistent cross-platform.
|
||||
///
|
||||
/// We expect to develop a much more fully-featured font API in the future, but
|
||||
/// this is necessary for our testing of Masonry.
|
||||
pub fn add_test_font(
|
||||
&mut self,
|
||||
data: Vec<u8>,
|
||||
) -> Vec<(fontique::FamilyId, Vec<fontique::FontInfo>)> {
|
||||
let families = self.register_fonts(data);
|
||||
// Make sure that all of these fonts are in the fallback chain for the Latin script.
|
||||
// <https://en.wikipedia.org/wiki/Script_(Unicode)#Latn>
|
||||
self.state
|
||||
.font_context
|
||||
.collection
|
||||
.append_fallbacks(*b"Latn", families.iter().map(|(family, _)| *family));
|
||||
families
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) -> (Scene, TreeUpdate) {
|
||||
if self.root_state().needs_layout {
|
||||
self.root_layout();
|
||||
// TODO - Rewrite more clearly after run_rewrite_passes is rewritten
|
||||
self.run_rewrite_passes();
|
||||
}
|
||||
if self.root_state().needs_layout {
|
||||
warn!("Widget requested layout during layout pass");
|
||||
|
@ -379,8 +363,7 @@ impl RenderRoot {
|
|||
f(widget_mut)
|
||||
});
|
||||
|
||||
let mut root_state = self.widget_arena.get_state_mut(self.root.id()).item.clone();
|
||||
self.post_event_processing(&mut root_state);
|
||||
self.run_rewrite_passes();
|
||||
|
||||
res
|
||||
}
|
||||
|
@ -395,20 +378,17 @@ impl RenderRoot {
|
|||
) -> R {
|
||||
let res = mutate_widget(self, id, f);
|
||||
|
||||
let mut root_state = self.widget_arena.get_state_mut(self.root.id()).item.clone();
|
||||
self.post_event_processing(&mut root_state);
|
||||
self.run_rewrite_passes();
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
// --- MARK: POINTER_EVENT ---
|
||||
fn root_on_pointer_event(&mut self, event: PointerEvent) -> Handled {
|
||||
let mut dummy_state = WidgetState::synthetic(self.root.id(), self.get_kurbo_size());
|
||||
let handled = root_on_pointer_event(self, &event);
|
||||
run_update_pointer_pass(self);
|
||||
|
||||
let handled = root_on_pointer_event(self, &mut dummy_state, &event);
|
||||
run_update_pointer_pass(self, &mut dummy_state);
|
||||
|
||||
self.post_event_processing(&mut dummy_state);
|
||||
self.run_rewrite_passes();
|
||||
self.get_root_widget().debug_validate(false);
|
||||
|
||||
handled
|
||||
|
@ -416,16 +396,14 @@ impl RenderRoot {
|
|||
|
||||
// --- MARK: TEXT_EVENT ---
|
||||
fn root_on_text_event(&mut self, event: TextEvent) -> Handled {
|
||||
let mut dummy_state = WidgetState::synthetic(self.root.id(), self.get_kurbo_size());
|
||||
|
||||
if matches!(event, TextEvent::FocusChange(false)) {
|
||||
root_on_pointer_event(self, &mut dummy_state, &PointerEvent::new_pointer_leave());
|
||||
root_on_pointer_event(self, &PointerEvent::new_pointer_leave());
|
||||
}
|
||||
|
||||
let handled = root_on_text_event(self, &mut dummy_state, &event);
|
||||
run_update_focus_pass(self, &mut dummy_state);
|
||||
let handled = root_on_text_event(self, &event);
|
||||
run_update_focus_pass(self);
|
||||
|
||||
self.post_event_processing(&mut dummy_state);
|
||||
self.run_rewrite_passes();
|
||||
self.get_root_widget().debug_validate(false);
|
||||
|
||||
handled
|
||||
|
@ -433,8 +411,6 @@ impl RenderRoot {
|
|||
|
||||
// --- MARK: ACCESS_EVENT ---
|
||||
pub fn root_on_access_event(&mut self, event: ActionRequest) {
|
||||
let mut dummy_state = WidgetState::synthetic(self.root.id(), self.get_kurbo_size());
|
||||
|
||||
let Ok(id) = event.target.0.try_into() else {
|
||||
warn!("Received ActionRequest with id 0. This shouldn't be possible.");
|
||||
return;
|
||||
|
@ -445,36 +421,12 @@ impl RenderRoot {
|
|||
data: event.data,
|
||||
};
|
||||
|
||||
root_on_access_event(self, &mut dummy_state, &event);
|
||||
root_on_access_event(self, &event);
|
||||
|
||||
self.post_event_processing(&mut dummy_state);
|
||||
self.run_rewrite_passes();
|
||||
self.get_root_widget().debug_validate(false);
|
||||
}
|
||||
|
||||
// --- MARK: LAYOUT ---
|
||||
pub(crate) fn root_layout(&mut self) {
|
||||
let window_size = self.get_kurbo_size();
|
||||
let bc = match self.size_policy {
|
||||
WindowSizePolicy::User => BoxConstraints::tight(window_size),
|
||||
WindowSizePolicy::Content => BoxConstraints::UNBOUNDED,
|
||||
};
|
||||
|
||||
let mut dummy_state = WidgetState::synthetic(self.root.id(), self.get_kurbo_size());
|
||||
let size = root_layout(self, &mut dummy_state, &bc);
|
||||
|
||||
if let WindowSizePolicy::Content = self.size_policy {
|
||||
let new_size = LogicalSize::new(size.width, size.height).to_physical(self.scale_factor);
|
||||
if self.size != new_size {
|
||||
self.size = new_size;
|
||||
self.state.emit_signal(RenderRootSignal::SetSize(new_size));
|
||||
}
|
||||
}
|
||||
|
||||
run_update_pointer_pass(self, &mut dummy_state);
|
||||
|
||||
self.post_event_processing(&mut dummy_state);
|
||||
}
|
||||
|
||||
// --- MARK: PAINT ---
|
||||
fn root_paint(&mut self) -> Scene {
|
||||
root_paint(self)
|
||||
|
@ -501,44 +453,33 @@ impl RenderRoot {
|
|||
kurbo::Size::new(size.width, size.height)
|
||||
}
|
||||
|
||||
// --- MARK: POST-EVENT ---
|
||||
fn post_event_processing(&mut self, widget_state: &mut WidgetState) {
|
||||
// If children are changed during the handling of an event,
|
||||
// we need to send RouteWidgetAdded now, so that they are ready for update/layout.
|
||||
if widget_state.children_changed {
|
||||
run_update_new_widgets_pass(self);
|
||||
}
|
||||
// --- MARK: REWRITE PASSES ---
|
||||
/// Run all rewrite passes on widget tree.
|
||||
///
|
||||
/// Rewrite passes are passes which occur after external events, and
|
||||
/// update flags and internal values to a consistent state.
|
||||
///
|
||||
/// See Pass Spec RFC for details. (TODO - Link to doc instead.)
|
||||
pub(crate) fn run_rewrite_passes(&mut self) {
|
||||
// TODO - Rerun passes if invalidation flags are still set
|
||||
|
||||
if self.state.debug_logger.layout_tree.root.is_none() {
|
||||
self.state.debug_logger.layout_tree.root = Some(self.root.id().to_raw() as u32);
|
||||
}
|
||||
// Note: this code doesn't do any short-circuiting, because each pass is
|
||||
// expected to have its own early exits.
|
||||
// Calling a run_xxx_pass (or root_xxx) should always be very fast if
|
||||
// the pass doesn't need to do anything.
|
||||
|
||||
if !self.state.scroll_request_targets.is_empty() {
|
||||
run_update_scroll_pass(self);
|
||||
}
|
||||
run_mutate_pass(self);
|
||||
run_update_widget_tree_pass(self);
|
||||
run_update_disabled_pass(self);
|
||||
run_update_stashed_pass(self);
|
||||
run_update_focus_chain_pass(self);
|
||||
run_update_focus_pass(self);
|
||||
root_layout(self);
|
||||
run_update_scroll_pass(self);
|
||||
root_compose(self);
|
||||
run_update_pointer_pass(self);
|
||||
|
||||
if self.root_state().needs_compose && !self.root_state().needs_layout {
|
||||
root_compose(self, widget_state);
|
||||
}
|
||||
|
||||
// Update the disabled and stashed state if necessary
|
||||
// Always do this before updating the focus-chain
|
||||
if self.root_state().needs_update_disabled {
|
||||
run_update_disabled_pass(self);
|
||||
}
|
||||
if self.root_state().needs_update_stashed {
|
||||
run_update_stashed_pass(self);
|
||||
}
|
||||
|
||||
// Update the focus-chain if necessary
|
||||
// Always do this before sending focus change, since this event updates the focus chain.
|
||||
if self.root_state().update_focus_chain {
|
||||
run_update_focus_chain_pass(self);
|
||||
}
|
||||
|
||||
run_update_focus_pass(self, widget_state);
|
||||
|
||||
if self.root_state().request_anim {
|
||||
if self.root_state().needs_anim {
|
||||
self.state.emit_signal(RenderRootSignal::RequestAnimFrame);
|
||||
}
|
||||
|
||||
|
@ -552,8 +493,6 @@ impl RenderRoot {
|
|||
{
|
||||
self.state.emit_signal(RenderRootSignal::RequestRedraw);
|
||||
}
|
||||
|
||||
run_mutate_pass(self, widget_state);
|
||||
}
|
||||
|
||||
pub(crate) fn request_render_all(&mut self) {
|
||||
|
@ -579,6 +518,7 @@ impl RenderRoot {
|
|||
|
||||
let (root_widget, mut root_state) = self.widget_arena.get_pair_mut(self.root.id());
|
||||
request_render_all_in(root_widget, root_state.reborrow_mut());
|
||||
self.state.emit_signal(RenderRootSignal::RequestRedraw);
|
||||
}
|
||||
|
||||
// Checks whether the given id points to a widget that is "interactive".
|
||||
|
|
|
@ -18,6 +18,7 @@ use winit::event::Ime;
|
|||
use crate::action::Action;
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
use crate::event::{PointerButton, PointerEvent, PointerState, TextEvent, WindowEvent};
|
||||
use crate::passes::update::run_update_anim_pass;
|
||||
use crate::render_root::{RenderRoot, RenderRootOptions, RenderRootSignal, WindowSizePolicy};
|
||||
use crate::testing::{screenshots::get_image_diff, snapshot_utils::get_cargo_workspace};
|
||||
use crate::tracing_backend::try_init_test_tracing;
|
||||
|
@ -66,15 +67,9 @@ pub const HARNESS_DEFAULT_BACKGROUND_COLOR: Color = Color::rgb8(0x29, 0x29, 0x29
|
|||
///
|
||||
/// **(TODO - Painting invalidation might not be accurate.)**
|
||||
///
|
||||
/// One minor difference is that layout is always calculated after every event, whereas
|
||||
/// in normal execution it is only calculated before paint. This might be create subtle
|
||||
/// differences in cases where timers are programmed to fire at the same time: in normal
|
||||
/// execution, they'll execute back-to-back; in the harness, they'll be separated with
|
||||
/// layout calls.
|
||||
///
|
||||
/// Also, paint only happens when the user explicitly calls rendering methods, whereas in
|
||||
/// a normal applications you could reasonably expect multiple paint calls between eg any
|
||||
/// two clicks.
|
||||
/// One minor difference is that paint only happens when the user explicitly calls rendering
|
||||
/// methods, whereas in a normal applications you could reasonably expect multiple paint calls
|
||||
/// between eg any two clicks.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
|
@ -179,6 +174,12 @@ impl TestHarness {
|
|||
// harnesses.
|
||||
let _ = try_init_test_tracing();
|
||||
|
||||
const ROBOTO: &[u8] = include_bytes!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/resources/fonts/roboto/Roboto-Regular.ttf"
|
||||
));
|
||||
let data = ROBOTO.to_vec();
|
||||
|
||||
let mut harness = TestHarness {
|
||||
render_root: RenderRoot::new(
|
||||
root_widget,
|
||||
|
@ -186,18 +187,13 @@ impl TestHarness {
|
|||
use_system_fonts: false,
|
||||
size_policy: WindowSizePolicy::User,
|
||||
scale_factor: 1.0,
|
||||
test_font: Some(data),
|
||||
},
|
||||
),
|
||||
mouse_state,
|
||||
window_size,
|
||||
background_color,
|
||||
};
|
||||
const ROBOTO: &[u8] = include_bytes!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/resources/fonts/roboto/Roboto-Regular.ttf"
|
||||
));
|
||||
let data = ROBOTO.to_vec();
|
||||
harness.render_root.add_test_font(data);
|
||||
harness.process_window_event(WindowEvent::Resize(window_size));
|
||||
|
||||
harness
|
||||
|
@ -212,9 +208,7 @@ impl TestHarness {
|
|||
/// as will any resulting commands. Commands created as a result of this event
|
||||
/// will also be dispatched.
|
||||
pub fn process_window_event(&mut self, event: WindowEvent) -> Handled {
|
||||
let handled = self.render_root.handle_window_event(event);
|
||||
self.process_state_after_event();
|
||||
handled
|
||||
self.render_root.handle_window_event(event)
|
||||
}
|
||||
|
||||
/// Send an event to the widget.
|
||||
|
@ -223,9 +217,7 @@ impl TestHarness {
|
|||
/// as will any resulting commands. Commands created as a result of this event
|
||||
/// will also be dispatched.
|
||||
pub fn process_pointer_event(&mut self, event: PointerEvent) -> Handled {
|
||||
let handled = self.render_root.handle_pointer_event(event);
|
||||
self.process_state_after_event();
|
||||
handled
|
||||
self.render_root.handle_pointer_event(event)
|
||||
}
|
||||
|
||||
/// Send an event to the widget.
|
||||
|
@ -234,15 +226,7 @@ impl TestHarness {
|
|||
/// as will any resulting commands. Commands created as a result of this event
|
||||
/// will also be dispatched.
|
||||
pub fn process_text_event(&mut self, event: TextEvent) -> Handled {
|
||||
let handled = self.render_root.handle_text_event(event);
|
||||
self.process_state_after_event();
|
||||
handled
|
||||
}
|
||||
|
||||
fn process_state_after_event(&mut self) {
|
||||
if self.root_widget().ctx.widget_state.needs_layout {
|
||||
self.render_root.root_layout();
|
||||
}
|
||||
self.render_root.handle_text_event(event)
|
||||
}
|
||||
|
||||
// --- MARK: RENDER ---
|
||||
|
@ -415,24 +399,18 @@ impl TestHarness {
|
|||
let event = TextEvent::Ime(Ime::Commit(c.to_string()));
|
||||
self.render_root.handle_text_event(event);
|
||||
}
|
||||
self.process_state_after_event();
|
||||
}
|
||||
|
||||
pub fn focus_on(&mut self, id: Option<WidgetId>) {
|
||||
self.render_root.state.next_focused_widget = id;
|
||||
// FIXME - Change this once run_rewrite_passes is merged
|
||||
let mut dummy_state = crate::WidgetState::synthetic(
|
||||
self.render_root.root.id(),
|
||||
self.render_root.get_kurbo_size(),
|
||||
);
|
||||
crate::passes::update::run_update_focus_pass(&mut self.render_root, &mut dummy_state);
|
||||
self.render_root.run_rewrite_passes();
|
||||
}
|
||||
|
||||
// TODO - Fold into move_timers_forward
|
||||
/// Send animation events to the widget tree
|
||||
pub fn animate_ms(&mut self, ms: u64) {
|
||||
self.render_root.root_anim_frame(ms * 1_000_000);
|
||||
self.process_state_after_event();
|
||||
run_update_anim_pass(&mut self.render_root, ms * 1_000_000);
|
||||
self.render_root.run_rewrite_passes();
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
|
@ -521,9 +499,7 @@ impl TestHarness {
|
|||
&mut self,
|
||||
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
|
||||
) -> R {
|
||||
let res = self.render_root.edit_root_widget(f);
|
||||
self.process_state_after_event();
|
||||
res
|
||||
self.render_root.edit_root_widget(f)
|
||||
}
|
||||
|
||||
/// Get a [`WidgetMut`] to a specific widget.
|
||||
|
@ -534,9 +510,7 @@ impl TestHarness {
|
|||
id: WidgetId,
|
||||
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
|
||||
) -> R {
|
||||
let res = self.render_root.edit_widget(id, f);
|
||||
self.process_state_after_event();
|
||||
res
|
||||
self.render_root.edit_widget(id, f)
|
||||
}
|
||||
|
||||
/// Pop next action from the queue
|
||||
|
|
|
@ -11,6 +11,10 @@ expression: record
|
|||
L(
|
||||
BuildFocusChain,
|
||||
),
|
||||
Layout(
|
||||
0.0W×0.0H,
|
||||
),
|
||||
Compose,
|
||||
Layout(
|
||||
400.0W×400.0H,
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue