use std::time::Duration;
use crate::pointer::{EventTime, PointerId};
use crate::widget_id::WidgetId;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct Press {
pub(crate) pointer: PointerId,
pub(crate) owner: Option<WidgetId>,
pub(crate) focusable: Option<WidgetId>,
pub(crate) inside: bool,
pub(crate) show_at: Option<EventTime>,
}
impl Press {
pub(crate) fn showing(&self) -> bool {
self.inside && self.show_at.is_none()
}
pub(crate) fn pending(&self) -> bool {
self.show_at.is_some()
}
fn resolve_delay(&mut self, now: EventTime) -> bool {
match self.show_at {
Some(deadline) if now >= deadline => {
self.show_at = None;
true
}
_ => false,
}
}
}
#[derive(Debug, Default)]
pub(crate) struct PressTable {
presses: Vec<Press>,
}
impl PressTable {
pub(crate) fn press(
&mut self,
pointer: PointerId,
focusable: Option<WidgetId>,
now: EventTime,
delay: Option<Duration>,
) {
self.presses.retain(|p| p.pointer != pointer);
self.presses.push(Press {
pointer,
owner: None,
focusable,
inside: true,
show_at: delay.map(|d| now + d),
});
}
pub(crate) fn set_owner(&mut self, pointer: PointerId, owner: WidgetId) {
if self
.presses
.iter()
.any(|p| p.pointer != pointer && p.owner == Some(owner))
{
return;
}
if let Some(press) = self.presses.iter_mut().find(|p| p.pointer == pointer) {
press.owner = Some(owner);
}
}
pub(crate) fn get(&self, pointer: PointerId) -> Option<&Press> {
self.presses.iter().find(|p| p.pointer == pointer)
}
pub(crate) fn get_mut(&mut self, pointer: PointerId) -> Option<&mut Press> {
self.presses.iter_mut().find(|p| p.pointer == pointer)
}
pub(crate) fn owner_of(&self, node: WidgetId) -> Option<PointerId> {
self.presses
.iter()
.find(|p| p.owner == Some(node))
.map(|p| p.pointer)
}
pub(crate) fn for_node(&self, node: WidgetId) -> Option<&Press> {
self.presses.iter().find(|p| p.owner == Some(node))
}
pub(crate) fn release(&mut self, pointer: PointerId) -> Option<WidgetId> {
let index = self.presses.iter().position(|p| p.pointer == pointer)?;
self.presses.swap_remove(index).owner
}
pub(crate) fn resolve_delays(&mut self, now: EventTime) -> Vec<WidgetId> {
self.presses
.iter_mut()
.filter_map(|p| p.resolve_delay(now).then_some(p.owner).flatten())
.collect()
}
pub(crate) fn next_deadline(&self) -> Option<EventTime> {
self.presses.iter().filter_map(|p| p.show_at).min()
}
pub(crate) fn is_empty(&self) -> bool {
self.presses.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn contact() -> PointerId {
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT: AtomicU64 = AtomicU64::new(70_000);
crate::pointer::PointerIdAllocator::global().begin(
crate::pointer::BackendDeviceKey::DEFAULT,
NEXT.fetch_add(1, Ordering::Relaxed),
)
}
fn node() -> WidgetId {
use slotmap::SlotMap;
let mut map: SlotMap<WidgetId, ()> = SlotMap::with_key();
map.insert(())
}
#[test]
fn a_release_clears_only_the_press_it_owns() {
let mut table = PressTable::default();
let (node, first, second) = (node(), contact(), contact());
table.press(first, None, EventTime::ZERO, None);
table.set_owner(first, node);
table.press(second, None, EventTime::ZERO, None);
table.release(second);
assert_eq!(table.owner_of(node), Some(first));
assert!(table.for_node(node).is_some_and(Press::showing));
}
#[test]
fn a_second_contact_does_not_take_over_a_nodes_visual() {
let mut table = PressTable::default();
let (node, first, second) = (node(), contact(), contact());
table.press(first, None, EventTime::ZERO, None);
table.set_owner(first, node);
table.press(second, None, EventTime::ZERO, None);
table.set_owner(second, node);
assert_eq!(table.owner_of(node), Some(first));
assert_eq!(
table.get(second).and_then(|p| p.owner),
None,
"the second contact holds a press, but not this node's visual"
);
}
#[test]
fn a_delayed_press_shows_only_once_its_deadline_passes() {
let mut table = PressTable::default();
let (node, held) = (node(), contact());
table.press(
held,
None,
EventTime::ZERO,
Some(Duration::from_millis(100)),
);
table.set_owner(held, node);
let press = *table.for_node(node).expect("press");
assert!(press.pending() && !press.showing());
assert_eq!(table.next_deadline(), Some(EventTime::from_millis(100)),);
assert!(table.resolve_delays(EventTime::from_millis(99)).is_empty());
assert_eq!(
table.resolve_delays(EventTime::from_millis(100)),
vec![node]
);
assert!(table.for_node(node).is_some_and(Press::showing));
assert_eq!(table.next_deadline(), None);
}
}