use rdom_core::NodeId;
use super::ScrollAxis;
use crate::TuiDom;
use crate::node::TuiNodeExt;
pub(super) fn set_scroll(dom: &mut TuiDom, element: NodeId, axis: ScrollAxis, value: i32) -> usize {
set_scroll_with(dom, element, axis, value, ClampTo::CurrentExtent)
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum ClampTo {
CurrentExtent,
NextLayout,
}
pub(super) fn set_scroll_with(
dom: &mut TuiDom,
element: NodeId,
axis: ScrollAxis,
value: i32,
clamp: ClampTo,
) -> usize {
crate::runtime::smooth_scroll::abort(dom, element);
let (viewport, content_size) = {
let ext = match dom.node(element).tui_ext() {
Some(e) => e,
None => return 0,
};
let border = dom
.node(element)
.computed()
.map(|c| c.border)
.unwrap_or_default();
let pb = crate::layout::compute_padding_box(ext.layout, border);
match axis {
ScrollAxis::Vertical => (pb.height as usize, ext.scroll_content_height),
ScrollAxis::Horizontal => (pb.width as usize, ext.scroll_content_width),
}
};
let max = content_size.saturating_sub(viewport) as i32;
let clamped = match clamp {
ClampTo::CurrentExtent => value.clamp(0, max),
ClampTo::NextLayout => value.max(0),
} as usize;
let changed = if let Some(ext) = dom.node_mut(element).ext_mut() {
match axis {
ScrollAxis::Vertical => {
let changed = ext.scroll_y != clamped;
ext.scroll_y = clamped;
changed
}
ScrollAxis::Horizontal => {
let changed = ext.scroll_x != clamped;
ext.scroll_x = clamped;
changed
}
}
} else {
false
};
if changed {
crate::runtime::state_writes::note();
let mut tui = crate::TuiEvent::new("scroll");
tui.event.cancelable = false;
crate::tui_event::dispatch_to_live(dom, element, &mut tui);
}
clamped
}
pub(crate) fn max_offsets(dom: &TuiDom, element: NodeId) -> Option<(i32, i32)> {
let ext = dom.node(element).tui_ext()?;
let border = dom
.node(element)
.computed()
.map(|c| c.border)
.unwrap_or_default();
let pb = crate::layout::compute_padding_box(ext.layout, border);
Some((
(ext.scroll_content_width as i32 - pb.width as i32).max(0),
(ext.scroll_content_height as i32 - pb.height as i32).max(0),
))
}
pub(crate) fn write_offsets(dom: &mut TuiDom, element: NodeId, x: i32, y: i32) -> bool {
let Some((max_x, max_y)) = max_offsets(dom, element) else {
return false;
};
let (x, y) = (x.clamp(0, max_x) as usize, y.clamp(0, max_y) as usize);
let changed = match dom.node_mut(element).ext_mut() {
Some(ext) => {
let changed = (ext.scroll_x, ext.scroll_y) != (x, y);
ext.scroll_x = x;
ext.scroll_y = y;
changed
}
None => false,
};
if changed {
crate::runtime::state_writes::note();
let mut tui = crate::TuiEvent::new("scroll");
tui.event.cancelable = false;
let live = crate::tui_event::dispatch_to_live(dom, element, &mut tui);
debug_assert!(live, "a scroll container that just scrolled is a live node");
}
changed
}