blob: 9d6175c785261088a7f279cb67b6b6bf59c3831f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Extensions for `fidl_fuchsia_ui_focus`.
use {
fidl_fuchsia_ui_focus::{FocusChain, FocusKoidChain},
fidl_fuchsia_ui_views_ext::ViewRefExt,
fuchsia_scenic as scenic,
fuchsia_zircon::{Koid, Status},
};
/// Extension trait for [`fidl_fuchsia_ui_focus::FocusChain`] and
/// [`fidl_fuchsia_ui_focus::FocusKoidChain`].
pub trait FocusChainExt
where
Self: Sized,
{
/// Returns the number of views in the chain.
fn len(&self) -> usize;
/// Returns true if there are no views in the chain.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Creates a new chain, in which all of the `ViewRef`s (or koids) have been duplicated from the
/// original. Returns an error if duplicating any of the `ViewRef`s fails.
fn duplicate(&self) -> Result<Self, Status>;
/// Returns a fallible iterator over the chain's koids. If any koid cannot be retrieved, the
/// iterator yields an error in its place.
fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_>;
/// Returns true if the two chains' `ViewRef`s correspond to the same koids, in the same
/// order. If any of the koids cannot be retrieved, returns an error.
fn equivalent<O: FocusChainExt>(&self, other: &O) -> Result<bool, Status> {
let self_len = self.len();
if self_len != other.len() {
return Ok(false);
}
if self_len == 0 {
return Ok(true);
}
let mut zipped = std::iter::zip(self.koids(), other.koids());
while let Some((a, b)) = zipped.next() {
if a? != b? {
return Ok(false);
}
}
Ok(true)
}
/// Converts this chain into a [`FocusKoidChain`], which contains just koids instead of
/// [`ViewRef`]s.
fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status>;
}
impl FocusChainExt for FocusChain {
fn len(&self) -> usize {
match self.focus_chain.as_ref() {
None => 0,
Some(v) => v.len(),
}
}
fn duplicate(&self) -> Result<Self, Status> {
let v = match self.focus_chain.as_ref() {
None => None,
Some(v) => Some(
v.iter().map(|vr| scenic::duplicate_view_ref(vr)).collect::<Result<Vec<_>, _>>()?,
),
};
let output = FocusChain { focus_chain: v, ..Default::default() };
Ok(output)
}
fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_> {
match &self.focus_chain {
None => Box::new(std::iter::empty()),
Some(v) => Box::new(v.iter().map(|vr| vr.get_koid())),
}
}
fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status> {
let raw_koids = match self.focus_chain.as_ref() {
None => None,
Some(_) => Some(
self.koids()
.map(|result| -> Result<u64, Status> { result.map(|koid| koid.raw_koid()) })
.collect::<Result<Vec<_>, _>>()?,
),
};
Ok(FocusKoidChain { focus_chain: raw_koids, ..Default::default() })
}
}
impl FocusChainExt for FocusKoidChain {
fn len(&self) -> usize {
match self.focus_chain.as_ref() {
None => 0,
Some(v) => v.len(),
}
}
/// Clones this `FocusKoidChain`.
fn duplicate(&self) -> Result<Self, Status> {
Ok(self.clone())
}
fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_> {
match &self.focus_chain {
None => Box::new(std::iter::empty()),
Some(v) => Box::new(v.iter().map(|raw| Ok(Koid::from_raw(*raw)))),
}
}
/// Clones this `FocusKoidChain`.
fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status> {
self.duplicate()
}
}
#[cfg(test)]
mod tests {
use {super::*, fidl_fuchsia_ui_focus_test_helpers::make_focus_chain};
#[test]
fn focus_chain_duplicate() {
let (source, _control_refs) = make_focus_chain(2);
let target = source.duplicate().expect("error in duplicate()");
let source_koids = source.koids().collect::<Result<Vec<_>, _>>().unwrap();
let target_koids = target.koids().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(source_koids, target_koids,);
}
#[test]
fn focus_chain_duplicate_empty() {
let source = FocusChain::default();
let target = source.duplicate().expect("error in duplicate()");
assert_eq!(target.focus_chain, None);
}
#[test]
fn focus_chain_equivalent_empty() {
let a = FocusChain::default();
let b = FocusChain::default();
assert!(a.equivalent(&b).unwrap());
let (a, _) = make_focus_chain(0);
let (b, _) = make_focus_chain(0);
assert!(a.equivalent(&b).unwrap());
}
#[test]
fn focus_chain_equivalent_same_lengths() {
let (a, _a) = make_focus_chain(3);
let (b, _b) = make_focus_chain(3);
assert!(!a.equivalent(&b).unwrap());
}
#[test]
fn focus_chain_equivalent_different_lengths() {
let (a, _a) = make_focus_chain(3);
let (b, _b) = make_focus_chain(5);
assert!(!a.equivalent(&b).unwrap());
}
#[test]
fn focus_chain_equivalent_duplicates() {
let (a, _a) = make_focus_chain(3);
let b = a.duplicate().expect("duplicate");
assert!(a.equivalent(&b).unwrap());
}
#[test]
fn focus_chain_to_focus_koid_chain() {
let (focus_chain, _view_control_refs) = make_focus_chain(2);
let raw_koids = vec![
focus_chain.focus_chain.as_ref().unwrap()[0].get_koid().unwrap().raw_koid(),
focus_chain.focus_chain.as_ref().unwrap()[1].get_koid().unwrap().raw_koid(),
];
let expected = FocusKoidChain { focus_chain: Some(raw_koids), ..Default::default() };
let actual = focus_chain.to_focus_koid_chain().unwrap();
assert_eq!(expected, actual);
}
#[test]
fn focus_chain_equivalent_focus_koid_chain() {
let (chain_a, _vcr_a) = make_focus_chain(2);
let (chain_b, _vcr_b) = make_focus_chain(2);
let koid_chain_a = chain_a.to_focus_koid_chain().unwrap();
let koid_chain_b = chain_b.to_focus_koid_chain().unwrap();
assert!(chain_a.equivalent(&koid_chain_a).unwrap());
assert!(!chain_a.equivalent(&koid_chain_b).unwrap())
}
}