use crate::index::HeightIndex;
use crate::message::MessageId;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Gravity {
Bottom,
Free,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ScrollAnchor {
pub message: Option<MessageId>,
pub offset: u32,
pub gravity: Gravity,
}
impl Default for ScrollAnchor {
fn default() -> Self {
ScrollAnchor {
message: None,
offset: 0,
gravity: Gravity::Bottom,
}
}
}
impl ScrollAnchor {
pub fn bottom() -> ScrollAnchor {
ScrollAnchor::default()
}
pub fn is_following(&self) -> bool {
self.gravity == Gravity::Bottom
}
}
#[derive(Debug)]
pub struct AnchorResolver;
impl AnchorResolver {
pub fn to_pixels(
anchor: &ScrollAnchor,
index: &mut HeightIndex,
viewport_height: u32,
row_of: impl Fn(MessageId) -> Option<usize>,
) -> u64 {
let total = index.total_height();
let max = total.saturating_sub(u64::from(viewport_height));
match anchor.message {
None => max,
Some(id) => match row_of(id) {
Some(row) => {
let base = index.offset_of(row);
let y = base + u64::from(anchor.offset);
y.min(max)
}
None => max,
},
}
}
pub fn from_pixels(
y: u64,
index: &mut HeightIndex,
viewport_height: u32,
follow_slop: u32,
id_of_row: impl Fn(usize) -> Option<MessageId>,
) -> ScrollAnchor {
let total = index.total_height();
let max = total.saturating_sub(u64::from(viewport_height));
if y + u64::from(follow_slop) >= max {
return ScrollAnchor::bottom();
}
match index.locate(y) {
Some(hit) => ScrollAnchor {
message: id_of_row(hit.row),
offset: hit.offset,
gravity: Gravity::Free,
},
None => ScrollAnchor::bottom(),
}
}
}