use crate::{core::state::Orientation, theme::Direction};
use dioxus::prelude::*;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Move {
Next,
Previous,
First,
Last,
}
#[must_use]
pub fn intent(key: &Key, orientation: Orientation, direction: Direction) -> Option<Move> {
let rtl = direction == Direction::Rtl;
match (key, orientation) {
(Key::Home, _) => Some(Move::First),
(Key::End, _) => Some(Move::Last),
(Key::ArrowDown, Orientation::Vertical) => Some(Move::Next),
(Key::ArrowUp, Orientation::Vertical) => Some(Move::Previous),
(Key::ArrowRight, Orientation::Horizontal) => {
Some(if rtl { Move::Previous } else { Move::Next })
}
(Key::ArrowLeft, Orientation::Horizontal) => {
Some(if rtl { Move::Next } else { Move::Previous })
}
_ => None,
}
}
#[must_use]
pub fn target(
len: usize,
current: usize,
step: Move,
reachable: impl Fn(usize) -> bool,
) -> Option<usize> {
if len == 0 {
return None;
}
match step {
Move::First => (0..len).find(|&i| reachable(i)),
Move::Last => (0..len).rev().find(|&i| reachable(i)),
Move::Next => (1..=len)
.map(|n| (current + n) % len)
.find(|&i| reachable(i)),
Move::Previous => (1..=len)
.map(|n| (current + len - n % len) % len)
.find(|&i| reachable(i)),
}
}
#[derive(Clone)]
pub(crate) struct Member {
pub(crate) key: u64,
pub(crate) value: String,
pub(crate) focusable: bool,
pub(crate) enabled: bool,
pub(crate) mounted: Option<Rc<MountedData>>,
}
#[derive(Clone, Copy)]
pub(crate) struct Roster {
members: Signal<Vec<Member>>,
next: Signal<u64>,
}
impl Roster {
pub(crate) fn new() -> Self {
Self {
members: Signal::new(Vec::new()),
next: Signal::new(0),
}
}
pub(crate) fn peek(&self) -> Vec<Member> {
self.members.peek().clone()
}
pub(crate) fn read(&self) -> Vec<Member> {
self.members.read().clone()
}
fn join(mut self, value: String, focusable: bool, enabled: bool) -> u64 {
let key = *self.next.peek();
self.next.set(key + 1);
self.members.write().push(Member {
key,
value,
focusable,
enabled,
mounted: None,
});
key
}
fn update(mut self, key: u64, value: &str, focusable: bool, enabled: bool) {
let stale = self.members.peek().iter().any(|m| {
m.key == key && (m.value != value || m.focusable != focusable || m.enabled != enabled)
});
if stale {
if let Some(member) = self.members.write().iter_mut().find(|m| m.key == key) {
value.clone_into(&mut member.value);
member.focusable = focusable;
member.enabled = enabled;
}
}
}
pub(crate) fn mount(mut self, key: u64, mounted: Rc<MountedData>) {
if let Some(member) = self.members.write().iter_mut().find(|m| m.key == key) {
member.mounted = Some(mounted);
}
}
fn leave(mut self, key: u64) {
self.members.write().retain(|m| m.key != key);
}
}
pub(crate) fn use_member(value: &str, focusable: bool, enabled: bool) -> Option<(Roster, u64)> {
let roster = try_consume_context::<Roster>();
let joined = use_hook(|| roster.map(|r| (r, r.join(value.to_owned(), focusable, enabled))));
if let Some((roster, key)) = joined {
roster.update(key, value, focusable, enabled);
}
use_drop(move || {
if let Some((roster, key)) = joined {
roster.leave(key);
}
});
joined
}
pub(crate) fn focus(member: &Member) {
if let Some(mounted) = member.mounted.clone() {
spawn(async move {
let _ = mounted.set_focus(true).await;
});
}
}