use std::cell::Cell;
use crate::draw_ctx::DrawCtx;
use crate::geometry::{Rect, Size};
use crate::layout_props::Insets;
thread_local! {
static RESERVED: Cell<Insets> = const {
Cell::new(Insets {
top: 0.0,
bottom: 0.0,
left: 0.0,
right: 0.0,
})
};
}
pub fn begin_frame() {
RESERVED.with(|c| {
c.set(Insets {
top: 0.0,
bottom: 0.0,
left: 0.0,
right: 0.0,
})
});
}
pub fn reserve(insets: Insets) {
RESERVED.with(|c| {
let cur = c.get();
c.set(Insets {
top: cur.top.max(insets.top),
bottom: cur.bottom.max(insets.bottom),
left: cur.left.max(insets.left),
right: cur.right.max(insets.right),
});
});
}
pub fn current() -> Insets {
RESERVED.with(|c| c.get())
}
pub fn clip_to(container: Rect, insets: Insets, viewport: Size) -> Insets {
Insets {
left: (insets.left - container.x).max(0.0),
bottom: (insets.bottom - container.y).max(0.0),
right: ((container.x + container.width) - (viewport.width - insets.right)).max(0.0),
top: ((container.y + container.height) - (viewport.height - insets.top)).max(0.0),
}
}
pub fn for_paint_ctx(ctx: &dyn DrawCtx, local_size: Size) -> Insets {
let t = ctx.root_transform();
let (mut x0, mut y0) = (0.0, 0.0);
let (mut x1, mut y1) = (local_size.width, local_size.height);
t.transform(&mut x0, &mut y0);
t.transform(&mut x1, &mut y1);
let s = crate::ux_scale::effective_scale().max(1e-6);
let abs = Rect::new(
x0.min(x1) / s,
y0.min(y1) / s,
(x1 - x0).abs() / s,
(y1 - y0).abs() / s,
);
clip_to(abs, current(), crate::widget::current_viewport())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clip_to_converts_viewport_insets_into_container_space() {
let viewport = Size::new(400.0, 800.0);
let container = Rect::new(0.0, 200.0, 400.0, 600.0);
let ins = Insets {
left: 56.0,
bottom: 300.0, right: 0.0,
top: 0.0,
};
let local = clip_to(container, ins, viewport);
assert_eq!(local.left, 56.0);
assert_eq!(local.bottom, 100.0, "only the intruding part remains");
assert_eq!(local.right, 0.0);
assert_eq!(local.top, 0.0);
let shallow = Insets {
bottom: 150.0,
..Insets::default()
};
assert_eq!(clip_to(container, shallow, viewport).bottom, 0.0);
}
#[test]
fn reservations_max_merge_and_reset() {
begin_frame();
reserve(Insets {
left: 56.0,
bottom: 40.0,
..Insets::default()
});
reserve(Insets {
left: 30.0,
bottom: 120.0,
..Insets::default()
});
let r = current();
assert_eq!(r.left, 56.0, "wider left rail wins");
assert_eq!(r.bottom, 120.0, "taller bottom strip wins");
assert_eq!(r.right, 0.0);
begin_frame();
assert_eq!(current(), Insets::default(), "new frame starts clean");
}
}