use crate::{widget::WidgetId, window::WindowId};
use super::*;
#[derive(Clone)]
pub struct WidgetPath {
window_id: WindowId,
path: Arc<Vec<WidgetId>>,
}
impl PartialEq for WidgetPath {
fn eq(&self, other: &Self) -> bool {
self.window_id == other.window_id && self.path == other.path
}
}
impl Eq for WidgetPath {}
impl PartialEq<InteractionPath> for WidgetPath {
fn eq(&self, other: &InteractionPath) -> bool {
other == self
}
}
impl fmt::Debug for WidgetPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("WidgetPath")
.field("window_id", &self.window_id)
.field("path", &self.path)
.finish_non_exhaustive()
} else {
write!(f, "{self}")
}
}
}
impl fmt::Display for WidgetPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}//", self.window_id)?;
for w in self.ancestors() {
write!(f, "{w}/")?;
}
write!(f, "{}", self.widget_id())
}
}
impl WidgetPath {
pub fn new(window_id: WindowId, path: Arc<Vec<WidgetId>>) -> WidgetPath {
WidgetPath { window_id, path }
}
pub fn into_parts(self) -> (WindowId, Arc<Vec<WidgetId>>) {
(self.window_id, self.path)
}
pub fn window_id(&self) -> WindowId {
self.window_id
}
pub fn ancestors(&self) -> &[WidgetId] {
&self.path[..self.path.len() - 1]
}
pub fn widget_id(&self) -> WidgetId {
self.path[self.path.len() - 1]
}
pub fn parent_id(&self) -> Option<WidgetId> {
self.ancestors().iter().copied().next_back()
}
pub fn widgets_path(&self) -> &[WidgetId] {
&self.path[..]
}
pub fn contains(&self, widget_id: WidgetId) -> bool {
self.path.iter().any(move |&w| w == widget_id)
}
pub fn ancestor_path(&self, ancestor_id: WidgetId) -> Option<Cow<WidgetPath>> {
self.path.iter().position(|&id| id == ancestor_id).map(|i| {
if i == self.path.len() - 1 {
Cow::Borrowed(self)
} else {
Cow::Owned(WidgetPath {
window_id: self.window_id,
path: self.path[..i].to_vec().into(),
})
}
})
}
pub fn shared_ancestor<'a>(&'a self, other: &'a WidgetPath) -> Option<Cow<'a, WidgetPath>> {
if self.window_id == other.window_id {
if let Some(i) = self.path.iter().zip(other.path.iter()).position(|(a, b)| a != b) {
if i == 0 {
None
} else {
let path = self.path[..i].to_vec().into();
Some(Cow::Owned(WidgetPath {
window_id: self.window_id,
path,
}))
}
} else if self.path.len() <= other.path.len() {
Some(Cow::Borrowed(self))
} else {
Some(Cow::Borrowed(other))
}
} else {
None
}
}
pub fn root_path(&self) -> Cow<WidgetPath> {
if self.path.len() == 1 {
Cow::Borrowed(self)
} else {
Cow::Owned(WidgetPath {
window_id: self.window_id,
path: Arc::new(vec![self.path[0]]),
})
}
}
pub fn sub_path(&self, widget_id: WidgetId) -> Option<Cow<WidgetPath>> {
if self.widget_id() == widget_id {
Some(Cow::Borrowed(self))
} else {
let i = self.path.iter().position(|&id| id == widget_id)?;
let path = Self::new(self.window_id, Arc::new(self.path[..=i].to_vec()));
Some(Cow::Owned(path))
}
}
}
#[derive(Clone)]
pub struct InteractionPath {
path: WidgetPath,
blocked: usize,
disabled: usize,
}
impl PartialEq for InteractionPath {
fn eq(&self, other: &Self) -> bool {
self.as_path() == other.as_path() && self.blocked == other.blocked && self.disabled == other.disabled
}
}
impl Eq for InteractionPath {}
impl PartialEq<WidgetPath> for InteractionPath {
fn eq(&self, other: &WidgetPath) -> bool {
self.as_path() == other
}
}
impl fmt::Debug for InteractionPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("InteractionPath")
.field("window_id", &self.window_id)
.field("path", &self.path)
.field("blocked", &self.blocked_index())
.field("disabled", &self.disabled_index())
.finish_non_exhaustive()
} else {
write!(f, "{self}")
}
}
}
impl fmt::Display for InteractionPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}//", self.window_id)?;
let mut sep = "";
for (w, i) in self.zip() {
write!(f, "{sep}{w}{{{i:?}}}")?;
sep = "/";
}
Ok(())
}
}
impl InteractionPath {
pub(super) fn new_internal(path: WidgetPath, blocked: usize, disabled: usize) -> Self {
Self { path, blocked, disabled }
}
pub fn new<P: IntoIterator<Item = (WidgetId, Interactivity)>>(window_id: WindowId, path: P) -> InteractionPath {
let iter = path.into_iter();
let mut path = Vec::with_capacity(iter.size_hint().0);
let mut blocked = None;
let mut disabled = None;
for (i, (w, interactivity)) in iter.enumerate() {
path.push(w);
if blocked.is_none() && interactivity.contains(Interactivity::BLOCKED) {
blocked = Some(i);
}
if disabled.is_none() && interactivity.contains(Interactivity::DISABLED) {
disabled = Some(i);
}
}
let len = path.len();
InteractionPath {
path: WidgetPath::new(window_id, path.into()),
blocked: blocked.unwrap_or(len),
disabled: disabled.unwrap_or(len),
}
}
pub fn new_enabled(window_id: WindowId, path: Arc<Vec<WidgetId>>) -> InteractionPath {
let path = WidgetPath::new(window_id, path);
Self::from_enabled(path)
}
pub fn from_enabled(path: WidgetPath) -> InteractionPath {
let len = path.path.len();
InteractionPath {
path,
blocked: len,
disabled: len,
}
}
pub fn as_path(&self) -> &WidgetPath {
&self.path
}
pub fn blocked_index(&self) -> Option<usize> {
if self.blocked < self.path.path.len() {
Some(self.blocked)
} else {
None
}
}
pub fn disabled_index(&self) -> Option<usize> {
if self.disabled < self.path.path.len() {
Some(self.disabled)
} else {
None
}
}
pub fn interaction_path(&self) -> impl DoubleEndedIterator<Item = Interactivity> + ExactSizeIterator {
struct InteractivityIter {
range: ops::Range<usize>,
blocked: usize,
disabled: usize,
}
impl InteractivityIter {
fn interactivity(&self, i: usize) -> Interactivity {
let mut interactivity = Interactivity::ENABLED;
if self.blocked <= i {
interactivity |= Interactivity::BLOCKED;
}
if self.disabled <= i {
interactivity |= Interactivity::DISABLED;
}
interactivity
}
}
impl Iterator for InteractivityIter {
type Item = Interactivity;
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|i| self.interactivity(i))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.range.len(), Some(self.range.len()))
}
}
impl ExactSizeIterator for InteractivityIter {}
impl DoubleEndedIterator for InteractivityIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.range.next_back().map(|i| self.interactivity(i))
}
}
InteractivityIter {
range: 0..self.path.path.len(),
blocked: self.blocked,
disabled: self.disabled,
}
}
pub fn interactivity_of(&self, widget_id: WidgetId) -> Option<Interactivity> {
self.path.widgets_path().iter().position(|&w| w == widget_id).map(|i| {
let mut interactivity = Interactivity::ENABLED;
if self.blocked <= i {
interactivity |= Interactivity::BLOCKED;
}
if self.disabled <= i {
interactivity |= Interactivity::DISABLED;
}
interactivity
})
}
pub fn interactivity(&self) -> Interactivity {
let mut interactivity = Interactivity::ENABLED;
let len = self.path.path.len();
if self.blocked < len {
interactivity |= Interactivity::BLOCKED;
}
if self.disabled < len {
interactivity |= Interactivity::DISABLED;
}
interactivity
}
pub fn zip(&self) -> impl DoubleEndedIterator<Item = (WidgetId, Interactivity)> + ExactSizeIterator + '_ {
self.path.widgets_path().iter().copied().zip(self.interaction_path())
}
pub fn unblocked(self) -> Option<InteractionPath> {
if self.blocked < self.path.path.len() {
if self.blocked == 0 {
return None;
}
Some(InteractionPath {
path: WidgetPath {
window_id: self.path.window_id,
path: self.path.path[..self.blocked].to_vec().into(),
},
blocked: self.blocked,
disabled: self.disabled,
})
} else {
Some(self)
}
}
pub fn enabled(self) -> Option<WidgetPath> {
let enabled_end = self.blocked.min(self.disabled);
if enabled_end < self.path.path.len() {
if enabled_end == 0 {
return None;
}
Some(WidgetPath {
window_id: self.path.window_id,
path: self.path.path[..enabled_end].to_vec().into(),
})
} else {
Some(self.path)
}
}
pub fn ancestor_path(&self, ancestor_id: WidgetId) -> Option<Cow<InteractionPath>> {
self.widgets_path().iter().position(|&id| id == ancestor_id).map(|i| {
if i == self.path.path.len() - 1 {
Cow::Borrowed(self)
} else {
Cow::Owned(InteractionPath {
path: WidgetPath {
window_id: self.window_id,
path: self.path.path[..=i].to_vec().into(),
},
blocked: self.blocked,
disabled: self.disabled,
})
}
})
}
pub fn shared_ancestor<'a>(&'a self, other: &'a InteractionPath) -> Option<Cow<'a, InteractionPath>> {
if self.window_id == other.window_id {
if let Some(i) = self.zip().zip(other.zip()).position(|(a, b)| a != b) {
if i == 0 {
None
} else {
let path = self.path.path[..i].to_vec().into();
Some(Cow::Owned(InteractionPath {
path: WidgetPath {
window_id: self.window_id,
path,
},
blocked: self.blocked,
disabled: self.disabled,
}))
}
} else if self.path.path.len() <= other.path.path.len() {
Some(Cow::Borrowed(self))
} else {
Some(Cow::Borrowed(other))
}
} else {
None
}
}
pub fn root_path(&self) -> Cow<InteractionPath> {
if self.path.path.len() == 1 {
Cow::Borrowed(self)
} else {
Cow::Owned(InteractionPath {
path: WidgetPath {
window_id: self.window_id,
path: Arc::new(vec![self.path.path[0]]),
},
blocked: self.blocked,
disabled: self.disabled,
})
}
}
pub fn sub_path(&self, widget_id: WidgetId) -> Option<Cow<InteractionPath>> {
if widget_id == self.widget_id() {
Some(Cow::Borrowed(self))
} else {
let path = self.path.sub_path(widget_id)?;
Some(Cow::Owned(Self {
path: path.into_owned(),
blocked: self.blocked,
disabled: self.disabled,
}))
}
}
}
impl ops::Deref for InteractionPath {
type Target = WidgetPath;
fn deref(&self) -> &Self::Target {
&self.path
}
}
impl From<InteractionPath> for WidgetPath {
fn from(p: InteractionPath) -> Self {
p.path
}
}