Implemented widget access from UiNode and UiNodeList.

This commit is contained in:
Samuel Guerra 2022-05-27 00:41:32 -03:00
parent 21b4000828
commit 37848fc178
12 changed files with 1262 additions and 169 deletions

View File

@ -4,4 +4,5 @@
* Review layout double-pass of stacks.
* Fix text final size, either clip or return accurate size.
* Get Widget from UiNode (at least to avoid having to do custom reference frames in fill_node).
* Test scroll to end when the height changes by scrolling.

View File

@ -13,7 +13,7 @@ pub fn first_and_last_window_events() {
let root_id = WidgetId::new_unique();
let stack_id = WidgetId::new_unique();
let button_0_id = buttons.widget_id(0);
let button_0_id = buttons.item_id(0);
let mut app = TestApp::new_w(window! {
content = v_stack!(id = stack_id; items = buttons);
@ -87,7 +87,7 @@ pub fn window_tab_cycle_index_auto() {
button! { content = text("Button 2"); tab_index = tab_ids[2] },
];
// we collect the widget_id values in the TAB navigation order.
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
let mut ids: Vec<_> = (0..3).map(|i| (buttons.item_id(i), tab_ids[i])).collect();
ids.sort_by_key(|(_, ti)| *ti);
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
@ -132,7 +132,7 @@ pub fn window_tab_cycle_and_alt_scope() {
button! { content = text("Button 0"); tab_index = tab_ids[0] },
button! { content = text("Button 1"); tab_index = tab_ids[1] },
];
let mut ids: Vec<_> = (0..2).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
let mut ids: Vec<_> = (0..2).map(|i| (buttons.item_id(i), tab_ids[i])).collect();
ids.sort_by_key(|(_, ti)| *ti);
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
@ -141,7 +141,7 @@ pub fn window_tab_cycle_and_alt_scope() {
button! { content = text("Alt 1"); tab_index = tab_ids[3] },
button! { content = text("Alt 2"); tab_index = tab_ids[4] },
];
let mut alt_ids: Vec<_> = (0..3).map(|i| (alt_buttons.widget_id(i), tab_ids[i + 2])).collect();
let mut alt_ids: Vec<_> = (0..3).map(|i| (alt_buttons.item_id(i), tab_ids[i + 2])).collect();
alt_ids.sort_by_key(|(_, ti)| *ti);
let alt_ids: Vec<_> = alt_ids.into_iter().map(|(id, _)| id).collect();
@ -232,7 +232,7 @@ fn window_tab_contained_and_continue(tab_nav: TabNav) {
button! { content = text("Button 2"); tab_index = tab_ids[2] },
];
// we collect the widget_id values in the TAB navigation order.
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
let mut ids: Vec<_> = (0..3).map(|i| (buttons.item_id(i), tab_ids[i])).collect();
ids.sort_by_key(|(_, ti)| *ti);
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
@ -285,7 +285,7 @@ fn window_tab_once_and_none(tab_nav: TabNav) {
button! { content = text("Button 2"); tab_index = tab_ids[2] },
];
// we collect the widget_id values in the TAB navigation order.
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
let mut ids: Vec<_> = (0..3).map(|i| (buttons.item_id(i), tab_ids[i])).collect();
ids.sort_by_key(|(_, ti)| *ti);
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
@ -328,14 +328,14 @@ fn two_continue_scopes_or_containers_in_tab_cycle_window(focus_scope: bool) {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.item_id(i)).collect();
let buttons_b = widgets![
button! { content = text("Button 0") },
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.item_id(i)).collect();
let a = v_stack! {
items = buttons_a;
@ -409,14 +409,14 @@ pub fn two_continue_scopes_with_mixed_indexes() {
button! { content = text("Button 2"); tab_index = 5; },
button! { content = text("Button 1"); tab_index = 3; },
];
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.item_id(i)).collect();
let buttons_b = widgets![
button! { content = text("Button 3"); tab_index = 2; },
button! { content = text("Button 4"); tab_index = 4; },
button! { content = text("Button 5"); tab_index = 6; },
];
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.item_id(i)).collect();
let a = v_stack! {
items = buttons_a;
@ -481,14 +481,14 @@ pub fn two_containers_with_mixed_indexes() {
button! { content = text("Button 2"); tab_index = 5; },
button! { content = text("Button 1"); tab_index = 3; },
];
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.item_id(i)).collect();
let buttons_b = widgets![
button! { content = text("Button 3"); tab_index = 2; },
button! { content = text("Button 4"); tab_index = 4; },
button! { content = text("Button 5"); tab_index = 6; },
];
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.item_id(i)).collect();
let a = v_stack(buttons_a);
let b = v_stack(buttons_b);
@ -539,7 +539,7 @@ pub fn tab_index_skip() {
button! { content = text("Button 1"); tab_index = TabIndex::SKIP; },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -562,13 +562,13 @@ pub fn tab_inner_container() {
// sanity check for `tab_skip_inner_container`.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack(inner_buttons),
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -595,7 +595,7 @@ pub fn tab_skip_inner_container() {
// directly.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -604,7 +604,7 @@ pub fn tab_skip_inner_container() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -643,7 +643,7 @@ pub fn tab_inner_scope_continue() {
// sanity check for `tab_skip_inner_scope_continue`.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -653,7 +653,7 @@ pub fn tab_inner_scope_continue() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -680,7 +680,7 @@ pub fn tab_skip_inner_scope_continue() {
// directly.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -691,7 +691,7 @@ pub fn tab_skip_inner_scope_continue() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -730,7 +730,7 @@ pub fn tab_inner_scope_cycle() {
// we expect tab navigation to enter the inner scope and get trapped in there.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -740,7 +740,7 @@ pub fn tab_inner_scope_cycle() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -772,7 +772,7 @@ pub fn tab_inner_scope_contained() {
// we expect tab navigation to enter the inner scope and get trapped in there.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -782,7 +782,7 @@ pub fn tab_inner_scope_contained() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -814,7 +814,7 @@ pub fn tab_inner_scope_once() {
// we expect tab navigation to enter the inner scope but then leave it.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -824,7 +824,7 @@ pub fn tab_inner_scope_once() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -850,7 +850,7 @@ pub fn tab_inner_scope_none() {
// we expect tab navigation to enter the inner scope and then not move.
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.item_id(i)).collect();
let items = widgets![
button! { content = text("Button 0") },
v_stack! {
@ -860,7 +860,7 @@ pub fn tab_inner_scope_none() {
},
button! { content = text("Button 3") },
];
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
let item_ids: Vec<_> = (0..3).map(|i| items.item_id(i)).collect();
let mut app = TestApp::new(v_stack(items));
@ -996,7 +996,7 @@ fn focused_removed_test(button1: impl Widget, set_var: impl FnOnce(&Vars)) {
button1,
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -1212,7 +1212,7 @@ pub fn directional_focus_up() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -1233,7 +1233,7 @@ pub fn directional_focus_down() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -1253,7 +1253,7 @@ pub fn directional_focus_left() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(h_stack(buttons));
@ -1274,7 +1274,7 @@ pub fn directional_focus_right() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(h_stack(buttons));
@ -1294,7 +1294,7 @@ pub fn directional_cycle_vertical() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new_w(window! {
directional_nav = DirectionalNav::Cycle;
@ -1316,7 +1316,7 @@ pub fn directional_cycle_horizontal() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new_w(window! {
directional_nav = DirectionalNav::Cycle;
@ -1338,7 +1338,7 @@ pub fn directional_contained_vertical() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new_w(window! {
directional_nav = DirectionalNav::Contained;
@ -1360,7 +1360,7 @@ pub fn directional_contained_horizontal() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new_w(window! {
directional_nav = DirectionalNav::Contained;
@ -1383,7 +1383,7 @@ pub fn directional_none() {
button! { content = text("Button 1") },
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new_w(window! {
directional_nav = DirectionalNav::None;
@ -1417,7 +1417,7 @@ pub fn directional_continue_up() {
},
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -1442,7 +1442,7 @@ pub fn directional_continue_down() {
},
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(v_stack(buttons));
@ -1467,7 +1467,7 @@ pub fn directional_continue_left() {
},
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(h_stack(buttons));
@ -1492,7 +1492,7 @@ pub fn directional_continue_right() {
},
button! { content = text("Button 2") },
];
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
let ids: Vec<_> = (0..3).map(|i| buttons.item_id(i)).collect();
let mut app = TestApp::new(h_stack(buttons));

View File

@ -118,6 +118,54 @@ pub trait UiNodeList: 'static {
/// Calls [`UiNode::render_update`] in only the `index` node or widget.
fn item_render_update(&self, index: usize, ctx: &mut RenderContext, update: &mut FrameUpdate);
/// Gets the id of the widget at the `index` if the node is a full widget.
///
/// The index is zero-based.
fn try_item_id(&self, index: usize) -> Option<WidgetId>;
/// Reference the state of the widget at the `index`.
fn try_item_state(&self, index: usize) -> Option<&StateMap>;
/// Exclusive reference the state of the widget at the `index`.
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap>;
/// Gets the bounds layout info of the node at the `index` if it is a full widget.
///
/// See [`Widget::bounds_info`] for more details.
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo>;
/// Gets the border and corners info from the node at the `index` if it is a full widget.
///
/// See [`Widget::border_info`] for more details.
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo>;
/// Gets the render info from the node at the `index` if it is a full widget.
///
/// See [`Widget::render_info`] for more details.
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo>;
/// Calls [`UiNode::render`] in all nodes allowed by a `filter`, skips rendering the rest.
fn render_node_filtered<F>(&self, filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(UiNodeFilterArgs) -> bool;
/// Calls [`WidgetLayout::try_with_outer`] in only the `index` node. The `transform` closure only runs if the node
/// is a full widget.
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R;
/// Calls [`WidgetLayout::try_with_outer`] in all nodes on the list. The `transform` closures only runs for the nodes
/// that are full widgets.
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs);
/// Count nodes that pass the `filter`.
fn count_nodes<F>(&self, filter: F) -> usize
where
F: FnMut(UiNodeFilterArgs) -> bool;
}
/// Arguments for the closure in [`UiNodeList::layout_all`] that runs before each child is layout.
@ -127,7 +175,7 @@ pub struct PreLayoutArgs<'a> {
/// Mutable reference to the widget state.
///
/// Is `None` in lists that only implement [`UiNodeList`].
/// Can be `None` in [`UiNodeList`] for nodes that are not full widgets.
pub state: Option<&'a mut StateMap>,
/// Constrains overwrite just for this child.
@ -151,10 +199,10 @@ pub struct PosLayoutArgs<'a> {
/// Mutable reference to the widget state.
///
/// Is `None` in lists that only implement [`UiNodeList`].
/// Can be `None` in [`UiNodeList`] for nodes that are not full widgets.
pub state: Option<&'a mut StateMap>,
/// The widget outer size.
/// The updated size.
pub size: PxSize,
}
impl<'a> PosLayoutArgs<'a> {
@ -196,18 +244,20 @@ fn default_ui_node_list_layout_all<N, C, D>(
D: FnMut(&mut LayoutContext, &mut WidgetLayout, PosLayoutArgs),
{
let (size, _) = wl.with_child(ctx, |ctx, wl| {
let mut args = PreLayoutArgs::new(index, None);
let mut args = PreLayoutArgs::new(index, node.try_state_mut());
pre_layout(ctx, wl, &mut args);
ctx.with_constrains(|c| args.constrains.take().unwrap_or(c), |ctx| node.layout(ctx, wl))
});
pos_layout(ctx, wl, PosLayoutArgs::new(index, None, size));
}
/// All [`Widget`] accessible *info*.
/// All [`Widget`] accessible info.
pub struct WidgetFilterArgs<'a> {
/// The widget index in the list.
pub index: usize,
/// The [`Widget::id`].
pub id: WidgetId,
/// The [`Widget::bounds_info`].
pub bounds_info: &'a WidgetBoundsInfo,
/// The [`Widget::border_info`].
@ -222,10 +272,11 @@ impl<'a> WidgetFilterArgs<'a> {
pub fn get(list: &'a impl WidgetList, index: usize) -> Self {
WidgetFilterArgs {
index,
bounds_info: list.widget_bounds_info(index),
border_info: list.widget_border_info(index),
render_info: list.widget_render_info(index),
state: list.widget_state(index),
id: list.item_id(index),
bounds_info: list.item_bounds_info(index),
border_info: list.item_border_info(index),
render_info: list.item_render_info(index),
state: list.item_state(index),
}
}
@ -233,6 +284,7 @@ impl<'a> WidgetFilterArgs<'a> {
pub fn new(index: usize, widget: &'a impl Widget) -> Self {
WidgetFilterArgs {
index,
id: widget.id(),
bounds_info: widget.bounds_info(),
border_info: widget.border_info(),
render_info: widget.render_info(),
@ -241,11 +293,53 @@ impl<'a> WidgetFilterArgs<'a> {
}
}
/// All [`UiNode`] accessible widget info.
pub struct UiNodeFilterArgs<'a> {
/// The node index in the list.
pub index: usize,
/// The [`UiNode::try_id`].
pub id: Option<WidgetId>,
/// The [`UiNode::try_bounds_info`].
pub bounds_info: Option<&'a WidgetBoundsInfo>,
/// The [`UiNode::try_border_info`].
pub border_info: Option<&'a WidgetBorderInfo>,
/// The [`UiNode::try_render_info`].
pub render_info: Option<&'a WidgetRenderInfo>,
/// The [`UiNode::try_state`].
pub state: Option<&'a StateMap>,
}
impl<'a> UiNodeFilterArgs<'a> {
/// Copy or borrow all info from a node list and index.
pub fn get(list: &'a impl UiNodeList, index: usize) -> Self {
UiNodeFilterArgs {
index,
id: list.try_item_id(index),
bounds_info: list.try_item_bounds_info(index),
border_info: list.try_item_border_info(index),
render_info: list.try_item_render_info(index),
state: list.try_item_state(index),
}
}
/// Copy or borrow all info from a node reference.
pub fn new(index: usize, node: &'a impl UiNode) -> Self {
UiNodeFilterArgs {
index,
id: node.try_id(),
bounds_info: node.try_bounds_info(),
border_info: node.try_border_info(),
render_info: node.try_render_info(),
state: node.try_state(),
}
}
}
/// A generic view over a list of [`Widget`] UI nodes.
///
/// Layout widgets should use this to abstract the children list type if possible.
pub trait WidgetList: UiNodeList {
/// Count widgets that pass filter using the widget state.
/// Count widgets that pass the `filter`.
fn count<F>(&self, filter: F) -> usize
where
F: FnMut(WidgetFilterArgs) -> bool;
@ -265,28 +359,28 @@ pub trait WidgetList: UiNodeList {
/// Gets the id of the widget at the `index`.
///
/// The index is zero-based.
fn widget_id(&self, index: usize) -> WidgetId;
fn item_id(&self, index: usize) -> WidgetId;
/// Reference the state of the widget at the `index`.
fn widget_state(&self, index: usize) -> &StateMap;
fn item_state(&self, index: usize) -> &StateMap;
/// Exclusive reference the state of the widget at the `index`.
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap;
fn item_state_mut(&mut self, index: usize) -> &mut StateMap;
/// Gets the bounds layout info of the widget at the `index`.
///
/// See [`Widget::bounds_info`] for more details.
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo;
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo;
/// Gets the border and corners info of the widget at the `index`.
///
/// See [`Widget::border_info`] for more details.
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo;
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo;
/// Gets the render info the widget at the `index`.
///
/// See [`Widget::render_info`] for more details.
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo;
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo;
/// Calls [`UiNode::render`] in all widgets allowed by a `filter`, skips rendering the rest.
fn render_filtered<F>(&self, filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
@ -294,9 +388,9 @@ pub trait WidgetList: UiNodeList {
F: FnMut(WidgetFilterArgs) -> bool;
/// Calls [`WidgetLayout::with_outer`] in only the `index` widget.
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs);
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R;
/// Calls [`WidgetLayout::with_outer`] in all widgets on the list.
fn outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, transform: F)

View File

@ -142,6 +142,115 @@ impl<A: WidgetList, B: WidgetList> UiNodeList for WidgetListChain<A, B> {
self.1.item_render_update(index - a_len, ctx, update)
}
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_id(index)
} else {
self.1.try_item_id(index - a_len)
}
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_state(index)
} else {
self.1.try_item_state(index - a_len)
}
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_state_mut(index)
} else {
self.1.try_item_state_mut(index - a_len)
}
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_bounds_info(index)
} else {
self.1.try_item_bounds_info(index - a_len)
}
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_border_info(index)
} else {
self.1.try_item_border_info(index - a_len)
}
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_render_info(index)
} else {
self.1.try_item_render_info(index - a_len)
}
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
self.0.render_node_filtered(&mut filter, ctx, frame);
let offset = self.0.len();
self.1.render_node_filtered(
|mut a| {
a.index += offset;
filter(a)
},
ctx,
frame,
);
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let a_len = self.0.len();
if index < a_len {
self.0.try_item_outer(index, wl, keep_previous, transform)
} else {
self.1.try_item_outer(index - a_len, wl, keep_previous, transform)
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
self.0.try_outer_all(wl, keep_previous, &mut transform);
let offset = self.0.len();
self.1.try_outer_all(wl, keep_previous, |wlt, mut args| {
args.index += offset;
transform(wlt, args);
})
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
let a_count = self.0.count_nodes(&mut filter);
let offset = self.0.len();
let b_count = self.1.count_nodes(|mut args| {
args.index += offset;
filter(args)
});
a_count + b_count
}
}
impl<A: WidgetList, B: WidgetList> WidgetList for WidgetListChain<A, B> {
@ -183,69 +292,69 @@ impl<A: WidgetList, B: WidgetList> WidgetList for WidgetListChain<A, B> {
);
}
fn widget_id(&self, index: usize) -> WidgetId {
fn item_id(&self, index: usize) -> WidgetId {
let a_len = self.0.len();
if index < a_len {
self.0.widget_id(index)
self.0.item_id(index)
} else {
self.1.widget_id(index - a_len)
self.1.item_id(index - a_len)
}
}
fn widget_state(&self, index: usize) -> &StateMap {
fn item_state(&self, index: usize) -> &StateMap {
let a_len = self.0.len();
if index < a_len {
self.0.widget_state(index)
self.0.item_state(index)
} else {
self.1.widget_state(index - a_len)
self.1.item_state(index - a_len)
}
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
let a_len = self.0.len();
if index < a_len {
self.0.widget_state_mut(index)
self.0.item_state_mut(index)
} else {
self.1.widget_state_mut(index - a_len)
self.1.item_state_mut(index - a_len)
}
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
let a_len = self.0.len();
if index < a_len {
self.0.widget_bounds_info(index)
self.0.item_bounds_info(index)
} else {
self.1.widget_bounds_info(index - a_len)
self.1.item_bounds_info(index - a_len)
}
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
let a_len = self.0.len();
if index < a_len {
self.0.widget_border_info(index)
self.0.item_border_info(index)
} else {
self.1.widget_border_info(index - a_len)
self.1.item_border_info(index - a_len)
}
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
let a_len = self.0.len();
if index < a_len {
self.0.widget_render_info(index)
self.0.item_render_info(index)
} else {
self.1.widget_render_info(index - a_len)
self.1.item_render_info(index - a_len)
}
}
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let a_len = self.0.len();
if index < a_len {
self.0.widget_outer(index, wl, keep_previous, transform);
self.0.item_outer(index, wl, keep_previous, transform)
} else {
self.1.widget_outer(index - a_len, wl, keep_previous, transform);
self.1.item_outer(index - a_len, wl, keep_previous, transform)
}
}
@ -391,4 +500,113 @@ impl<A: UiNodeList, B: UiNodeList> UiNodeList for UiNodeListChain<A, B> {
self.1.item_render_update(index - a_len, ctx, update)
}
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_id(index)
} else {
self.1.try_item_id(index - a_len)
}
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_state(index)
} else {
self.1.try_item_state(index - a_len)
}
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_state_mut(index)
} else {
self.1.try_item_state_mut(index - a_len)
}
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_bounds_info(index)
} else {
self.1.try_item_bounds_info(index - a_len)
}
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_border_info(index)
} else {
self.1.try_item_border_info(index - a_len)
}
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
let a_len = self.0.len();
if index < a_len {
self.0.try_item_render_info(index)
} else {
self.1.try_item_render_info(index - a_len)
}
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
self.0.render_node_filtered(&mut filter, ctx, frame);
let offset = self.0.len();
self.1.render_node_filtered(
|mut a| {
a.index += offset;
filter(a)
},
ctx,
frame,
);
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let a_len = self.0.len();
if index < a_len {
self.0.try_item_outer(index, wl, keep_previous, transform)
} else {
self.1.try_item_outer(index - a_len, wl, keep_previous, transform)
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
self.0.try_outer_all(wl, keep_previous, &mut transform);
let offset = self.0.len();
self.1.try_outer_all(wl, keep_previous, |wlt, mut args| {
args.index += offset;
transform(wlt, args);
})
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
let a_count = self.0.count_nodes(&mut filter);
let offset = self.0.len();
let b_count = self.1.count_nodes(|mut args| {
args.index += offset;
filter(args)
});
a_count + b_count
}
}

View File

@ -13,6 +13,8 @@ use crate::{
BoxedWidget, UiNode, Widget, WidgetId,
};
use super::UiNodeFilterArgs;
/// A vector of boxed [`Widget`] items that remains sorted.
///
/// This type is a [`WidgetList`] that can be modified during runtime, and automatically remains sorted
@ -372,6 +374,81 @@ impl UiNodeList for SortedWidgetVec {
fn item_render_update(&self, index: usize, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.vec[index].render_update(ctx, update);
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
self.vec[index].try_id()
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
self.vec[index].try_state()
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
self.vec[index].try_state_mut()
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
self.vec[index].try_bounds_info()
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
self.vec[index].try_border_info()
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
self.vec[index].try_render_info()
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
w.render(ctx, frame);
}
}
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let w = &mut self.vec[index];
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(index, w.try_state_mut(), size))
})
} else {
None
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
for (i, w) in self.vec.iter_mut().enumerate() {
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(i, w.try_state_mut(), size));
});
}
}
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
let mut count = 0;
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
count += 1;
}
}
count
}
}
impl WidgetList for SortedWidgetVec {
fn boxed_widget_all(mut self) -> WidgetVec {
@ -381,27 +458,27 @@ impl WidgetList for SortedWidgetVec {
}
}
fn widget_id(&self, index: usize) -> WidgetId {
fn item_id(&self, index: usize) -> WidgetId {
self.vec[index].id()
}
fn widget_state(&self, index: usize) -> &StateMap {
fn item_state(&self, index: usize) -> &StateMap {
self.vec[index].state()
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
self.vec[index].state_mut()
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
self.vec[index].bounds_info()
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
self.vec[index].border_info()
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
self.vec[index].render_info()
}
@ -431,26 +508,26 @@ impl WidgetList for SortedWidgetVec {
count
}
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let w = &mut self.vec[index];
let size = w.bounds_info().outer_size();
wl.with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(index, Some(w.state_mut()), size));
});
transform(wlt, PosLayoutArgs::new(index, Some(w.state_mut()), size))
})
}
fn outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs) ,
{
for (i, w) in self.vec.iter_mut().enumerate() {
let size = w.bounds_info().outer_size();
wl.with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(i, Some(w.state_mut()), size));
})
});
}
}
}

