-
Notifications
You must be signed in to change notification settings - Fork 190
Implement compose pass #512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9e5354d
0bb2f28
4bd8453
edccd94
13aab24
136e54a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ use std::time::Duration; | |
| use accesskit::{NodeBuilder, TreeUpdate}; | ||
| use parley::{FontContext, LayoutContext}; | ||
| use tracing::{trace, warn}; | ||
| use vello::kurbo::Vec2; | ||
|
|
||
| use crate::action::Action; | ||
| use crate::render_root::{MutateCallback, RenderRootSignal, RenderRootState}; | ||
|
|
@@ -84,6 +85,13 @@ pub struct LayoutCtx<'a> { | |
| pub(crate) mouse_pos: Option<Point>, | ||
| } | ||
|
|
||
| pub struct ComposeCtx<'a> { | ||
| pub(crate) global_state: &'a mut RenderRootState, | ||
| pub(crate) widget_state: &'a mut WidgetState, | ||
| pub(crate) widget_state_children: ArenaMutChildren<'a, WidgetState>, | ||
| pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>, | ||
| } | ||
|
|
||
| /// A context passed to paint methods of widgets. | ||
| pub struct PaintCtx<'a> { | ||
| pub(crate) global_state: &'a mut RenderRootState, | ||
|
|
@@ -114,6 +122,7 @@ impl_context_method!( | |
| EventCtx<'_>, | ||
| LifeCycleCtx<'_>, | ||
| LayoutCtx<'_>, | ||
| ComposeCtx<'_>, | ||
| PaintCtx<'_>, | ||
| AccessCtx<'_>, | ||
| { | ||
|
|
@@ -187,6 +196,7 @@ impl_context_method!( | |
| MutateCtx<'_>, | ||
| EventCtx<'_>, | ||
| LifeCycleCtx<'_>, | ||
| ComposeCtx<'_>, | ||
| PaintCtx<'_>, | ||
| AccessCtx<'_>, | ||
| { | ||
|
|
@@ -229,6 +239,7 @@ impl_context_method!( | |
| MutateCtx<'_>, | ||
| EventCtx<'_>, | ||
| LifeCycleCtx<'_>, | ||
| ComposeCtx<'_>, | ||
| PaintCtx<'_>, | ||
| AccessCtx<'_>, | ||
| { | ||
|
|
@@ -412,6 +423,17 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, LifeCycleCtx<'_>, { | |
| self.widget_state.needs_layout = true; | ||
| } | ||
|
|
||
| // TODO - Document better | ||
| /// Request a [`compose`] pass. | ||
| /// | ||
| /// The compose pass is often cheaper than the layout pass, because it can only transform individual widgets' position. | ||
| /// [`compose`]: crate::Widget::compose | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no thing above this that this would be providing a link target for.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm the Link seems still not to be working using |
||
| pub fn request_compose(&mut self) { | ||
| trace!("request_compose"); | ||
| self.widget_state.needs_compose = true; | ||
| self.widget_state.request_compose = true; | ||
| } | ||
|
|
||
| pub fn request_accessibility_update(&mut self) { | ||
| trace!("request_accessibility_update"); | ||
| self.widget_state.needs_accessibility_update = true; | ||
|
|
@@ -494,6 +516,7 @@ impl_context_method!( | |
| EventCtx<'_>, | ||
| LifeCycleCtx<'_>, | ||
| LayoutCtx<'_>, | ||
| ComposeCtx<'_>, | ||
| { | ||
| // TODO - Remove from MutateCtx? | ||
| /// Queue a callback that will be called with a [`WidgetMut`] for this widget. | ||
|
|
@@ -847,7 +870,7 @@ impl LayoutCtx<'_> { | |
| self.assert_layout_done(child, "place_child"); | ||
| if origin != self.get_child_state_mut(child).origin { | ||
| self.get_child_state_mut(child).origin = origin; | ||
| self.get_child_state_mut(child).needs_window_origin = true; | ||
| self.get_child_state_mut(child).translation_changed = true; | ||
Philipp-M marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| self.get_child_state_mut(child) | ||
| .is_expecting_place_child_call = false; | ||
|
|
@@ -859,6 +882,22 @@ impl LayoutCtx<'_> { | |
| } | ||
| } | ||
|
|
||
| impl ComposeCtx<'_> { | ||
| pub fn needs_compose(&self) -> bool { | ||
| self.widget_state.needs_compose | ||
| } | ||
|
|
||
| /// Set a translation for the child widget. | ||
| /// | ||
| /// The translation is applied on top of the position from [`LayoutCtx::place_child`]. | ||
| pub fn set_child_translation(&mut self, translation: Vec2) { | ||
Philipp-M marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if self.widget_state.translation != translation { | ||
waywardmonkeys marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.widget_state.translation = translation; | ||
| self.widget_state.translation_changed = true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // --- MARK: OTHER STUFF --- | ||
| impl_context_method!(LayoutCtx<'_>, PaintCtx<'_>, { | ||
| /// Get the contexts needed to build and paint text sections. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| // Copyright 2024 the Xilem Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use tracing::info_span; | ||
| use vello::kurbo::Vec2; | ||
|
|
||
| use crate::render_root::{RenderRoot, RenderRootState}; | ||
| use crate::tree_arena::{ArenaMut, ArenaMutChildren}; | ||
| use crate::{ComposeCtx, Widget, WidgetId, WidgetState}; | ||
|
|
||
| fn recurse_on_children( | ||
| id: WidgetId, | ||
| mut widget: ArenaMut<'_, Box<dyn Widget>>, | ||
| mut state: ArenaMutChildren<'_, WidgetState>, | ||
| mut callback: impl FnMut(ArenaMut<'_, Box<dyn Widget>>, ArenaMut<'_, WidgetState>), | ||
| ) { | ||
| let parent_name = widget.item.short_type_name(); | ||
| let parent_id = id; | ||
|
|
||
| for child_id in widget.item.children_ids() { | ||
| let widget = widget | ||
| .children | ||
| .get_child_mut(child_id.to_raw()) | ||
| .unwrap_or_else(|| { | ||
| panic!( | ||
| "Error in '{}' #{}: cannot find child #{} returned by children_ids()", | ||
| parent_name, | ||
| parent_id.to_raw(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, but that's a future PR. |
||
| child_id.to_raw() | ||
| ) | ||
| }); | ||
| let state = state.get_child_mut(child_id.to_raw()).unwrap_or_else(|| { | ||
| panic!( | ||
| "Error in '{}' #{}: cannot find child #{} returned by children_ids()", | ||
| parent_name, | ||
| parent_id.to_raw(), | ||
| child_id.to_raw() | ||
| ) | ||
| }); | ||
|
|
||
| callback(widget, state); | ||
| } | ||
| } | ||
|
|
||
| fn compose_widget( | ||
| global_state: &mut RenderRootState, | ||
| mut widget: ArenaMut<'_, Box<dyn Widget>>, | ||
| mut state: ArenaMut<'_, WidgetState>, | ||
| parent_moved: bool, | ||
| parent_translation: Vec2, | ||
| ) { | ||
| 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 mut ctx = ComposeCtx { | ||
| global_state, | ||
| widget_state: state.item, | ||
| widget_state_children: state.children.reborrow_mut(), | ||
| widget_children: widget.children.reborrow_mut(), | ||
| }; | ||
| if ctx.widget_state.request_compose { | ||
| widget.item.compose(&mut ctx); | ||
| } | ||
|
|
||
| state.item.needs_compose = false; | ||
| state.item.request_compose = false; | ||
| state.item.translation_changed = false; | ||
|
|
||
| let id = state.item.id; | ||
| let parent_state = state.item; | ||
| recurse_on_children( | ||
| id, | ||
| widget.reborrow_mut(), | ||
| state.children, | ||
| |widget, mut state| { | ||
| if !moved && !state.item.translation_changed && !state.item.needs_compose { | ||
| return; | ||
| } | ||
| compose_widget( | ||
| global_state, | ||
| widget, | ||
| state.reborrow_mut(), | ||
| moved, | ||
| translation, | ||
| ); | ||
| parent_state.merge_up(state.item); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| // ---------------- | ||
|
|
||
| pub fn root_compose(root: &mut RenderRoot, global_root_state: &mut WidgetState) { | ||
| 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); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.