use super::*;
use crate::input::ScrollRegion;
use crate::tree::NIL;
const AT_LIMIT: f32 = 0.5;
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ScrollLatch {
x: Option<Key>,
y: Option<Key>,
held: u32,
}
enum Answer {
Take,
Pass,
}
impl ScrollRegion {
fn hears(&self, x: bool, held: u32) -> bool {
self.mods & held != 0 && if x { self.takes_x } else { self.takes_y }
}
fn asked(&self, x: bool, held: u32) -> ScrollRegion {
if self.mods == 0 || self.hears(x, held) {
return *self;
}
ScrollRegion {
handler: false,
takes_x: self.scrolls_x,
takes_y: self.scrolls_y,
mods: 0,
..*self
}
}
}
impl Core {
pub(crate) fn route_scroll(&mut self, delta: Vec2, begins: bool, out: &mut Vec<UiEvent>) {
if begins {
self.scroll_latch = ScrollLatch {
held: self.interaction.modifiers().bits(),
..ScrollLatch::default()
};
}
let tx = (delta.x != 0.0)
.then(|| self.scroll_target(true, delta.x))
.flatten();
let ty = (delta.y != 0.0)
.then(|| self.scroll_target(false, delta.y))
.flatten();
match (tx, ty) {
(Some(a), Some(b)) if a.key == b.key && a.handler == b.handler => {
self.scroll_into(a, delta, out)
}
(a, b) => {
if let Some(a) = a {
self.scroll_into(a, Vec2::new(delta.x, 0.0), out);
}
if let Some(b) = b {
self.scroll_into(b, Vec2::new(0.0, delta.y), out);
}
}
}
}
fn scroll_target(&mut self, x: bool, d: f32) -> Option<ScrollRegion> {
let latched = if x {
self.scroll_latch.x
} else {
self.scroll_latch.y
};
if let Some(key) = latched {
if let Some(r) = self
.interaction
.scroll_regions
.iter()
.rev()
.find(|r| r.key == key && !r.inert)
{
return Some(r.asked(x, self.scroll_latch.held));
}
}
let regions = &self.interaction.scroll_regions;
let mut at = self
.interaction
.scroll_regions_at()
.next()
.map(|r| r.node)
.and_then(|node| regions.iter().rposition(|r| r.node == node));
let mut picked = None;
let held = self.scroll_latch.held;
let mut named = at.filter(|_| held != 0);
while let Some(i) = named {
let r = regions[i];
if r.inert {
break;
}
if r.hears(x, held) {
picked = Some(r);
at = None;
break;
}
named = (r.parent != NIL).then_some(r.parent as usize);
}
while let Some(i) = at {
let r = regions[i].asked(x, held);
if r.inert {
break;
}
if matches!(self.answer(&r, x, d), Answer::Take) {
picked = Some(r);
break;
}
at = (r.parent != NIL).then_some(r.parent as usize);
}
let key = picked.map(|r| r.key);
if x {
self.scroll_latch.x = key;
} else {
self.scroll_latch.y = key;
}
picked
}
fn answer(&self, r: &ScrollRegion, x: bool, d: f32) -> Answer {
let takes = if x { r.takes_x } else { r.takes_y };
if !takes {
return Answer::Pass;
}
let scrolls = if x { r.scrolls_x } else { r.scrolls_y };
if r.contain || (r.handler && !scrolls) {
return Answer::Take;
}
let Some((at, max)) = self.scroll.room(r.key) else {
return Answer::Take;
};
let (at, max) = if x { (at.x, max.x) } else { (at.y, max.y) };
let room = if d > 0.0 { at } else { max - at };
if room > AT_LIMIT {
Answer::Take
} else {
Answer::Pass
}
}
fn scroll_into(&mut self, r: ScrollRegion, delta: Vec2, out: &mut Vec<UiEvent>) {
if r.handler {
let p = self.interaction.cursor().unwrap_or(Vec2::ZERO);
if let Some(mut ev) = self.scroll_event(r.key, p, delta) {
if r.mods != 0
&& let Value::Map(entries) = &mut ev.payload
{
let held = crate::input::KeyMods::from_bits(self.scroll_latch.held);
let at = entries
.iter()
.position(|(k, _)| k == "tag")
.unwrap_or(entries.len());
entries.insert(at, ("mods".to_string(), held.to_fields()));
}
out.push(ev);
}
} else {
self.scroll.scroll_by(r.key, Vec2::new(-delta.x, -delta.y));
}
}
}