View File

@ -1,10 +1,11 @@
use super::WidgetFilterArgs;
use crate::{
context::{InfoContext, LayoutContext, RenderContext, StateMap, WidgetContext},
event::EventUpdateArgs,
node_vec,
render::{FrameBuilder, FrameUpdate},
ui_list::{PosLayoutArgs, PreLayoutArgs, UiListObserver, UiNodeList, UiNodeVec, WidgetList, WidgetVec},
ui_list::{
PosLayoutArgs, PreLayoutArgs, UiListObserver, UiNodeFilterArgs, UiNodeList, UiNodeVec, WidgetFilterArgs, WidgetList, WidgetVec,
},
units::PxSize,
widget_info::{
WidgetBorderInfo, WidgetBoundsInfo, WidgetInfoBuilder, WidgetLayout, WidgetLayoutTranslation, WidgetRenderInfo, WidgetSubscriptions,
@ -84,59 +85,59 @@ macro_rules! impl_tuples {
)+
}
fn widget_id(&self, index: usize) -> WidgetId {
fn item_id(&self, index: usize) -> WidgetId {
match index {
$($n => self.items.$n.id(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_state(&self, index: usize) -> &StateMap {
fn item_state(&self, index: usize) -> &StateMap {
match index {
$($n => self.items.$n.state(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
match index {
$($n => self.items.$n.state_mut(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
match index {
$($n => self.items.$n.bounds_info(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
match index {
$($n => self.items.$n.border_info(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
match index {
$($n => self.items.$n.render_info(),)+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
match index {
$($n => {
let w = &mut self.items.$n;
let size = w.bounds_info().outer_size();
wl.with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new($n, Some(w.state_mut()), size));
});
transform(wlt, PosLayoutArgs::new($n, Some(w.state_mut()), size))
})
})+
_ => panic!("index {index} out of range for length {}", self.len())
}
@ -288,6 +289,117 @@ macro_rules! impl_tuples {
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
match index {
$(
$n => self.items.$n.try_id(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
match index {
$(
$n => self.items.$n.try_state(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
match index {
$(
$n => self.items.$n.try_state_mut(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
match index {
$(
$n => self.items.$n.try_bounds_info(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
match index {
$(
$n => self.items.$n.try_border_info(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
match index {
$(
$n => self.items.$n.try_render_info(),
)+
_ => panic!("index {index} out of range for length {}", self.len()),
}
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
$(
if filter(UiNodeFilterArgs::new($n, &self.items.$n)) {
self.items.$n.render(ctx, frame);
}
)+
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
match index {
$($n => {
let w = &mut self.items.$n;
if let Some(size) = w.try_bounds_info().map(|i|i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new($n, w.try_state_mut(), size))
})
} else {
None
}
})+
_ => panic!("index {index} out of range for length {}", self.len())
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
$(
let w = &mut self.items.$n;
if let Some(size) = w.try_bounds_info().map(|i|i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new($n, w.try_state_mut(), size));
});
}
)*
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
let mut count = 0;
$(
if filter(UiNodeFilterArgs::new($n, &self.items.$n)) {
count += 1;
}
)+
count
}
}
};
}
@ -388,6 +500,56 @@ macro_rules! empty_node_list {
fn item_render_update(&self, index: usize, _: &mut RenderContext, _: &mut FrameUpdate) {
panic!("index {index} out of range for length 0")
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
panic!("index {index} out of range for length 0")
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
panic!("index {index} out of range for length 0")
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
panic!("index {index} out of range for length 0")
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
panic!("index {index} out of range for length 0")
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
panic!("index {index} out of range for length 0")
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
panic!("index {index} out of range for length 0")
}
fn render_node_filtered<F>(&self, _: F, _: &mut RenderContext, _: &mut FrameBuilder)
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
}
fn try_item_outer<F, R>(&mut self, index: usize, _: &mut WidgetLayout, _: bool, _: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
panic!("index {index} out of range for length 0")
}
fn try_outer_all<F>(&mut self, _: &mut WidgetLayout, _: bool, _: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
}
fn count_nodes<F>(&self, _: F) -> usize
where
F: FnMut(super::UiNodeFilterArgs) -> bool,
{
0
}
}
)+}
}
@ -413,33 +575,33 @@ impl WidgetList for WidgetList0 {
{
}
fn widget_id(&self, index: usize) -> WidgetId {
fn item_id(&self, index: usize) -> WidgetId {
panic!("index {index} out of range for length 0")
}
fn widget_state(&self, index: usize) -> &StateMap {
fn item_state(&self, index: usize) -> &StateMap {
panic!("index {index} out of range for length 0")
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
panic!("index {index} out of range for length 0")
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
panic!("index {index} out of range for length 0")
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
panic!("index {index} out of range for length 0")
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
panic!("index {index} out of range for length 0")
}
fn widget_outer<F>(&mut self, index: usize, _: &mut WidgetLayout, _: bool, _: F)
fn item_outer<F, R>(&mut self, index: usize, _: &mut WidgetLayout, _: bool, _: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
panic!("index {index} out of range for length 0")
}

View File

@ -389,33 +389,108 @@ impl UiNodeList for WidgetVec {
fn item_render_update(&self, index: usize, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.vec[index].render_update(ctx, update);
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
self.vec[index].try_id()
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
self.vec[index].try_state()
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
self.vec[index].try_state_mut()
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
self.vec[index].try_bounds_info()
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
self.vec[index].try_border_info()
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
self.vec[index].try_render_info()
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
w.render(ctx, frame);
}
}
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let w = &mut self.vec[index];
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(index, w.try_state_mut(), size))
})
} else {
None
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
for (i, w) in self.vec.iter_mut().enumerate() {
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(i, w.try_state_mut(), size));
});
}
}
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
let mut count = 0;
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
count += 1;
}
}
count
}
}
impl WidgetList for WidgetVec {
fn boxed_widget_all(self) -> WidgetVec {
self
}
fn widget_id(&self, index: usize) -> WidgetId {
fn item_id(&self, index: usize) -> WidgetId {
self.vec[index].id()
}
fn widget_state(&self, index: usize) -> &StateMap {
fn item_state(&self, index: usize) -> &StateMap {
self.vec[index].state()
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
self.vec[index].state_mut()
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
self.vec[index].bounds_info()
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
self.vec[index].border_info()
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
self.vec[index].render_info()
}
@ -445,15 +520,15 @@ impl WidgetList for WidgetVec {
count
}
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let w = &mut self.vec[index];
let size = w.bounds_info().outer_size();
wl.with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(index, Some(w.state_mut()), size));
});
transform(wlt, PosLayoutArgs::new(index, Some(w.state_mut()), size))
})
}
fn outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
@ -839,6 +914,81 @@ impl UiNodeList for UiNodeVec {
fn item_render_update(&self, index: usize, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.vec[index].render_update(ctx, update);
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
self.vec[index].try_id()
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
self.vec[index].try_state()
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
self.vec[index].try_state_mut()
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
self.vec[index].try_bounds_info()
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
self.vec[index].try_border_info()
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
self.vec[index].try_render_info()
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
w.render(ctx, frame);
}
}
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
let w = &mut self.vec[index];
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(index, w.try_state_mut(), size))
})
} else {
None
}
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, mut transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
for (i, w) in self.vec.iter_mut().enumerate() {
if let Some(size) = w.try_bounds_info().map(|i| i.outer_size()) {
wl.try_with_outer(w, keep_previous, |wlt, w| {
transform(wlt, PosLayoutArgs::new(i, w.try_state_mut(), size));
});
}
}
}
fn count_nodes<F>(&self, mut filter: F) -> usize
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
let mut count = 0;
for (i, w) in self.iter().enumerate() {
if filter(UiNodeFilterArgs::new(i, w)) {
count += 1;
}
}
count
}
}
/// Creates a [`WidgetVec`] containing the arguments.
@ -902,3 +1052,5 @@ macro_rules! node_vec {
}
#[doc(inline)]
pub use crate::node_vec;
use super::UiNodeFilterArgs;

View File

@ -183,6 +183,75 @@ impl<W: WidgetList> UiNodeList for ZSortedWidgetList<W> {
fn item_render_update(&self, index: usize, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.list.item_render_update(index, ctx, update)
}
fn try_item_id(&self, index: usize) -> Option<WidgetId> {
self.list.try_item_id(index)
}
fn try_item_state(&self, index: usize) -> Option<&StateMap> {
self.list.try_item_state(index)
}
fn try_item_state_mut(&mut self, index: usize) -> Option<&mut StateMap> {
self.list.try_item_state_mut(index)
}
fn try_item_bounds_info(&self, index: usize) -> Option<&WidgetBoundsInfo> {
self.list.try_item_bounds_info(index)
}
fn try_item_border_info(&self, index: usize) -> Option<&WidgetBorderInfo> {
self.list.try_item_border_info(index)
}
fn try_item_render_info(&self, index: usize) -> Option<&WidgetRenderInfo> {
self.list.try_item_render_info(index)
}
fn render_node_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
if self.lookup.is_empty() {
self.list.render_node_filtered(filter, ctx, frame);
} else {
for &i in &self.lookup {
let i = i as usize;
let args = UiNodeFilterArgs {
index: i,
id: self.try_item_id(i),
bounds_info: self.try_item_bounds_info(i),
border_info: self.try_item_border_info(i),
render_info: self.try_item_render_info(i),
state: self.try_item_state(i),
};
if filter(args) {
self.item_render(i, ctx, frame);
}
}
}
}
fn try_item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> Option<R>
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
self.list.try_item_outer(index, wl, keep_previous, transform)
}
fn try_outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
where
F: FnMut(&mut WidgetLayoutTranslation, PosLayoutArgs),
{
self.list.try_outer_all(wl, keep_previous, transform)
}
fn count_nodes<F>(&self, filter: F) -> usize
where
F: FnMut(UiNodeFilterArgs) -> bool,
{
self.list.count_nodes(filter)
}
}
impl<W: WidgetList> WidgetList for ZSortedWidgetList<W> {
fn count<F>(&self, filter: F) -> usize
@ -196,28 +265,28 @@ impl<W: WidgetList> WidgetList for ZSortedWidgetList<W> {
self.list.boxed_widget_all()
}
fn widget_id(&self, index: usize) -> WidgetId {
self.list.widget_id(index)
fn item_id(&self, index: usize) -> WidgetId {
self.list.item_id(index)
}
fn widget_state(&self, index: usize) -> &StateMap {
self.list.widget_state(index)
fn item_state(&self, index: usize) -> &StateMap {
self.list.item_state(index)
}
fn widget_state_mut(&mut self, index: usize) -> &mut StateMap {
self.list.widget_state_mut(index)
fn item_state_mut(&mut self, index: usize) -> &mut StateMap {
self.list.item_state_mut(index)
}
fn widget_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
self.list.widget_bounds_info(index)
fn item_bounds_info(&self, index: usize) -> &WidgetBoundsInfo {
self.list.item_bounds_info(index)
}
fn widget_border_info(&self, index: usize) -> &WidgetBorderInfo {
self.list.widget_border_info(index)
fn item_border_info(&self, index: usize) -> &WidgetBorderInfo {
self.list.item_border_info(index)
}
fn widget_render_info(&self, index: usize) -> &WidgetRenderInfo {
self.list.widget_render_info(index)
fn item_render_info(&self, index: usize) -> &WidgetRenderInfo {
self.list.item_render_info(index)
}
fn render_filtered<F>(&self, mut filter: F, ctx: &mut RenderContext, frame: &mut FrameBuilder)
@ -231,10 +300,11 @@ impl<W: WidgetList> WidgetList for ZSortedWidgetList<W> {
let i = i as usize;
let args = WidgetFilterArgs {
index: i,
bounds_info: self.widget_bounds_info(i),
border_info: self.widget_border_info(i),
render_info: self.widget_render_info(i),
state: self.widget_state(i),
id: self.item_id(i),
bounds_info: self.item_bounds_info(i),
border_info: self.item_border_info(i),
render_info: self.item_render_info(i),
state: self.item_state(i),
};
if filter(args) {
self.item_render(i, ctx, frame);
@ -243,11 +313,11 @@ impl<W: WidgetList> WidgetList for ZSortedWidgetList<W> {
}
}
fn widget_outer<F>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
fn item_outer<F, R>(&mut self, index: usize, wl: &mut WidgetLayout, keep_previous: bool, transform: F) -> R
where
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs),
F: FnOnce(&mut WidgetLayoutTranslation, PosLayoutArgs) -> R,
{
self.list.widget_outer(index, wl, keep_previous, transform)
self.list.item_outer(index, wl, keep_previous, transform)
}
fn outer_all<F>(&mut self, wl: &mut WidgetLayout, keep_previous: bool, transform: F)
@ -467,7 +537,7 @@ pub trait WidgetListZIndexExt {
}
impl<L: WidgetList> WidgetListZIndexExt for L {
fn widget_z_index(&self, index: usize) -> ZIndex {
self.widget_state(index).copy(ZIndexKey).unwrap_or_default()
self.item_state(index).copy(ZIndexKey).unwrap_or_default()
}
fn init_all_z(&mut self, ctx: &mut WidgetContext, sort_z: &mut bool) {

View File

@ -272,6 +272,68 @@ pub trait UiNode: 'static {
{
self
}
/// Gets if this node is a [`Widget`] implementer.
fn is_widget(&self) -> bool {
false
}
/// Gets the [`Widget::id`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_id(&self) -> Option<WidgetId> {
None
}
/// Gets the [`Widget::state`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_state(&self) -> Option<&StateMap> {
None
}
/// Gets the [`Widget::state_mut`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_state_mut(&mut self) -> Option<&mut StateMap> {
None
}
/// Gets the [`Widget::bounds_info`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_bounds_info(&self) -> Option<&WidgetBoundsInfo> {
None
}
/// Gets the [`Widget::border_info`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_border_info(&self) -> Option<&WidgetBorderInfo> {
None
}
/// Gets the [`Widget::render_info`] if this node [`is_widget`].
///
/// [`is_widget`]: UiNode::is_widget
fn try_render_info(&self) -> Option<&WidgetRenderInfo> {
None
}
/// Gets this node as a [`BoxedWidget`], if the node [`is_widget`] this is the same as
/// [`Widget::boxed_wgt`], otherwise a new widget is generated with the node as the *inner*.
///
/// [`is_widget`]: Self::is_widget
fn into_widget(self) -> BoxedWidget
where
Self: Sized,
{
use crate::widget_base::implicit_base::nodes;
let node = nodes::inner(self.cfg_boxed());
let wgt = nodes::widget(node, WidgetId::new_unique());
wgt.boxed_wgt()
}
}
#[doc(hidden)]
@ -285,6 +347,15 @@ pub trait UiNodeBoxed: 'static {
fn layout_boxed(&mut self, ctx: &mut LayoutContext, wl: &mut WidgetLayout) -> PxSize;
fn render_boxed(&self, ctx: &mut RenderContext, frame: &mut FrameBuilder);
fn render_update_boxed(&self, ctx: &mut RenderContext, update: &mut FrameUpdate);
fn is_widget_boxed(&self) -> bool;
fn try_id_boxed(&self) -> Option<WidgetId>;
fn try_state_boxed(&self) -> Option<&StateMap>;
fn try_state_mut_boxed(&mut self) -> Option<&mut StateMap>;
fn try_bounds_info_boxed(&self) -> Option<&WidgetBoundsInfo>;
fn try_border_info_boxed(&self) -> Option<&WidgetBorderInfo>;
fn try_render_info_boxed(&self) -> Option<&WidgetRenderInfo>;
fn into_widget_boxed(self: Box<Self>) -> BoxedWidget;
}
impl<U: UiNode> UiNodeBoxed for U {
@ -323,6 +394,38 @@ impl<U: UiNode> UiNodeBoxed for U {
fn render_update_boxed(&self, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.render_update(ctx, update);
}
fn is_widget_boxed(&self) -> bool {
self.is_widget()
}
fn try_id_boxed(&self) -> Option<WidgetId> {
self.try_id()
}
fn try_state_boxed(&self) -> Option<&StateMap> {
self.try_state()
}
fn try_state_mut_boxed(&mut self) -> Option<&mut StateMap> {
self.try_state_mut()
}
fn try_bounds_info_boxed(&self) -> Option<&WidgetBoundsInfo> {
self.try_bounds_info()
}
fn try_border_info_boxed(&self) -> Option<&WidgetBorderInfo> {
self.try_border_info()
}
fn try_render_info_boxed(&self) -> Option<&WidgetRenderInfo> {
self.try_render_info()
}
fn into_widget_boxed(self: Box<Self>) -> BoxedWidget {
self.into_widget()
}
}
/// An [`UiNode`] in a box.
@ -375,6 +478,41 @@ impl UiNode for BoxedUiNode {
{
self
}
fn is_widget(&self) -> bool {
self.as_ref().is_widget_boxed()
}
fn try_id(&self) -> Option<WidgetId> {
self.as_ref().try_id_boxed()
}
fn try_state(&self) -> Option<&StateMap> {
self.as_ref().try_state_boxed()
}
fn try_state_mut(&mut self) -> Option<&mut StateMap> {
self.as_mut().try_state_mut_boxed()
}
fn try_bounds_info(&self) -> Option<&WidgetBoundsInfo> {
self.as_ref().try_bounds_info_boxed()
}
fn try_border_info(&self) -> Option<&WidgetBorderInfo> {
self.as_ref().try_border_info_boxed()
}
fn try_render_info(&self) -> Option<&WidgetRenderInfo> {
self.as_ref().try_render_info_boxed()
}
fn into_widget(self) -> BoxedWidget
where
Self: Sized,
{
self.into_widget_boxed()
}
}
impl<U: UiNode> UiNode for Option<U> {
@ -433,6 +571,75 @@ impl<U: UiNode> UiNode for Option<U> {
node.render_update(ctx, update);
}
}
fn boxed(self) -> BoxedUiNode
where
Self: Sized,
{
match self {
Some(node) => node.boxed(),
None => NilUiNode.boxed(),
}
}
fn is_widget(&self) -> bool {
match self {
Some(node) => node.is_widget(),
None => false,
}
}
fn try_id(&self) -> Option<WidgetId> {
match self {
Some(node) => node.try_id(),
None => None,
}
}
fn try_state(&self) -> Option<&StateMap> {
match self {
Some(node) => node.try_state(),
None => None,
}
}
fn try_state_mut(&mut self) -> Option<&mut StateMap> {
match self {
Some(node) => node.try_state_mut(),
None => None,
}
}
fn try_bounds_info(&self) -> Option<&WidgetBoundsInfo> {
match self {
Some(node) => node.try_bounds_info(),
None => None,
}
}
fn try_border_info(&self) -> Option<&WidgetBorderInfo> {
match self {
Some(node) => node.try_border_info(),
None => None,
}
}
fn try_render_info(&self) -> Option<&WidgetRenderInfo> {
match self {
Some(node) => node.try_render_info(),
None => None,
}
}
fn into_widget(self) -> BoxedWidget
where
Self: Sized,
{
match self {
Some(node) => node.into_widget(),
None => NilUiNode.into_widget(),
}
}
}
macro_rules! declare_widget_test_calls {
@ -657,6 +864,48 @@ impl UiNode for BoxedWidget {
fn render_update(&self, ctx: &mut RenderContext, update: &mut FrameUpdate) {
self.as_ref().render_update_boxed(ctx, update);
}
fn boxed(self) -> BoxedUiNode
where
Self: Sized,
{
Box::new(self)
}
fn is_widget(&self) -> bool {
true
}
fn try_id(&self) -> Option<WidgetId> {
Some(self.id())
}
fn try_state(&self) -> Option<&StateMap> {
Some(self.state())
}
fn try_state_mut(&mut self) -> Option<&mut StateMap> {
Some(self.state_mut())
}
fn try_bounds_info(&self) -> Option<&WidgetBoundsInfo> {
Some(self.bounds_info())
}
fn try_border_info(&self) -> Option<&WidgetBorderInfo> {
Some(self.border_info())
}
fn try_render_info(&self) -> Option<&WidgetRenderInfo> {
Some(self.render_info())
}
fn into_widget(self) -> BoxedWidget
where
Self: Sized,
{
self
}
}
impl Widget for BoxedWidget {
fn id(&self) -> WidgetId {
@ -682,6 +931,13 @@ impl Widget for BoxedWidget {
fn render_info(&self) -> &WidgetRenderInfo {
self.as_ref().render_info_boxed()
}
fn boxed_wgt(self) -> BoxedWidget
where
Self: Sized,
{
self
}
}
/// A UI node that does not contain any other node, only takes the minimum space and renders nothing.

View File

@ -368,18 +368,6 @@ pub mod implicit_base {
}
}
fn update(&mut self, ctx: &mut WidgetContext) {
#[cfg(debug_assertions)]
if !self.inited {
tracing::error!(target: "widget_base", "`UiNode::update` called in not inited widget {:?}", self.id);
}
if self.subscriptions.borrow().update_intersects(ctx.updates) {
let (_, updates) = ctx.widget_context(self.id, &self.info, &mut self.state, |ctx| self.child.update(ctx));
*self.pending_updates.get_mut() |= updates;
}
}
fn event<EU: EventUpdateArgs>(&mut self, ctx: &mut WidgetContext, args: &EU) {
#[cfg(debug_assertions)]
if !self.inited {
@ -392,6 +380,18 @@ pub mod implicit_base {
}
}
fn update(&mut self, ctx: &mut WidgetContext) {
#[cfg(debug_assertions)]
if !self.inited {
tracing::error!(target: "widget_base", "`UiNode::update` called in not inited widget {:?}", self.id);
}
if self.subscriptions.borrow().update_intersects(ctx.updates) {
let (_, updates) = ctx.widget_context(self.id, &self.info, &mut self.state, |ctx| self.child.update(ctx));
*self.pending_updates.get_mut() |= updates;
}
}
fn layout(&mut self, ctx: &mut LayoutContext, wl: &mut WidgetLayout) -> PxSize {
#[cfg(debug_assertions)]
if !self.inited {
@ -442,6 +442,41 @@ pub mod implicit_base {
}
// else, don't need to do anything, updates are additive.
}
fn is_widget(&self) -> bool {
true
}
fn try_id(&self) -> Option<WidgetId> {
Some(self.id())
}
fn try_state(&self) -> Option<&StateMap> {
Some(self.state())
}
fn try_state_mut(&mut self) -> Option<&mut StateMap> {
Some(self.state_mut())
}
fn try_bounds_info(&self) -> Option<&WidgetBoundsInfo> {
Some(self.bounds_info())
}
fn try_border_info(&self) -> Option<&WidgetBorderInfo> {
Some(self.border_info())
}
fn try_render_info(&self) -> Option<&WidgetRenderInfo> {
Some(self.render_info())
}
fn into_widget(self) -> crate::BoxedWidget
where
Self: Sized,
{
self.boxed_wgt()
}
}
impl<T: UiNode> Widget for WidgetNode<T> {
fn id(&self) -> WidgetId {

View File

@ -14,7 +14,7 @@ use crate::{
var::{Var, VarValue, VarsRead, WithVarsRead},
widget_base::Visibility,
window::WindowId,
Widget, WidgetId,
UiNode, Widget, WidgetId,
};
unique_id_64! {
@ -233,17 +233,43 @@ impl WidgetLayout {
keep_previous: bool,
translate: impl FnOnce(&mut WidgetLayoutTranslation, &mut W) -> R,
) -> R {
let prev_outer = widget.bounds_info().outer_offset();
self.with_outer_impl(widget.bounds_info().clone(), widget, keep_previous, translate)
}
/// Applies [`with_outer`] to the `node` if it is a full widget.
///
/// Returns `Some(_)` if `translate` was called, or `None` if the `node` was not a full widget.
///
/// [`with_outer`]: Self::with_outer
pub fn try_with_outer<N: UiNode, R>(
&mut self,
node: &mut N,
keep_previous: bool,
translate: impl FnOnce(&mut WidgetLayoutTranslation, &mut N) -> R,
) -> Option<R> {
node.try_bounds_info()
.cloned()
.map(|info| self.with_outer_impl(info, node, keep_previous, translate))
}
fn with_outer_impl<T, R>(
&mut self,
bounds: WidgetBoundsInfo,
target: &mut T,
keep_previous: bool,
translate: impl FnOnce(&mut WidgetLayoutTranslation, &mut T) -> R,
) -> R {
let prev_outer = bounds.outer_offset();
if !keep_previous {
widget.bounds_info().set_outer_offset(PxVector::zero());
bounds.set_outer_offset(PxVector::zero());
}
let mut wl = WidgetLayout {
t: WidgetLayoutTranslation {
offset_buf: PxVector::zero(),
baseline: Px(0),
known: Some(widget.bounds_info().clone()),
known: Some(bounds),
known_target: KnownTarget::Outer,
},
known_prev_offsets: [PxVector::zero(); 3],
@ -252,10 +278,12 @@ impl WidgetLayout {
child_offset_changed: false,
};
let size = translate(&mut wl, widget);
let size = translate(&mut wl, target);
if prev_outer != widget.bounds_info().outer_offset() {
widget.bounds_info().update_offsets_version();
let bounds = wl.t.known.unwrap();
if prev_outer != bounds.outer_offset() {
bounds.update_offsets_version();
self.child_offset_changed = true;
}

View File

@ -118,7 +118,7 @@ pub mod h_stack {
|ctx| {
size.width = Px(0);
for i in 0..self.children.len() {
let o_size = self.children.widget_bounds_info(i).outer_size();
let o_size = self.children.item_bounds_info(i).outer_size();
if Some(o_size.height) != ctx.constrains().y.max() {
// only need second pass for items that don't fill
let (a_size, _) = wl.with_child(ctx, |ctx, wl| {
@ -133,7 +133,7 @@ pub mod h_stack {
}
} else {
// item already fills width, but may have moved due to sibling new fill size
self.children.widget_outer(i, wl, false, |wlt, _| {
self.children.item_outer(i, wl, false, |wlt, _| {
wlt.translate(PxVector::new(size.width, Px(0)));
if o_size.width > Px(0) {
@ -302,7 +302,7 @@ pub mod v_stack {
|ctx| {
size.height = Px(0);
for i in 0..self.children.len() {
let o_size = self.children.widget_bounds_info(i).outer_size();
let o_size = self.children.item_bounds_info(i).outer_size();
if Some(o_size.width) != ctx.constrains().x.max() {
// only need second pass for items that don't fill
let (a_size, _) = wl.with_child(ctx, |ctx, wl| {
@ -317,7 +317,7 @@ pub mod v_stack {
}
} else {
// item already fills width, but may have moved due to sibling new fill size
self.children.widget_outer(i, wl, false, |wlt, _| {
self.children.item_outer(i, wl, false, |wlt, _| {
wlt.translate(PxVector::new(Px(0), size.height));
if o_size.height > Px(0) {