Fixed focus bugs,

This commit is contained in:
Samuel Guerra 2022-07-08 18:59:32 -03:00
parent 7e23dbe199
commit 7d4a255a1f
21 changed files with 146 additions and 160 deletions

View File

@ -1,3 +1,5 @@
* Directional navigation is disabling when focused is disabled.
* Use `nearest_oriented` in `directional_from`.
* Speedup the alt focus query, it is the slowest now.

View File

@ -410,7 +410,7 @@ mod inspect {
} else {
return format!("<{p}>");
};
let widget = if let Some(w) = frame.get(p) {
let widget = if let Some(w) = frame.get(p.widget_id()) {
w
} else {
return format!("<{p}>");

View File

@ -1218,6 +1218,7 @@ pub fn focus_goes_to_parent_after_remove() {
assert_eq!(Some(child_id), app.focused());
app.take_focus_changed();
println!("!!: JUST BEFORE INTERACTIVE BLOCKED");
app.set_vars(|vars| {
interactive.set(vars, false);
});

View File

@ -439,7 +439,7 @@ impl EventDeliveryList {
let mut self_windows = self.windows.borrow_mut();
'search: for wgt in search {
for win in windows.widget_trees() {
if let Some(info) = win.find(wgt) {
if let Some(info) = win.get(wgt) {
if let Some(w) = self_windows.iter_mut().find(|w| w.id == win.window_id()) {
if !w.all {
w.widgets.push(info.path());

View File

@ -97,8 +97,8 @@ use crate::{
service::Service,
units::{Px, PxPoint, PxRect, TimeUnits},
var::{var, RcVar, ReadOnlyRcVar, Var, Vars},
widget_info::{InteractionPath, WidgetBoundsInfo},
window::{WindowFocusChangedEvent, WindowId, Windows},
widget_info::{InteractionPath, WidgetBoundsInfo, WidgetInfoTree},
window::{WidgetInfoChangedEvent, WindowFocusChangedEvent, WindowId, Windows},
WidgetId,
};
@ -370,7 +370,7 @@ event! {
///
/// This extension requires the [`Windows`] service.
///
/// This extension listens to the [`MouseInputEvent`], [`ShortcutEvent`] and [`WindowFocusChangedEvent`].
/// This extension listens to the [`MouseInputEvent`], [`ShortcutEvent`] [`WindowFocusChangedEvent`] and [`WidgetInfoChangedEvent`].
///
/// To work properly it should be added to the app after the windows manager extension.
///
@ -384,12 +384,14 @@ event! {
pub struct FocusManager {
last_keyboard_event: Instant,
commands: Option<FocusCommands>,
pending_render: Option<WidgetInfoTree>,
}
impl Default for FocusManager {
fn default() -> Self {
Self {
last_keyboard_event: Instant::now() - Duration::from_secs(10),
commands: None,
pending_render: None,
}
}
}
@ -400,33 +402,49 @@ impl AppExtension for FocusManager {
}
fn event_preview<EV: EventUpdateArgs>(&mut self, ctx: &mut AppContext, args: &EV) {
self.commands.as_mut().unwrap().event_preview(ctx, args);
if let Some(args) = WidgetInfoChangedEvent.update(args) {
if ctx
.services
.focus()
.focused
.as_ref()
.map(|f| f.path.window_id() == args.window_id)
.unwrap_or_default()
{
// we need up-to-date due to visibility or spatial movement and that is affected by both layout and render.
// so we delay responding to the event if a render or layout was requested when the tree was invalidated.
if args.pending_render {
self.pending_render = Some(args.tree.clone());
} else {
// no visual change, update interactivity changes.
self.pending_render = None;
self.on_info_tree_update(&args.tree, ctx);
}
}
} else {
self.commands.as_mut().unwrap().event_preview(ctx, args);
}
}
fn render(&mut self, ctx: &mut AppContext) {
let (focus, windows) = ctx.services.req_multi::<(Focus, Windows)>();
if let Some(f) = &focus.focused {
let w_id = f.path.window_id();
if let Ok(tree) = windows.widget_tree(w_id) {
if tree.last_changed_frame().unwrap_or(FrameId::INVALID) == focus.enabled_nav.1 {
return;
if let Some(tree) = self.pending_render.take() {
self.on_info_tree_update(&tree, ctx);
} else {
// update visibility or enabled commands, they may have changed if the `spatial_frame_id` changed.
let (focus, windows) = ctx.services.req_multi::<(Focus, Windows)>();
let mut invalidated_cmds_or_focused = None;
if let Some(f) = &focus.focused {
let w_id = f.path.window_id();
if let Ok(tree) = windows.widget_tree(w_id) {
if tree.spatial_frame_id().unwrap_or(FrameId::INVALID) != focus.enabled_nav.1 {
invalidated_cmds_or_focused = Some(tree.clone());
}
}
}
}
focus.update_focused_center();
// widgets may have changed visibility.
let args = focus.continue_focus(ctx.vars, windows);
let w_id = args.as_ref().and_then(|a| a.new_focus.as_ref().map(|p| p.window_id()));
self.notify(ctx.vars, ctx.events, focus, windows, args);
// cleanup return focuses.
if let Some(w_id) = w_id {
let tree = windows.widget_tree(w_id).unwrap();
for args in focus.cleanup_returns(ctx.vars, FocusInfoTree::new(tree, focus.focus_disabled_widgets)) {
ReturnFocusChangedEvent.notify(ctx.events, args);
if let Some(tree) = invalidated_cmds_or_focused {
self.on_info_tree_update(&tree, ctx);
}
}
}
@ -480,6 +498,20 @@ impl AppExtension for FocusManager {
}
}
impl FocusManager {
fn on_info_tree_update(&mut self, tree: &WidgetInfoTree, ctx: &mut AppContext) {
let (focus, windows) = ctx.services.req_multi::<(Focus, Windows)>();
focus.update_focused_center();
// widget tree rebuilt or visibility may have changed, check if focus is still valid
let args = focus.continue_focus(ctx.vars, windows);
self.notify(ctx.vars, ctx.events, focus, windows, args);
// cleanup return focuses.
for args in focus.cleanup_returns(ctx.vars, FocusInfoTree::new(tree, focus.focus_disabled_widgets)) {
ReturnFocusChangedEvent.notify(ctx.events, args);
}
}
fn notify(&mut self, vars: &Vars, events: &mut Events, focus: &mut Focus, windows: &mut Windows, args: Option<FocusChangedArgs>) {
if let Some(mut args) = args {
if !args.highlight && args.new_focus.is_some() {
@ -794,7 +826,7 @@ impl Focus {
(Some(prev), move_) => {
if let Ok(info) = windows.widget_tree(prev.path.window_id()) {
let info = FocusInfoTree::new(info, self.focus_disabled_widgets);
if let Some(w) = info.find(prev.path.widget_id()) {
if let Some(w) = info.get(prev.path.widget_id()) {
if let Some(new_focus) = match move_ {
// tabular
FocusTarget::Next => w.next_tab(false),
@ -864,7 +896,7 @@ impl Focus {
if let Ok(true) = windows.is_focused(focused.path.window_id()) {
let info = windows.widget_tree(focused.path.window_id()).unwrap();
if let Some(widget) = info
.find(focused.path.widget_id())
.get(focused.path.widget_id())
.map(|w| w.as_focus_info(self.focus_disabled_widgets))
{
if widget.is_focusable() {
@ -896,7 +928,7 @@ impl Focus {
} else {
// widget not found, move to focusable known parent
for &parent in focused.path.ancestors().iter().rev() {
if let Some(parent) = info.find(parent).and_then(|w| w.as_focusable(self.focus_disabled_widgets)) {
if let Some(parent) = info.get(parent).and_then(|w| w.as_focusable(self.focus_disabled_widgets)) {
// move to nearest inside focusable parent, or parent
let new_focus = parent.nearest(focused.center, Px::MAX).unwrap_or(parent);
self.enabled_nav = new_focus.enabled_nav_with_frame();
@ -952,7 +984,7 @@ impl Focus {
let mut target = None;
if let Some(w) = windows
.widget_trees()
.find_map(|info| info.find(widget_id))
.find_map(|info| info.get(widget_id))
.map(|w| w.as_focus_info(self.focus_disabled_widgets))
{
if w.is_focusable() {
@ -1083,7 +1115,7 @@ impl Focus {
fn move_after_focus(&mut self, vars: &Vars, windows: &Windows, reverse: bool) -> Option<FocusChangedArgs> {
if let Some(focused) = &self.focused {
if let Some(info) = windows.focused_info() {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(&focused.path) {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(focused.path.widget_id()) {
if widget.is_scope() {
if let Some(widget) = widget.on_focus_scope_move(|id| self.return_focused.get(&id).map(|p| p.as_path()), reverse) {
self.enabled_nav = widget.enabled_nav_with_frame();
@ -1126,7 +1158,7 @@ impl Focus {
// moved inside an ALT.
if let Ok(info) = windows.widget_tree(new_focus.path.window_id()) {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(&new_focus.path) {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(new_focus.path.widget_id()) {
let alt_scope = if widget.is_alt_scope() {
Some(widget)
} else {
@ -1159,7 +1191,7 @@ impl Focus {
if let Some(new_focus) = &self.focused {
if let Ok(info) = windows.widget_tree(new_focus.path.window_id()) {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(&new_focus.path) {
if let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets).get(new_focus.path.widget_id()) {
if widget.scopes().all(|s| !s.is_alt_scope()) {
// if not inside ALT, update return for each LastFocused parent scopes.
@ -1211,7 +1243,7 @@ impl Focus {
let mut retain = false;
if let Some(widget) = info.get(widget_path) {
if let Some(widget) = info.get(widget_path.widget_id()) {
if let Some(scope) = widget.scopes().find(|s| s.info.widget_id() == scope_id) {
if scope.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused {
retain = true; // retain, widget still exists in same scope and scope still is LastFocused.
@ -1227,7 +1259,7 @@ impl Focus {
*widget_path = path;
}
}
} else if let Some(scope) = info.find(scope_id) {
} else if let Some(scope) = info.get(scope_id) {
if scope.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused {
// widget not inside scope anymore, but scope still exists and is valid.
if let Some(first) = scope.first_tab_descendant() {
@ -1263,7 +1295,7 @@ impl Focus {
}
if !retain {
let scope_path = info.find(scope_id).map(|i| i.info.interaction_path());
let scope_path = info.get(scope_id).map(|i| i.info.interaction_path());
if scope_path.is_some() {
match self.return_focused_var.entry(scope_id) {
@ -1294,7 +1326,7 @@ impl Focus {
retain_alt = false; // will retain only if still valid
if let Some(widget) = info.get(widget_path) {
if let Some(widget) = info.get(widget_path.widget_id()) {
if !widget.scopes().any(|s| s.info.widget_id() == scope.widget_id()) {
retain_alt = true; // retain, widget still exists outside of the ALT scope.
@ -1373,6 +1405,7 @@ impl Focus {
}
}
#[derive(Debug)]
struct FocusedInfo {
path: InteractionPath,
bounds_info: WidgetBoundsInfo,
@ -1394,7 +1427,7 @@ trait EnabledNavWithFrame {
impl<'a> EnabledNavWithFrame for WidgetFocusInfo<'a> {
fn enabled_nav_with_frame(self) -> (FocusNavAction, FrameId) {
let nav = self.enabled_nav();
let frame = self.info.tree().last_changed_frame().unwrap_or(FrameId::INVALID);
let frame = self.info.tree().spatial_frame_id().unwrap_or(FrameId::INVALID);
(nav, frame)
}
}

View File

@ -411,28 +411,19 @@ impl<'a> FocusInfoTree<'a> {
}
/// Reference to the widget in the tree, if it is present and is focusable.
pub fn find(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
self.tree
.find(widget_id)
.and_then(|i| i.as_focusable(self.focus_disabled_widgets()))
}
/// Reference to the widget in the tree, if it is present and is focusable.
///
/// Faster then [`find`](Self::find) if the widget path was generated by the same tree.
pub fn get(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
self.tree.get(path).and_then(|i| i.as_focusable(self.focus_disabled_widgets()))
pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
self.tree.get(widget_id).and_then(|i| i.as_focusable(self.focus_disabled_widgets()))
}
/// Reference to the first focusable widget or parent in the tree.
pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
self.get(path)
.or_else(|| path.ancestors().iter().rev().find_map(|&id| self.find(id)))
self.get(path.widget_id())
.or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
}
/// If the tree info contains the widget and it is focusable.
pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
self.find(widget_id).is_some()
self.get(widget_id).is_some()
}
}
@ -642,7 +633,7 @@ impl<'a> WidgetFocusInfo<'a> {
}
}
FocusScopeOnFocus::LastFocused => last_focused(self.info.widget_id())
.and_then(|path| self.info.tree().get(path))
.and_then(|path| self.info.tree().get(path.widget_id()))
.and_then(|w| w.as_focusable(self.focus_disabled_widgets()))
.and_then(|f| {
if f.info.is_descendant(self.info) {
@ -1018,7 +1009,7 @@ impl<'a> WidgetFocusInfo<'a> {
up_to_scope.all(|w| !w.focus_info().skip_directional())
}
};
if any {
return scope.oriented(origin, Px::MAX, orientation).find(|f| filter(*f));
}

View File

@ -166,7 +166,7 @@ pub fn enabled_nav_cycle_0_horizontal() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-0").unwrap();
let item = tree.get("item-0").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -179,7 +179,7 @@ pub fn enabled_nav_cycle_0_vertical() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-0").unwrap();
let item = tree.get("item-0").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -192,7 +192,7 @@ pub fn enabled_nav_cycle_1_horizontal() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-1").unwrap();
let item = tree.get("item-1").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -205,7 +205,7 @@ pub fn enabled_nav_cycle_1_vertical() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-1").unwrap();
let item = tree.get("item-1").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -218,7 +218,7 @@ pub fn enabled_nav_cycle_2_horizontal() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-2").unwrap();
let item = tree.get("item-2").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -231,7 +231,7 @@ pub fn enabled_nav_cycle_2_vertical() {
let tree = scope(TabNav::Cycle, DirectionalNav::Cycle, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-2").unwrap();
let item = tree.get("item-2").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -244,7 +244,7 @@ pub fn enabled_nav_contained_0_horizontal() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-0").unwrap();
let item = tree.get("item-0").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -257,7 +257,7 @@ pub fn enabled_nav_contained_0_vertical() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-0").unwrap();
let item = tree.get("item-0").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -270,7 +270,7 @@ pub fn enabled_nav_contained_1_horizontal() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-1").unwrap();
let item = tree.get("item-1").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -283,7 +283,7 @@ pub fn enabled_nav_contained_1_vertical() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-1").unwrap();
let item = tree.get("item-1").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -296,7 +296,7 @@ pub fn enabled_nav_contained_2_horizontal() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-2").unwrap();
let item = tree.get("item-2").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -309,7 +309,7 @@ pub fn enabled_nav_contained_2_vertical() {
let tree = scope(TabNav::Contained, DirectionalNav::Contained, false);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-2").unwrap();
let item = tree.get("item-2").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -322,7 +322,7 @@ pub fn enabled_nav_none_0() {
let tree = scope(TabNav::None, DirectionalNav::None, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-0").unwrap();
let item = tree.get("item-0").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -335,7 +335,7 @@ pub fn enabled_nav_none_1() {
let tree = scope(TabNav::None, DirectionalNav::None, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-1").unwrap();
let item = tree.get("item-1").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();
@ -348,7 +348,7 @@ pub fn enabled_nav_none_2() {
let tree = scope(TabNav::None, DirectionalNav::None, true);
let tree = FocusInfoTree::new(&tree, true);
let item = tree.find("item-2").unwrap();
let item = tree.get("item-2").unwrap();
let result = item.enabled_nav();
let actual = item.actual_enabled_nav();

View File

@ -877,7 +877,7 @@ impl ShortcutTarget {
let mut found = self.last_found.borrow_mut();
if let Some(found) = &mut *found {
if let Ok(tree) = windows.widget_tree(found.window_id()) {
if let Some(w) = tree.get(found) {
if let Some(w) = tree.get(found.widget_id()) {
let path = w.interaction_path();
*found = path.as_path().clone();
@ -887,7 +887,7 @@ impl ShortcutTarget {
}
for tree in windows.widget_trees() {
if let Some(w) = tree.find(self.widget_id) {
if let Some(w) = tree.get(self.widget_id) {
let path = w.interaction_path();
*found = Some(path.as_path().clone());
@ -1173,7 +1173,7 @@ impl ShortcutActions {
cmd_focused_widget = Some(cmd);
} else if cmd_not_focused_widget.is_none() {
for tree in windows.widget_trees() {
if let Some(info) = tree.find(id) {
if let Some(info) = tree.get(id) {
if info.interactivity().is_enabled() {
cmd_not_focused_widget = Some(cmd);
}

View File

@ -743,7 +743,7 @@ impl MouseManager {
let target = hits
.target()
.and_then(|t| frame_info.find(t.widget_id).map(|w| w.interaction_path()))
.and_then(|t| frame_info.get(t.widget_id).map(|w| w.interaction_path()))
.unwrap_or_else(|| frame_info.root().interaction_path());
let target = match target.unblocked() {
@ -985,7 +985,7 @@ impl MouseManager {
let hits = FrameHitInfo::new(window_id, hits_res.0, hits_res.1, &hits_res.2);
let target = if let Some(t) = hits.target() {
frame_info.find(t.widget_id).map(|w| w.interaction_path()).unwrap_or_else(|| {
frame_info.get(t.widget_id).map(|w| w.interaction_path()).unwrap_or_else(|| {
tracing::error!("hits target `{}` not found", t.widget_id);
frame_info.root().interaction_path()
})
@ -1045,7 +1045,7 @@ impl MouseManager {
let target = hits
.target()
.and_then(|t| frame_info.find(t.widget_id).map(|w| w.interaction_path()))
.and_then(|t| frame_info.get(t.widget_id).map(|w| w.interaction_path()))
.unwrap_or_else(|| frame_info.root().interaction_path());
if let Some(target) = target.unblocked() {
@ -1119,7 +1119,7 @@ impl AppExtension for MouseManager {
let hits = FrameHitInfo::new(args.window_id, args.frame_id, args.cursor_hits.0, &args.cursor_hits.1);
let target = hits
.target()
.and_then(|t| windows.widget_tree(args.window_id).unwrap().find(t.widget_id))
.and_then(|t| windows.widget_tree(args.window_id).unwrap().get(t.widget_id))
.and_then(|w| w.interaction_path().unblocked());
self.pos_hits = (args.frame_id, args.cursor_hits.0, args.cursor_hits.1.clone());
@ -1460,7 +1460,7 @@ impl Mouse {
if let Some((widget_id, mode)) = self.capture_request.take() {
if let Ok(true) = windows.is_focused(current_target.window_id()) {
// current window pressed
if let Some(widget) = windows.widget_tree(current_target.window_id()).unwrap().find(widget_id) {
if let Some(widget) = windows.widget_tree(current_target.window_id()).unwrap().get(widget_id) {
// request valid
self.set_capture(widget.interaction_path(), mode, events);
}
@ -1478,7 +1478,7 @@ impl Mouse {
if let Some((target, mode)) = &self.current_capture {
if frame.window_id() == target.window_id() {
// is a frame from the capturing window.
if let Some(widget) = frame.get(target) {
if let Some(widget) = frame.get(target.widget_id()) {
if let Some(new_path) = widget.new_interaction_path(&InteractionPath::from_enabled(target.clone())) {
// widget moved inside window tree.
let mode = *mode;

View File

@ -397,7 +397,7 @@ impl FrameBuilder {
let patch = undo_prev.then(&outer_transform);
if patch != RenderTransform::identity() {
for info in ctx.info_tree.find(ctx.path.widget_id()).unwrap().self_and_descendants() {
for info in ctx.info_tree.get(ctx.path.widget_id()).unwrap().self_and_descendants() {
let bounds = info.bounds_info();
bounds.set_outer_transform(bounds.outer_transform().then(&patch));
bounds.set_inner_transform(bounds.inner_transform().then(&patch), ctx.info_tree);
@ -467,7 +467,7 @@ impl FrameBuilder {
/// [`Hidden`]: crate::widget_info::Visibility::Hidden
/// [`Collapsed`]: crate::widget_info::Visibility::Collapsed
pub fn skip_render(&self, info_tree: &WidgetInfoTree) {
if let Some(w) = info_tree.find(self.widget_id) {
if let Some(w) = info_tree.get(self.widget_id) {
w.bounds_info().set_rendered(self.widget_rendered);
for w in w.descendants() {
w.bounds_info().set_rendered(false);
@ -482,7 +482,7 @@ impl FrameBuilder {
/// Widgets that control the visibility of their children can call this and then, in the same frame, render
/// only the children that should be visible.
pub fn skip_render_descendants(&self, info_tree: &WidgetInfoTree) {
if let Some(w) = info_tree.find(self.widget_id) {
if let Some(w) = info_tree.get(self.widget_id) {
w.bounds_info().set_rendered(self.widget_rendered);
for w in w.descendants() {
w.bounds_info().set_rendered(false);
@ -1315,7 +1315,7 @@ impl FrameUpdate {
if let Some(undo_prev) = prev_outer.inverse() {
let patch = undo_prev.then(&outer_transform);
for info in ctx.info_tree.find(ctx.path.widget_id()).unwrap().self_and_descendants() {
for info in ctx.info_tree.get(ctx.path.widget_id()).unwrap().self_and_descendants() {
let bounds = info.bounds_info();
bounds.set_outer_transform(bounds.outer_transform().then(&patch));
bounds.set_inner_transform(bounds.inner_transform().then(&patch), ctx.info_tree);

View File

@ -230,7 +230,7 @@ pub fn default_no_child() {
);
wgt.test_info(&mut ctx, &mut info);
let (build_info, _) = info.finalize();
let wgt_info = build_info.find(wgt.id()).unwrap();
let wgt_info = build_info.get(wgt.id()).unwrap();
assert!(wgt_info.descendants().next().is_none());
assert!(wgt_info.meta().is_empty());

View File

@ -736,7 +736,7 @@ fn vis_enabled_eq_state(child: impl UiNode, state: StateVar, expected: bool) ->
event_state(child, state, true, WidgetInfoChangedEvent, move |ctx, _| {
let is_enabled = ctx
.info_tree
.find(ctx.path.widget_id())
.get(ctx.path.widget_id())
.unwrap()
.interactivity()
.is_visually_enabled();
@ -868,7 +868,7 @@ fn visibility_eq_state(child: impl UiNode, state: StateVar, expected: Visibility
move |ctx, _| {
let vis = ctx
.info_tree
.find(ctx.path.widget_id())
.get(ctx.path.widget_id())
.map(|w| w.visibility())
.unwrap_or(Visibility::Visible);

View File

@ -67,8 +67,8 @@ struct WidgetInfoTreeInner {
interactivity_filters: InteractivityFilters,
frame_id: Cell<Option<FrameId>>,
last_changed_frame: Cell<Option<FrameId>>,
bounds_changed: Cell<bool>, // temporary, if `true` will set `last_changed_frame`.
spatial_frame_id: Cell<Option<FrameId>>,
bounds_changed: Cell<bool>, // temporary, if `true` will set `spatial_frame_id`.
}
impl PartialEq for WidgetInfoTree {
fn eq(&self, other: &Self) -> bool {
@ -104,13 +104,13 @@ impl WidgetInfoTree {
self.0.frame_id.get()
}
/// Last window frame rendered after this tree updated that caused some widget bounds to change.
pub fn last_changed_frame(&self) -> Option<FrameId> {
self.0.last_changed_frame.get()
/// Last window frame rendered where some widget's where resized or moved.
pub fn spatial_frame_id(&self) -> Option<FrameId> {
self.0.spatial_frame_id.get()
}
/// Reference to the widget in the tree, if it is present.
pub fn find(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
self.0.lookup.get(&widget_id.into()).map(|i| WidgetInfo::new(self, *i))
}
@ -119,25 +119,10 @@ impl WidgetInfoTree {
self.0.lookup.contains_key(&widget_id.into())
}
/// Reference to the widget in the tree, if it is present.
///
/// Faster then [`find`](Self::find) if the widget path was generated by `self`.
pub fn get(&self, path: &WidgetPath) -> Option<WidgetInfo> {
if let Some(id) = path.node_id {
if let Some(n) = self.0.tree.get(id) {
if n.value().widget_id == path.widget_id() {
return Some(WidgetInfo::new(self, id));
}
}
}
self.find(path.widget_id())
}
/// Reference to the widget or first parent that is present.
pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetInfo> {
self.get(path)
.or_else(|| path.ancestors().iter().rev().find_map(|&id| self.find(id)))
self.get(path.widget_id())
.or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
}
/// If the widgets in this tree have been rendered at least once, after the first render the widget bounds info are always up-to-date
@ -530,7 +515,7 @@ impl WidgetInfoTree {
pub(crate) fn after_render(&self, frame_id: FrameId) {
self.0.frame_id.set(Some(frame_id));
if self.0.bounds_changed.take() {
self.0.last_changed_frame.set(Some(frame_id));
self.0.spatial_frame_id.set(Some(frame_id));
*self.0.inner_bounds_tree.borrow_mut() = None;
}
}
@ -1038,7 +1023,7 @@ impl<'a> WidgetInfo<'a> {
path.reverse();
path.push(self.widget_id());
WidgetPath::new_internal(self.tree.0.window_id, path.into(), self.node_id)
WidgetPath::new(self.tree.0.window_id, path)
}
/// Full path to this widget with [`interactivity`] values.
@ -1065,7 +1050,7 @@ impl<'a> WidgetInfo<'a> {
let len = path.len();
let path = WidgetPath::new_internal(self.tree.0.window_id, path.into(), self.node_id);
let path = WidgetPath::new(self.tree.0.window_id, path);
InteractionPath::new_internal(
path,
blocked.map(|i| len - i - 1).unwrap_or(len),
@ -1478,7 +1463,7 @@ impl<'a> WidgetInfo<'a> {
let a = self.path();
let b = other.path();
let shared = a.shared_ancestor(&b).unwrap();
self.tree.get(&*shared)
self.tree.get(shared.widget_id())
} else {
None
}

View File

@ -125,7 +125,7 @@ impl WidgetInfoBuilder {
let wgt = ctx
.info_tree
.find(widget_id)
.get(widget_id)
.unwrap_or_else(|| panic!("cannot reuse `{:?}`, not found in previous tree", ctx.path));
self.tree.index_mut(self.node).push_reuse(
@ -193,7 +193,7 @@ impl WidgetInfoBuilder {
interactivity_filters: self.interactivity_filters,
inner_bounds_tree: RefCell::new(Some(Rc::new(spatial::QuadTree::new()))),
frame_id: Cell::new(None),
last_changed_frame: Cell::new(None),
spatial_frame_id: Cell::new(None),
bounds_changed: Cell::new(true),
}));
@ -507,7 +507,7 @@ impl WidgetLayout {
self.known_collapsed = true;
let widget_id = ctx.path.widget_id();
if let Some(w) = ctx.info_tree.find(widget_id) {
if let Some(w) = ctx.info_tree.get(widget_id) {
for w in w.self_and_descendants() {
let info = w.info();
info.bounds_info.set_outer_size(PxSize::zero());
@ -531,7 +531,7 @@ impl WidgetLayout {
/// the children that should be visible.
pub fn collapse_descendants(&mut self, ctx: &mut LayoutContext) {
let widget_id = ctx.path.widget_id();
if let Some(w) = ctx.info_tree.find(widget_id) {
if let Some(w) = ctx.info_tree.get(widget_id) {
for w in w.descendants() {
let info = w.info();
info.bounds_info.set_outer_size(PxSize::zero());

View File

@ -992,8 +992,8 @@ mod tests {
fn self_and_next_siblings_in() {
let tree = data_nested();
let root = tree.find("c-1").unwrap();
let item = tree.find("c-1-1").unwrap();
let root = tree.get("c-1").unwrap();
let item = tree.get("c-1-1").unwrap();
let result: Vec<_> = item.self_and_next_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root
@ -1009,8 +1009,8 @@ mod tests {
fn self_and_prev_siblings_in() {
let tree = data_nested();
let root = tree.find("c-1").unwrap();
let item = tree.find("c-1-1").unwrap();
let root = tree.get("c-1").unwrap();
let item = tree.get("c-1-1").unwrap();
let result: Vec<_> = item.self_and_prev_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root
@ -1028,7 +1028,7 @@ mod tests {
let tree = data_nested();
let root = tree.root();
let item = tree.find("c-1-1").unwrap();
let item = tree.get("c-1-1").unwrap();
let result: Vec<_> = item.self_and_next_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root
@ -1045,7 +1045,7 @@ mod tests {
let tree = data_nested();
let root = tree.root();
let item = tree.find("c-1-1").unwrap();
let item = tree.get("c-1-1").unwrap();
let result: Vec<_> = item.self_and_prev_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root
@ -1063,7 +1063,7 @@ mod tests {
let tree = data_nested();
let root = tree.root();
let item = tree.find("c-1-1-0").unwrap();
let item = tree.get("c-1-1-0").unwrap();
let result: Vec<_> = item.next_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root
@ -1081,7 +1081,7 @@ mod tests {
let tree = data_nested();
let root = tree.root();
let item = tree.find("c-1-1-0").unwrap();
let item = tree.get("c-1-1-0").unwrap();
let result: Vec<_> = item.prev_siblings_in(root).map(|w| w.test_name()).collect();
let expected: Vec<_> = root

View File

@ -1,9 +1,8 @@
use super::*;
/// Full address of a widget in a specific [`WidgetInfoTree`].
/// Full address of a widget.
#[derive(Clone)]
pub struct WidgetPath {
pub(super) node_id: Option<tree::NodeId>,
window_id: WindowId,
path: Box<[WidgetId]>,
}
@ -41,20 +40,11 @@ impl fmt::Display for WidgetPath {
}
}
impl WidgetPath {
pub(super) fn new_internal(window_id: WindowId, path: Box<[WidgetId]>, node_id: tree::NodeId) -> Self {
Self {
node_id: Some(node_id),
window_id,
path,
}
}
/// New custom widget path.
///
/// The path is not guaranteed to have ever existed.
pub fn new<P: Into<Box<[WidgetId]>>>(window_id: WindowId, path: P) -> WidgetPath {
WidgetPath {
node_id: None,
window_id,
path: path.into(),
}
@ -92,7 +82,6 @@ impl WidgetPath {
Cow::Borrowed(self)
} else {
Cow::Owned(WidgetPath {
node_id: None,
window_id: self.window_id,
path: self.path[..i].to_vec().into_boxed_slice(),
})
@ -109,7 +98,6 @@ impl WidgetPath {
} else {
let path = self.path[..i].to_vec().into_boxed_slice();
Some(Cow::Owned(WidgetPath {
node_id: None,
window_id: self.window_id,
path,
}))
@ -130,7 +118,6 @@ impl WidgetPath {
Cow::Borrowed(self)
} else {
Cow::Owned(WidgetPath {
node_id: None,
window_id: self.window_id,
path: Box::new([self.path[0]]),
})
@ -326,7 +313,6 @@ impl InteractionPath {
let blocked = self.blocked - 1;
Some(InteractionPath {
path: WidgetPath {
node_id: None,
window_id: self.path.window_id,
path: self.path.path[blocked..].to_vec().into_boxed_slice(),
},
@ -349,7 +335,6 @@ impl InteractionPath {
return None;
}
Some(WidgetPath {
node_id: None,
window_id: self.path.window_id,
path: self.path.path[..enabled_end].to_vec().into_boxed_slice(),
})
@ -366,7 +351,6 @@ impl InteractionPath {
} else {
Cow::Owned(InteractionPath {
path: WidgetPath {
node_id: None,
window_id: self.window_id,
path: self.path.path[..i].to_vec().into_boxed_slice(),
},
@ -387,7 +371,6 @@ impl InteractionPath {
let path = self.path.path[..i].to_vec().into_boxed_slice();
Some(Cow::Owned(InteractionPath {
path: WidgetPath {
node_id: None,
window_id: self.window_id,
path,
},
@ -412,7 +395,6 @@ impl InteractionPath {
} else {
Cow::Owned(InteractionPath {
path: WidgetPath {
node_id: None,
window_id: self.window_id,
path: Box::new([self.path.path[0]]),
},

View File

@ -44,14 +44,6 @@ impl<T> Tree<T> {
NodeRef { tree: self, id }
}
pub fn get(&self, id: NodeId) -> Option<NodeRef<T>> {
if self.nodes.len() < id.get() {
Some(NodeRef { tree: self, id })
} else {
None
}
}
pub fn index_mut(&mut self, id: NodeId) -> NodeMut<T> {
#[cfg(debug_assertions)]
let _ = self.nodes[id.get()];

View File

@ -71,7 +71,7 @@ pub fn capture_mouse(child: impl UiNode, mode: impl IntoVar<CaptureMode>) -> imp
if let Some(new_mode) = self.mode.copy_new(ctx.vars) {
if ctx
.info_tree
.find(ctx.path.widget_id())
.get(ctx.path.widget_id())
.map(|w| w.interactivity().is_enabled())
.unwrap_or(false)
{

View File

@ -309,7 +309,7 @@ pub fn show_directional_query(child: impl UiNode, orientation: impl IntoVar<Opti
let mut none = true;
if let Some(target) = &args.target {
for w_id in target.widgets_path().iter().rev() {
if let Some(w) = ctx.info_tree.find(*w_id) {
if let Some(w) = ctx.info_tree.get(*w_id) {
if let Some(w) = w.as_focusable(true) {
let search_quads: Vec<_> = orientation
.search_bounds(w.info.center(), Px::MAX, ctx.info_tree.spatial_bounds())

View File

@ -654,7 +654,7 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
if let Some(path) = &args.new_focus {
if path.contains(self_id) && path.widget_id() != self_id {
// probable focus move inside.
if let Some(target) = ctx.info_tree.get(path) {
if let Some(target) = ctx.info_tree.get(path.widget_id()) {
// target exits
if let Some(us) = target.ancestors().find(|w| w.widget_id() == self_id) {
// confirmed, target is descendant
@ -677,7 +677,7 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
// event send to us and enabled
if let Some(request) = ScrollToRequest::from_args(args) {
// has unhandled request
if let Some(target) = ctx.info_tree.find(request.widget_id) {
if let Some(target) = ctx.info_tree.get(request.widget_id) {
// target exists
if let Some(us) = target.ancestors().find(|w| w.widget_id() == self_id) {
// target is descendant
@ -709,7 +709,7 @@ pub fn scroll_to_node(child: impl UiNode) -> impl UiNode {
let r = self.child.layout(ctx, wl);
if let Some((bounds, mode)) = self.scroll_to.take() {
let us = ctx.info_tree.find(ctx.path.widget_id()).unwrap();
let us = ctx.info_tree.get(ctx.path.widget_id()).unwrap();
if let Some(viewport_bounds) = us.viewport() {
let target_bounds = bounds.inner_bounds();
match mode {

View File

@ -141,7 +141,7 @@ impl WindowLayers {
let _q = RunOnDrop::new(|| querying.set(false));
args.info
.tree()
.find(anchor)
.get(anchor)
.map(|a| a.interactivity())
.unwrap_or(Interactivity::BLOCKED)
} else {
@ -153,7 +153,7 @@ impl WindowLayers {
}
fn init(&mut self, ctx: &mut WidgetContext) {
if let Some(w) = ctx.info_tree.find(self.anchor.copy(ctx.vars)) {
if let Some(w) = ctx.info_tree.get(self.anchor.copy(ctx.vars)) {
self.anchor_info = Some((w.bounds_info(), w.border_info()));
}
@ -172,7 +172,7 @@ impl WindowLayers {
if args.window_id == ctx.path.window_id() {
self.anchor_info = ctx
.info_tree
.find(self.anchor.copy(ctx.vars))
.get(self.anchor.copy(ctx.vars))
.map(|w| (w.bounds_info(), w.border_info()));
}
self.widget.event(ctx, args);
@ -183,7 +183,7 @@ impl WindowLayers {
fn update(&mut self, ctx: &mut WidgetContext) {
if let Some(anchor) = self.anchor.copy_new(ctx) {
self.anchor_info = ctx.info_tree.find(anchor).map(|w| (w.bounds_info(), w.border_info()));
self.anchor_info = ctx.info_tree.get(anchor).map(|w| (w.bounds_info(), w.border_info()));
if self.mode.get(ctx).interaction {
ctx.updates.info();
}