use std::fmt;
use std::sync::atomic::Ordering::Relaxed;
use atomic::Atomic;
use parking_lot::Mutex;
use zng_app::{
widget::{
WidgetId,
info::{TreeFilter, Visibility, WeakWidgetInfoTree, WidgetInfo, WidgetInfoBuilder, WidgetInfoTree, WidgetPath},
},
window::WindowId,
};
use zng_ext_window::NestedWindowWidgetInfoExt;
use zng_layout::unit::{DistanceKey, Orientation2D, Px, PxBox, PxPoint, PxRect, PxSize};
use zng_state_map::{StateId, static_id};
use zng_unique_id::IdSet;
use zng_var::impl_from_and_into_var;
use zng_view_api::window::FocusIndicator;
use zng_app::widget::info::iter as w_iter;
use super::iter::IterFocusableExt;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct TabIndex(pub u32);
impl TabIndex {
pub const SKIP: TabIndex = TabIndex(u32::MAX);
pub const AUTO: TabIndex = TabIndex(u32::MAX / 2);
pub const LAST: TabIndex = TabIndex(u32::MAX - 1);
pub fn is_skip(self) -> bool {
self == Self::SKIP
}
pub fn is_auto(self) -> bool {
self == Self::AUTO
}
pub fn is_before_auto(self) -> bool {
self.0 < Self::AUTO.0
}
pub fn is_after_auto(self) -> bool {
self.0 > Self::AUTO.0
}
pub fn not_skip(index: u32) -> Self {
TabIndex(if index == Self::SKIP.0 { Self::SKIP.0 - 1 } else { index })
}
pub fn before_auto(index: u32) -> Self {
TabIndex(if index >= Self::AUTO.0 { Self::AUTO.0 - 1 } else { index })
}
pub fn after_auto(index: u32) -> Self {
Self::not_skip((Self::AUTO.0 + 1).saturating_add(index))
}
}
impl fmt::Debug for TabIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
if self.is_auto() {
write!(f, "TabIndex::AUTO")
} else if self.is_skip() {
write!(f, "TabIndex::SKIP")
} else if self.is_after_auto() {
write!(f, "TabIndex::after_auto({})", self.0 - Self::AUTO.0 - 1)
} else {
write!(f, "TabIndex({})", self.0)
}
} else {
if self.is_auto() {
write!(f, "AUTO")
} else if self.is_skip() {
write!(f, "SKIP")
} else if self.is_after_auto() {
write!(f, "after_auto({})", self.0 - Self::AUTO.0 - 1)
} else {
write!(f, "{}", self.0)
}
}
}
}
impl Default for TabIndex {
fn default() -> Self {
TabIndex::AUTO
}
}
impl_from_and_into_var! {
fn from(index: u32) -> TabIndex {
TabIndex::not_skip(index)
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum TabIndexSerde<'s> {
Named(&'s str),
Unnamed(u32),
}
impl serde::Serialize for TabIndex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let name = if self.is_auto() {
Some("AUTO")
} else if self.is_skip() {
Some("SKIP")
} else {
None
};
if let Some(name) = name {
return TabIndexSerde::Named(name).serialize(serializer);
}
}
TabIndexSerde::Unnamed(self.0).serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for TabIndex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
match TabIndexSerde::deserialize(deserializer)? {
TabIndexSerde::Named(name) => match name {
"AUTO" => Ok(TabIndex::AUTO),
"SKIP" => Ok(TabIndex::SKIP),
unknown => Err(D::Error::unknown_variant(unknown, &["AUTO", "SKIP"])),
},
TabIndexSerde::Unnamed(i) => Ok(TabIndex(i)),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum TabNav {
None,
Continue,
Contained,
Cycle,
Once,
}
impl fmt::Debug for TabNav {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "TabNav::")?;
}
match self {
TabNav::None => write!(f, "None"),
TabNav::Continue => write!(f, "Continue"),
TabNav::Contained => write!(f, "Contained"),
TabNav::Cycle => write!(f, "Cycle"),
TabNav::Once => write!(f, "Once"),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum DirectionalNav {
None,
Continue,
Contained,
Cycle,
}
impl fmt::Debug for DirectionalNav {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "DirectionalNav::")?;
}
match self {
DirectionalNav::None => write!(f, "None"),
DirectionalNav::Continue => write!(f, "Continue"),
DirectionalNav::Contained => write!(f, "Contained"),
DirectionalNav::Cycle => write!(f, "Cycle"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FocusRequest {
pub target: FocusTarget,
pub highlight: bool,
pub force_window_focus: bool,
pub window_indicator: Option<FocusIndicator>,
pub fallback_only: bool,
}
impl FocusRequest {
pub fn new(target: FocusTarget, highlight: bool) -> Self {
Self {
target,
highlight,
force_window_focus: false,
window_indicator: None,
fallback_only: false,
}
}
pub fn direct(widget_id: WidgetId, highlight: bool) -> Self {
Self::new(FocusTarget::Direct { target: widget_id }, highlight)
}
pub fn direct_or_exit(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
Self::new(
FocusTarget::DirectOrExit {
target: widget_id,
navigation_origin,
},
highlight,
)
}
pub fn direct_or_enter(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
Self::new(
FocusTarget::DirectOrEnter {
target: widget_id,
navigation_origin,
},
highlight,
)
}
pub fn direct_or_related(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
Self::new(
FocusTarget::DirectOrRelated {
target: widget_id,
navigation_origin,
},
highlight,
)
}
pub fn enter(highlight: bool) -> Self {
Self::new(FocusTarget::Enter, highlight)
}
pub fn exit(recursive_alt: bool, highlight: bool) -> Self {
Self::new(FocusTarget::Exit { recursive_alt }, highlight)
}
pub fn next(highlight: bool) -> Self {
Self::new(FocusTarget::Next, highlight)
}
pub fn prev(highlight: bool) -> Self {
Self::new(FocusTarget::Prev, highlight)
}
pub fn up(highlight: bool) -> Self {
Self::new(FocusTarget::Up, highlight)
}
pub fn right(highlight: bool) -> Self {
Self::new(FocusTarget::Right, highlight)
}
pub fn down(highlight: bool) -> Self {
Self::new(FocusTarget::Down, highlight)
}
pub fn left(highlight: bool) -> Self {
Self::new(FocusTarget::Left, highlight)
}
pub fn alt(highlight: bool) -> Self {
Self::new(FocusTarget::Alt, highlight)
}
pub fn with_force_window_focus(mut self) -> Self {
self.force_window_focus = true;
self
}
pub fn with_indicator(mut self, indicator: FocusIndicator) -> Self {
self.window_indicator = Some(indicator);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum FocusTarget {
Direct {
target: WidgetId,
},
DirectOrExit {
target: WidgetId,
navigation_origin: bool,
},
DirectOrEnter {
target: WidgetId,
navigation_origin: bool,
},
DirectOrRelated {
target: WidgetId,
navigation_origin: bool,
},
Enter,
Exit {
recursive_alt: bool,
},
Next,
Prev,
Up,
Right,
Down,
Left,
Alt,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FocusNavAction: u16 {
const ENTER = 0b0000_0000_0001;
const EXIT = 0b0000_0000_0010;
const NEXT = 0b0000_0000_0100;
const PREV = 0b0000_0000_1000;
const UP = 0b0000_0001_0000;
const RIGHT = 0b0000_0010_0000;
const DOWN = 0b0000_0100_0000;
const LEFT = 0b0000_1000_0000;
const ALT = 0b0001_0000_0000;
const DIRECTIONAL =
FocusNavAction::UP.bits() | FocusNavAction::RIGHT.bits() | FocusNavAction::DOWN.bits() | FocusNavAction::LEFT.bits();
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(super) struct FocusMode: u8 {
const DISABLED = 1;
const HIDDEN = 2;
}
}
impl FocusMode {
pub fn new(focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
let mut mode = FocusMode::empty();
mode.set(FocusMode::DISABLED, focus_disabled_widgets);
mode.set(FocusMode::HIDDEN, focus_hidden_widgets);
mode
}
}
#[derive(Clone, Debug)]
pub struct FocusInfoTree {
tree: WidgetInfoTree,
mode: FocusMode,
}
impl FocusInfoTree {
pub fn new(tree: WidgetInfoTree, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
FocusInfoTree {
tree,
mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
}
}
pub fn tree(&self) -> &WidgetInfoTree {
&self.tree
}
pub fn focus_disabled_widgets(&self) -> bool {
self.mode.contains(FocusMode::DISABLED)
}
pub fn focus_hidden_widgets(&self) -> bool {
self.mode.contains(FocusMode::HIDDEN)
}
pub fn root(&self) -> WidgetFocusInfo {
WidgetFocusInfo {
info: self.tree.root(),
mode: self.mode,
}
}
pub fn focusable_root(&self) -> Option<WidgetFocusInfo> {
let root = self.root();
if root.is_focusable() {
return Some(root);
}
let mut candidate = None;
let mut candidate_weight = usize::MAX;
for w in root.descendants().tree_filter(|_| TreeFilter::SkipDescendants) {
let weight = w.info.prev_siblings().count() + w.info.ancestors().count();
if weight < candidate_weight {
candidate = Some(w);
candidate_weight = weight;
}
}
candidate
}
pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
self.tree
.get(widget_id)
.and_then(|i| i.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
}
pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
self.get(path.widget_id())
.or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
}
pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
self.get(widget_id).is_some()
}
}
pub trait WidgetInfoFocusExt {
fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo;
fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo>;
}
impl WidgetInfoFocusExt for WidgetInfo {
fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo {
WidgetFocusInfo::new(self, focus_disabled_widgets, focus_hidden_widgets)
}
fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo> {
let r = self.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
if r.is_focusable() { Some(r) } else { None }
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct WidgetFocusInfo {
info: WidgetInfo,
mode: FocusMode,
}
impl WidgetFocusInfo {
pub fn new(widget_info: WidgetInfo, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
WidgetFocusInfo {
info: widget_info,
mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
}
}
pub fn info(&self) -> &WidgetInfo {
&self.info
}
pub fn focus_disabled_widgets(&self) -> bool {
self.mode.contains(FocusMode::DISABLED)
}
pub fn focus_hidden_widgets(&self) -> bool {
self.mode.contains(FocusMode::HIDDEN)
}
pub fn root(&self) -> Self {
self.ancestors().last().unwrap_or_else(|| self.clone())
}
pub fn focus_tree(&self) -> FocusInfoTree {
FocusInfoTree {
tree: self.info.tree().clone(),
mode: self.mode,
}
}
pub fn is_focusable(&self) -> bool {
self.focus_info().is_focusable()
}
pub fn is_scope(&self) -> bool {
self.focus_info().is_scope()
}
pub fn is_alt_scope(&self) -> bool {
self.focus_info().is_alt_scope()
}
pub fn nested_window(&self) -> Option<WindowId> {
self.info.nested_window()
}
pub fn nested_window_tree(&self) -> Option<FocusInfoTree> {
self.info
.nested_window_tree()
.map(|t| FocusInfoTree::new(t, self.focus_disabled_widgets(), self.focus_hidden_widgets()))
}
fn mode_allows_focus(&self) -> bool {
let int = self.info.interactivity();
if self.mode.contains(FocusMode::DISABLED) {
if int.is_blocked() {
return false;
}
} else if !int.is_enabled() {
return false;
}
let vis = self.info.visibility();
if self.mode.contains(FocusMode::HIDDEN) {
if vis == Visibility::Collapsed {
return false;
}
} else if vis != Visibility::Visible {
return false;
}
true
}
fn mode_allows_focus_ignore_blocked(&self) -> bool {
let int = self.info.interactivity();
if !self.mode.contains(FocusMode::DISABLED) && int.is_vis_disabled() {
return false;
}
let vis = self.info.visibility();
if self.mode.contains(FocusMode::HIDDEN) {
if vis == Visibility::Collapsed {
return false;
}
} else if vis != Visibility::Visible {
return false;
}
true
}
pub fn focus_info(&self) -> FocusInfo {
if self.mode_allows_focus() {
if let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID) {
return builder.build();
} else if self.info.nested_window().is_some() {
return FocusInfo::FocusScope {
tab_index: TabIndex::AUTO,
skip_directional: false,
tab_nav: TabNav::Contained,
directional_nav: DirectionalNav::Contained,
on_focus: FocusScopeOnFocus::FirstDescendant,
alt: false,
};
}
}
FocusInfo::NotFocusable
}
pub fn focus_info_ignore_blocked(&self) -> FocusInfo {
if self.mode_allows_focus_ignore_blocked()
&& let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID)
{
return builder.build();
}
FocusInfo::NotFocusable
}
pub fn ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
let focus_disabled_widgets = self.focus_disabled_widgets();
let focus_hidden_widgets = self.focus_hidden_widgets();
self.info.ancestors().focusable(focus_disabled_widgets, focus_hidden_widgets)
}
pub fn self_and_ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
[self.clone()].into_iter().chain(self.ancestors())
}
pub fn scopes(&self) -> impl Iterator<Item = WidgetFocusInfo> {
let focus_disabled_widgets = self.focus_disabled_widgets();
let focus_hidden_widgets = self.focus_hidden_widgets();
self.info.ancestors().filter_map(move |i| {
let i = i.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
if i.is_scope() { Some(i) } else { None }
})
}
pub fn parent(&self) -> Option<WidgetFocusInfo> {
self.ancestors().next()
}
pub fn scope(&self) -> Option<WidgetFocusInfo> {
self.scopes().next()
}
pub fn alt_scope(&self) -> Option<WidgetFocusInfo> {
if self.in_alt_scope() {
let mut alt_scope = self.clone();
for scope in self.scopes() {
if scope.is_alt_scope() {
alt_scope = scope;
} else {
return scope.inner_alt_scope_skip(&alt_scope);
}
}
return None;
}
if self.is_scope() {
let r = self.inner_alt_scope();
if r.is_some() {
return r;
}
}
let mut skip = self.clone();
for scope in self.scopes() {
let r = scope.inner_alt_scope_skip(&skip);
if r.is_some() {
return r;
}
skip = scope;
}
None
}
fn inner_alt_scope(&self) -> Option<WidgetFocusInfo> {
let inner_alt = self.info.meta().get(*FOCUS_INFO_ID)?.inner_alt.load(Relaxed);
if let Some(id) = inner_alt
&& let Some(wgt) = self.info.tree().get(id)
{
let wgt = wgt.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
if wgt.is_alt_scope() && wgt.info.is_descendant(&self.info) {
return Some(wgt);
}
}
None
}
fn inner_alt_scope_skip(&self, skip: &WidgetFocusInfo) -> Option<WidgetFocusInfo> {
if let Some(alt) = self.inner_alt_scope()
&& !alt.info.is_descendant(&skip.info)
&& alt.info != skip.info
{
return Some(alt);
}
None
}
pub fn in_alt_scope(&self) -> bool {
self.is_alt_scope() || self.scopes().any(|s| s.is_alt_scope())
}
pub fn on_focus_scope_move(
&self,
last_focused: impl FnOnce(WidgetId) -> Option<WidgetId>,
is_tab_cycle_reentry: bool,
reverse: bool,
) -> Option<WidgetFocusInfo> {
match self.focus_info() {
FocusInfo::FocusScope { on_focus, .. } => {
let candidate = match on_focus {
FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::FirstDescendantIgnoreBounds => {
if reverse {
self.last_tab_descendant()
} else {
self.first_tab_descendant()
}
}
FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds => {
if is_tab_cycle_reentry { None } else { last_focused(self.info.id()) }
.and_then(|id| self.info.tree().get(id))
.and_then(|w| w.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
.and_then(|f| {
if f.info.is_descendant(&self.info) {
Some(f) } else {
None
}
})
.or_else(|| {
if reverse {
self.last_tab_descendant()
} else {
self.first_tab_descendant()
}
})
} FocusScopeOnFocus::Widget => None,
};
if let FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::LastFocused = on_focus
&& let Some(candidate) = &candidate
&& !self.info.inner_bounds().contains_rect(&candidate.info().inner_bounds())
{
return None;
}
candidate
}
FocusInfo::NotFocusable | FocusInfo::Focusable { .. } => None,
}
}
pub fn descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
super::iter::FocusTreeIter::new(self.info.descendants(), self.mode)
}
pub fn self_and_descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
super::iter::FocusTreeIter::new(self.info.self_and_descendants(), self.mode)
}
pub fn has_tab_descendant(&self) -> bool {
self.descendants().tree_find(Self::filter_tab_skip).is_some()
}
pub fn first_tab_descendant(&self) -> Option<WidgetFocusInfo> {
let mut best = (TabIndex::SKIP, self.clone());
for d in self.descendants().tree_filter(Self::filter_tab_skip) {
let idx = d.focus_info().tab_index();
if idx < best.0 {
best = (idx, d);
}
}
if best.0.is_skip() { None } else { Some(best.1) }
}
pub fn last_tab_descendant(&self) -> Option<WidgetFocusInfo> {
let mut best = (-1i64, self.clone());
for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
let idx = d.focus_info().tab_index().0 as i64;
if idx > best.0 {
best = (idx, d);
}
}
if best.0 < 0 { None } else { Some(best.1) }
}
pub fn next_focusables(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
if let Some(scope) = self.scope() {
super::iter::FocusTreeIter::new(self.info.next_siblings_in(&scope.info), self.mode)
} else {
super::iter::FocusTreeIter::new(self.info.next_siblings_in(&self.info), self.mode)
}
}
pub fn next_focusable(&self) -> Option<WidgetFocusInfo> {
self.next_focusables().next()
}
fn filter_tab_skip(w: &WidgetFocusInfo) -> TreeFilter {
if w.focus_info().tab_index().is_skip() {
TreeFilter::SkipAll
} else {
TreeFilter::Include
}
}
pub fn next_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
self.next_tab_focusable_impl(skip_self, false)
}
fn next_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
let self_index = self.focus_info().tab_index();
if self_index == TabIndex::SKIP {
return self.next_focusables().tree_find(Self::filter_tab_skip);
}
let mut best = (TabIndex::SKIP, self.clone());
if !skip_self {
for d in self.descendants().tree_filter(Self::filter_tab_skip) {
let idx = d.focus_info().tab_index();
if idx == self_index {
return Some(d);
} else if idx < best.0 && idx > self_index {
if any {
return Some(d);
}
best = (idx, d);
}
}
}
for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
let idx = s.focus_info().tab_index();
if idx == self_index {
return Some(s);
} else if idx < best.0 && idx > self_index {
if any {
return Some(s);
}
best = (idx, s);
}
}
for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
let idx = s.focus_info().tab_index();
if idx <= best.0 && idx > self_index {
if any {
return Some(s);
}
best = (idx, s);
}
}
if best.0.is_skip() { None } else { Some(best.1) }
}
pub fn prev_focusables(&self) -> super::iter::FocusTreeIter<w_iter::RevTreeIter> {
if let Some(scope) = self.scope() {
super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&scope.info), self.mode)
} else {
super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&self.info), self.mode)
}
}
pub fn prev_focusable(&self) -> Option<WidgetFocusInfo> {
self.prev_focusables().next()
}
pub fn prev_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
self.prev_tab_focusable_impl(skip_self, false)
}
fn prev_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
let self_index = self.focus_info().tab_index();
if self_index == TabIndex::SKIP {
return self.prev_focusables().tree_find(Self::filter_tab_skip);
}
let self_index = self_index.0 as i64;
let mut best = (-1i64, self.clone());
if !skip_self {
for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
let idx = d.focus_info().tab_index().0 as i64;
if idx == self_index {
return Some(d);
} else if idx > best.0 && idx < self_index {
if any {
return Some(d);
}
best = (idx, d);
}
}
}
for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
let idx = s.focus_info().tab_index().0 as i64;
if idx == self_index {
return Some(s);
} else if idx > best.0 && idx < self_index {
if any {
return Some(s);
}
best = (idx, s);
}
}
for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
let idx = s.focus_info().tab_index().0 as i64;
if idx >= best.0 && idx < self_index {
if any {
return Some(s);
}
best = (idx, s);
}
}
if best.0 < 0 { None } else { Some(best.1) }
}
pub fn next_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("next_tab").entered();
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.tab_nav() {
TabNav::None => None,
TabNav::Continue => self.next_tab_focusable(skip_self).or_else(|| scope.next_tab(true)),
TabNav::Contained => self.next_tab_focusable(skip_self),
TabNav::Cycle => self.next_tab_focusable(skip_self).or_else(|| scope.first_tab_descendant()),
TabNav::Once => scope.next_tab(true),
}
} else {
None
}
}
pub fn prev_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("prev_tab").entered();
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.tab_nav() {
TabNav::None => None,
TabNav::Continue => self.prev_tab_focusable(skip_self).or_else(|| scope.prev_tab(true)),
TabNav::Contained => self.prev_tab_focusable(skip_self),
TabNav::Cycle => self.prev_tab_focusable(skip_self).or_else(|| scope.last_tab_descendant()),
TabNav::Once => scope.prev_tab(true),
}
} else {
None
}
}
pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetFocusInfo> {
let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
self.info
.nearest_filtered(origin, max_radius, |w| cast(w.clone()).is_focusable())
.map(cast)
}
pub fn nearest_filtered(
&self,
origin: PxPoint,
max_radius: Px,
mut filter: impl FnMut(WidgetFocusInfo) -> bool,
) -> Option<WidgetFocusInfo> {
let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
self.info
.nearest_filtered(origin, max_radius, |w| {
let w = cast(w.clone());
w.is_focusable() && filter(w)
})
.map(cast)
}
pub fn nearest_bounded_filtered(
&self,
origin: PxPoint,
max_radius: Px,
bounds: PxRect,
mut filter: impl FnMut(WidgetFocusInfo) -> bool,
) -> Option<WidgetFocusInfo> {
let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
self.info
.nearest_bounded_filtered(origin, max_radius, bounds, move |w| {
let w = cast(w.clone());
w.is_focusable() && filter(w)
})
.map(cast)
}
pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
self.info
.nearest_oriented_filtered(origin, max_distance, orientation, |w| cast(w.clone()).is_focusable())
.map(cast)
}
pub fn nearest_oriented_filtered(
&self,
origin: PxPoint,
max_distance: Px,
orientation: Orientation2D,
mut filter: impl FnMut(WidgetFocusInfo) -> bool,
) -> Option<WidgetFocusInfo> {
let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
self.info
.nearest_oriented_filtered(origin, max_distance, orientation, |w| {
let w = cast(w.clone());
w.is_focusable() && filter(w)
})
.map(cast)
}
fn directional_from(
&self,
scope: &WidgetFocusInfo,
origin: PxBox,
orientation: Orientation2D,
skip_self: bool,
any: bool,
) -> Option<WidgetFocusInfo> {
let self_id = self.info.id();
let scope_id = scope.info.id();
let skip_parent = if self.is_focusable() {
None
} else {
self.ancestors().next().map(|w| w.info.id())
};
let filter = |w: &WidgetFocusInfo| {
let mut up_to_scope = w.self_and_ancestors().take_while(|w| w.info.id() != scope_id);
if skip_self {
up_to_scope.all(|w| w.info.id() != self_id && !w.focus_info().skip_directional())
} else {
up_to_scope.all(|w| !w.focus_info().skip_directional())
}
};
let origin_center = origin.center();
let mut oriented = scope
.info
.oriented(origin_center, Px::MAX, orientation)
.chain(
scope
.info
.oriented_box(origin, origin.width().max(origin.height()) * Px(2), orientation)
.filter(|w| !w.inner_bounds().to_box2d().intersects(&origin)),
)
.focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets())
.filter(|w| w.info.id() != scope_id && Some(w.info.id()) != skip_parent);
if any {
return oriented.find(filter);
}
let parent_range = self.parent().map(|w| w.info.descendants_range()).unwrap_or_default();
let mut ancestor_dist = DistanceKey::NONE_MAX;
let mut ancestor = None;
let mut sibling_dist = DistanceKey::NONE_MAX;
let mut sibling = None;
let mut other_dist = DistanceKey::NONE_MAX;
let mut other = None;
for w in oriented {
if filter(&w) {
let dist = w.info.distance_key(origin_center);
let mut is_ancestor = None;
let mut is_ancestor = || *is_ancestor.get_or_insert_with(|| w.info.is_ancestor(&self.info));
let mut is_sibling = None;
let mut is_sibling = || *is_sibling.get_or_insert_with(|| parent_range.contains(&w.info));
if dist <= ancestor_dist && is_ancestor() {
ancestor_dist = dist;
ancestor = Some(w);
} else if dist <= sibling_dist && is_sibling() {
sibling_dist = dist;
sibling = Some(w);
} else if dist <= other_dist && !is_ancestor() && !is_sibling() {
other_dist = dist;
other = Some(w);
}
}
}
if other_dist <= ancestor_dist && other_dist <= sibling_dist {
other
} else {
sibling.or(ancestor)
}
}
fn directional_next(&self, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
self.directional_next_from(orientation, self.info.inner_bounds().to_box2d())
}
fn directional_next_from(&self, orientation: Orientation2D, from: PxBox) -> Option<WidgetFocusInfo> {
self.scope()
.and_then(|s| self.directional_from(&s, from, orientation, false, false))
}
pub fn focusable_up(&self) -> Option<WidgetFocusInfo> {
self.directional_next(Orientation2D::Above)
}
pub fn focusable_down(&self) -> Option<WidgetFocusInfo> {
self.directional_next(Orientation2D::Below)
}
pub fn focusable_left(&self) -> Option<WidgetFocusInfo> {
self.directional_next(Orientation2D::Left)
}
pub fn focusable_right(&self) -> Option<WidgetFocusInfo> {
self.directional_next(Orientation2D::Right)
}
pub fn next_up(&self) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("next_up").entered();
self.next_up_from(self.info.inner_bounds().to_box2d())
}
fn next_up_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.directional_nav() {
DirectionalNav::None => None,
DirectionalNav::Continue => self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
let mut from = scope.info.inner_bounds();
from.origin.y -= Px(1);
from.size.height = Px(1);
scope.next_up_from(from.to_box2d())
}),
DirectionalNav::Contained => self.directional_next_from(Orientation2D::Above, origin),
DirectionalNav::Cycle => {
self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
let mut from_pt = origin.center();
from_pt.y = scope.info.spatial_bounds().max.y;
self.directional_from(
&scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Above,
false,
false,
)
})
}
}
} else {
None
}
}
pub fn next_right(&self) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("next_right").entered();
self.next_right_from(self.info.inner_bounds().to_box2d())
}
fn next_right_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.directional_nav() {
DirectionalNav::None => None,
DirectionalNav::Continue => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
let mut from = scope.info.inner_bounds();
from.origin.x += from.size.width + Px(1);
from.size.width = Px(1);
scope.next_right_from(from.to_box2d())
}),
DirectionalNav::Contained => self.directional_next_from(Orientation2D::Right, origin),
DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
let mut from_pt = origin.center();
from_pt.x = scope.info.spatial_bounds().min.x;
self.directional_from(
&scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Right,
false,
false,
)
}),
}
} else {
None
}
}
pub fn next_down(&self) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("next_down").entered();
self.next_down_from(self.info.inner_bounds().to_box2d())
}
fn next_down_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.directional_nav() {
DirectionalNav::None => None,
DirectionalNav::Continue => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
let mut from = scope.info.inner_bounds();
from.origin.y += from.size.height + Px(1);
from.size.height = Px(1);
scope.next_down_from(from.to_box2d())
}),
DirectionalNav::Contained => self.directional_next_from(Orientation2D::Below, origin),
DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
let mut from_pt = origin.center();
from_pt.y = scope.info.spatial_bounds().min.y;
self.directional_from(
&scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Below,
false,
false,
)
}),
}
} else {
None
}
}
pub fn next_left(&self) -> Option<WidgetFocusInfo> {
let _span = tracing::trace_span!("next_left").entered();
self.next_left_from(self.info.inner_bounds().to_box2d())
}
fn next_left_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
if let Some(scope) = self.scope() {
let scope_info = scope.focus_info();
match scope_info.directional_nav() {
DirectionalNav::None => None,
DirectionalNav::Continue => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
let mut from = scope.info.inner_bounds();
from.origin.x -= Px(1);
from.size.width = Px(1);
scope.next_left_from(from.to_box2d())
}),
DirectionalNav::Contained => self.directional_next_from(Orientation2D::Left, origin),
DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
let mut from_pt = origin.center();
from_pt.x = scope.info.spatial_bounds().max.x;
self.directional_from(
&scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Left,
false,
false,
)
}),
}
} else {
None
}
}
fn enabled_tab_nav(
&self,
scope: &WidgetFocusInfo,
scope_info: FocusInfo,
skip_self: bool,
already_found: FocusNavAction,
) -> FocusNavAction {
match scope_info.tab_nav() {
TabNav::None => FocusNavAction::empty(),
tab_nav @ (TabNav::Continue | TabNav::Contained) => {
let mut nav = already_found;
if !nav.contains(FocusNavAction::PREV) && self.prev_tab_focusable_impl(skip_self, true).is_some() {
nav |= FocusNavAction::PREV;
}
if !nav.contains(FocusNavAction::NEXT) && self.next_tab_focusable_impl(skip_self, true).is_some() {
nav |= FocusNavAction::NEXT;
}
if !nav.contains(FocusNavAction::PREV | FocusNavAction::NEXT)
&& tab_nav == TabNav::Continue
&& let Some(p_scope) = scope.scope()
{
nav |= scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, nav)
}
nav
}
TabNav::Cycle => {
if scope.descendants().tree_filter(Self::filter_tab_skip).any(|w| &w != self) {
FocusNavAction::PREV | FocusNavAction::NEXT
} else {
FocusNavAction::empty()
}
}
TabNav::Once => {
if let Some(p_scope) = scope.scope() {
scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, already_found)
} else {
FocusNavAction::empty()
}
}
}
}
fn enabled_directional_nav(
&self,
scope: &WidgetFocusInfo,
scope_info: FocusInfo,
skip_self: bool,
already_found: FocusNavAction,
) -> FocusNavAction {
let directional_nav = scope_info.directional_nav();
if directional_nav == DirectionalNav::None {
return FocusNavAction::empty();
}
let mut nav = already_found;
let from_pt = self.info.inner_bounds().to_box2d();
if !nav.contains(FocusNavAction::UP)
&& self
.directional_from(scope, from_pt, Orientation2D::Above, skip_self, true)
.is_some()
{
nav |= FocusNavAction::UP;
}
if !nav.contains(FocusNavAction::RIGHT)
&& self
.directional_from(scope, from_pt, Orientation2D::Right, skip_self, true)
.is_some()
{
nav |= FocusNavAction::RIGHT;
}
if !nav.contains(FocusNavAction::DOWN)
&& self
.directional_from(scope, from_pt, Orientation2D::Below, skip_self, true)
.is_some()
{
nav |= FocusNavAction::DOWN;
}
if !nav.contains(FocusNavAction::LEFT)
&& self
.directional_from(scope, from_pt, Orientation2D::Left, skip_self, true)
.is_some()
{
nav |= FocusNavAction::LEFT;
}
if !nav.contains(FocusNavAction::DIRECTIONAL) {
match directional_nav {
DirectionalNav::Continue => {
if let Some(p_scope) = scope.scope() {
nav |= scope.enabled_directional_nav(&p_scope, p_scope.focus_info(), true, nav);
}
}
DirectionalNav::Cycle => {
let scope_bounds = scope.info.inner_bounds();
if !nav.contains(FocusNavAction::UP) {
let mut from_pt = from_pt.center();
from_pt.y = scope_bounds.max().y;
if self
.directional_from(
scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Above,
true,
true,
)
.is_some()
{
nav |= FocusNavAction::UP;
}
}
if !nav.contains(FocusNavAction::RIGHT) {
let mut from_pt = from_pt.center();
from_pt.x = scope_bounds.min().x;
if self
.directional_from(
scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Right,
true,
true,
)
.is_some()
{
nav |= FocusNavAction::RIGHT;
}
}
if !nav.contains(FocusNavAction::DOWN) {
let mut from_pt = from_pt.center();
from_pt.y = scope_bounds.min().y;
if self
.directional_from(
scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Below,
true,
true,
)
.is_some()
{
nav |= FocusNavAction::DOWN;
}
}
if !nav.contains(FocusNavAction::LEFT) {
let mut from_pt = from_pt.center();
from_pt.x = scope_bounds.max().x;
if self
.directional_from(
scope,
PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
Orientation2D::Left,
true,
true,
)
.is_some()
{
nav |= FocusNavAction::LEFT;
}
}
if !nav.contains(FocusNavAction::DIRECTIONAL) {
let info = self.focus_info();
if info.is_scope() && matches!(info.directional_nav(), DirectionalNav::Continue) {
if nav.contains(FocusNavAction::UP) || nav.contains(FocusNavAction::DOWN) {
nav |= FocusNavAction::UP | FocusNavAction::DOWN;
}
if nav.contains(FocusNavAction::LEFT) || nav.contains(FocusNavAction::RIGHT) {
nav |= FocusNavAction::LEFT | FocusNavAction::RIGHT;
}
}
}
}
_ => {}
}
}
nav
}
pub fn enabled_nav(&self) -> FocusNavAction {
let _span = tracing::trace_span!("enabled_nav").entered();
let mut nav = FocusNavAction::empty();
if let Some(scope) = self.scope() {
nav |= FocusNavAction::EXIT;
nav.set(FocusNavAction::ENTER, self.descendants().next().is_some());
let scope_info = scope.focus_info();
nav |= self.enabled_tab_nav(&scope, scope_info, false, FocusNavAction::empty());
nav |= self.enabled_directional_nav(&scope, scope_info, false, FocusNavAction::empty());
}
nav.set(FocusNavAction::ALT, self.in_alt_scope() || self.alt_scope().is_some());
nav
}
}
impl_from_and_into_var! {
fn from(focus_info: WidgetFocusInfo) -> WidgetInfo {
focus_info.info
}
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum FocusInfo {
NotFocusable,
Focusable {
tab_index: TabIndex,
skip_directional: bool,
},
FocusScope {
tab_index: TabIndex,
skip_directional: bool,
tab_nav: TabNav,
directional_nav: DirectionalNav,
on_focus: FocusScopeOnFocus,
alt: bool,
},
}
#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum FocusScopeOnFocus {
Widget,
FirstDescendant,
LastFocused,
FirstDescendantIgnoreBounds,
LastFocusedIgnoreBounds,
}
impl fmt::Debug for FocusScopeOnFocus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "FocusScopeOnFocus::")?;
}
match self {
FocusScopeOnFocus::Widget => write!(f, "Widget"),
FocusScopeOnFocus::FirstDescendant => write!(f, "FirstDescendant"),
FocusScopeOnFocus::LastFocused => write!(f, "LastFocused"),
FocusScopeOnFocus::FirstDescendantIgnoreBounds => write!(f, "FirstDescendantIgnoreBounds"),
FocusScopeOnFocus::LastFocusedIgnoreBounds => write!(f, "LastFocusedIgnoreBounds"),
}
}
}
impl Default for FocusScopeOnFocus {
fn default() -> Self {
FocusScopeOnFocus::FirstDescendant
}
}
impl FocusInfo {
pub fn is_focusable(self) -> bool {
!matches!(self, FocusInfo::NotFocusable)
}
pub fn is_scope(self) -> bool {
matches!(self, FocusInfo::FocusScope { .. })
}
pub fn is_alt_scope(self) -> bool {
match self {
FocusInfo::FocusScope { alt, .. } => alt,
_ => false,
}
}
pub fn tab_nav(self) -> TabNav {
match self {
FocusInfo::FocusScope { tab_nav, .. } => tab_nav,
FocusInfo::Focusable { .. } => TabNav::Continue,
FocusInfo::NotFocusable => TabNav::None,
}
}
pub fn directional_nav(self) -> DirectionalNav {
match self {
FocusInfo::FocusScope { directional_nav, .. } => directional_nav,
FocusInfo::Focusable { .. } => DirectionalNav::Continue,
FocusInfo::NotFocusable => DirectionalNav::None,
}
}
pub fn tab_index(self) -> TabIndex {
match self {
FocusInfo::Focusable { tab_index, .. } => tab_index,
FocusInfo::FocusScope { tab_index, .. } => tab_index,
FocusInfo::NotFocusable => TabIndex::SKIP,
}
}
pub fn skip_directional(self) -> bool {
match self {
FocusInfo::Focusable { skip_directional, .. } => skip_directional,
FocusInfo::FocusScope { skip_directional, .. } => skip_directional,
FocusInfo::NotFocusable => true,
}
}
pub fn scope_on_focus(self) -> FocusScopeOnFocus {
match self {
FocusInfo::FocusScope { on_focus, .. } => on_focus,
_ => FocusScopeOnFocus::Widget,
}
}
}
static_id! {
static ref FOCUS_INFO_ID: StateId<FocusInfoData>;
static ref FOCUS_TREE_ID: StateId<FocusTreeData>;
}
#[derive(Default)]
pub(super) struct FocusTreeData {
alt_scopes: Mutex<IdSet<WidgetId>>,
}
impl FocusTreeData {
pub(super) fn consolidate_alt_scopes(prev_tree: &WeakWidgetInfoTree, new_tree: &WidgetInfoTree) {
let prev = prev_tree
.upgrade()
.and_then(|t| t.build_meta().get(*FOCUS_TREE_ID).map(|d| d.alt_scopes.lock().clone()))
.unwrap_or_default();
let mut alt_scopes = prev;
if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
alt_scopes.extend(data.alt_scopes.lock().iter());
}
alt_scopes.retain(|id| {
if let Some(wgt) = new_tree.get(*id)
&& let Some(info) = wgt.meta().get(*FOCUS_INFO_ID)
&& info.build().is_alt_scope()
{
for parent in wgt.ancestors() {
if let Some(info) = parent.meta().get(*FOCUS_INFO_ID)
&& info.build().is_scope()
{
info.inner_alt.store(Some(*id), Relaxed);
break;
}
}
return true;
}
false
});
if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
*data.alt_scopes.lock() = alt_scopes;
}
}
}
#[derive(Default, Debug)]
struct FocusInfoData {
focusable: Option<bool>,
scope: Option<bool>,
alt_scope: bool,
on_focus: FocusScopeOnFocus,
tab_index: Option<TabIndex>,
tab_nav: Option<TabNav>,
directional_nav: Option<DirectionalNav>,
skip_directional: Option<bool>,
inner_alt: Atomic<Option<WidgetId>>,
access_handler_registered: bool,
}
impl FocusInfoData {
pub fn build(&self) -> FocusInfo {
match (self.focusable, self.scope, self.tab_index, self.tab_nav, self.directional_nav) {
(Some(false), _, _, _, _) => FocusInfo::NotFocusable,
(_, Some(true), idx, tab, dir) | (_, None, idx, tab @ Some(_), dir) | (_, None, idx, tab, dir @ Some(_)) => {
FocusInfo::FocusScope {
tab_index: idx.unwrap_or(TabIndex::AUTO),
skip_directional: self.skip_directional.unwrap_or_default(),
tab_nav: tab.unwrap_or(TabNav::Continue),
directional_nav: dir.unwrap_or(DirectionalNav::Continue),
alt: self.alt_scope,
on_focus: self.on_focus,
}
}
(Some(true), _, idx, _, _) | (_, _, idx @ Some(_), _, _) => FocusInfo::Focusable {
tab_index: idx.unwrap_or(TabIndex::AUTO),
skip_directional: self.skip_directional.unwrap_or_default(),
},
_ => FocusInfo::NotFocusable,
}
}
}
pub struct FocusInfoBuilder<'a>(&'a mut WidgetInfoBuilder);
impl<'a> FocusInfoBuilder<'a> {
pub fn new(builder: &'a mut WidgetInfoBuilder) -> Self {
let mut r = Self(builder);
r.with_tree_data(|_| {}); r
}
fn with_data<R>(&mut self, visitor: impl FnOnce(&mut FocusInfoData) -> R) -> R {
let mut access = self.0.access().is_some();
let r = self.0.with_meta(|m| {
let data = m.into_entry(*FOCUS_INFO_ID).or_default();
if access {
access = !std::mem::replace(&mut data.access_handler_registered, true);
}
visitor(data)
});
if access {
self.0.access().unwrap().on_access_build(|args| {
if args.widget.info().clone().into_focusable(true, false).is_some() {
args.node.commands.push(zng_view_api::access::AccessCmdName::Focus);
}
});
}
r
}
fn with_tree_data<R>(&mut self, visitor: impl FnOnce(&mut FocusTreeData) -> R) -> R {
self.0.with_build_meta(|m| visitor(m.into_entry(*FOCUS_TREE_ID).or_default()))
}
pub fn focusable(&mut self, is_focusable: bool) -> &mut Self {
self.with_data(|data| {
data.focusable = Some(is_focusable);
});
self
}
pub fn focusable_passive(&mut self, is_focusable: bool) -> &mut Self {
self.with_data(|data| {
if data.focusable.is_none() {
data.focusable = Some(is_focusable);
}
});
self
}
pub fn scope(&mut self, is_focus_scope: bool) -> &mut Self {
self.with_data(|data| {
data.scope = Some(is_focus_scope);
});
self
}
pub fn alt_scope(&mut self, is_alt_focus_scope: bool) -> &mut Self {
self.with_data(|data| {
data.alt_scope = is_alt_focus_scope;
if is_alt_focus_scope {
data.scope = Some(true);
if data.tab_index.is_none() {
data.tab_index = Some(TabIndex::SKIP);
}
if data.tab_nav.is_none() {
data.tab_nav = Some(TabNav::Cycle);
}
if data.directional_nav.is_none() {
data.directional_nav = Some(DirectionalNav::Cycle);
}
if data.skip_directional.is_none() {
data.skip_directional = Some(true);
}
}
});
if is_alt_focus_scope {
let wgt_id = self.0.widget_id();
self.with_tree_data(|d| d.alt_scopes.lock().insert(wgt_id));
}
self
}
pub fn on_focus(&mut self, as_focus_scope_on_focus: FocusScopeOnFocus) -> &mut Self {
self.with_data(|data| {
data.on_focus = as_focus_scope_on_focus;
});
self
}
pub fn tab_index(&mut self, tab_index: TabIndex) -> &mut Self {
self.with_data(|data| {
data.tab_index = Some(tab_index);
});
self
}
pub fn tab_nav(&mut self, scope_tab_nav: TabNav) -> &mut Self {
self.with_data(|data| {
data.tab_nav = Some(scope_tab_nav);
});
self
}
pub fn directional_nav(&mut self, scope_directional_nav: DirectionalNav) -> &mut Self {
self.with_data(|data| {
data.directional_nav = Some(scope_directional_nav);
});
self
}
pub fn skip_directional(&mut self, skip: bool) -> &mut Self {
self.with_data(|data| {
data.skip_directional = Some(skip);
});
self
}
}