use super::*;
mod offscreen;
use offscreen::{
paint_subtree_backbuffered, paint_subtree_layer, paint_subtree_unified_backbuffer,
};
std::thread_local! {
static PAINT_CLIP_STACK: std::cell::RefCell<Vec<Rect>> =
std::cell::RefCell::new(Vec::new());
}
pub fn current_paint_clip() -> Option<Rect> {
PAINT_CLIP_STACK.with(|stack| stack.borrow().last().copied())
}
pub fn paint_subtree(widget: &mut dyn Widget, ctx: &mut dyn DrawCtx) {
if widget.is_visible() && widget.defer_paint_to_overlay() {
return;
}
paint_subtree_forced(widget, ctx);
}
pub(crate) fn paint_subtree_forced(widget: &mut dyn Widget, ctx: &mut dyn DrawCtx) {
if !widget.is_visible() {
if paint_subtree_unified_backbuffer(widget, ctx, true) {
return;
}
if ctx.supports_compositing_layers() {
if let Some(layer) = widget.compositing_layer() {
paint_subtree_layer(widget, ctx, true, layer);
}
}
return;
}
if paint_subtree_unified_backbuffer(widget, ctx, true) {
return;
} else if widget.backbuffer_cache_mut().is_some() {
paint_subtree_backbuffered(widget, ctx);
} else {
paint_subtree_direct(widget, ctx);
}
}
pub fn paint_global_overlays(widget: &mut dyn Widget, ctx: &mut dyn DrawCtx) {
if !widget.is_visible() {
return;
}
let n = widget.children().len();
for i in 0..n {
let child = &mut widget.children_mut()[i];
let b = child.bounds();
ctx.save();
ctx.translate(b.x, b.y);
paint_global_overlays(child.as_mut(), ctx);
ctx.restore();
}
widget.paint_global_overlay(ctx);
}
fn paint_subtree_direct(widget: &mut dyn Widget, ctx: &mut dyn DrawCtx) {
paint_subtree_direct_inner(widget, ctx, true, true);
}
fn paint_subtree_direct_no_overlay(widget: &mut dyn Widget, ctx: &mut dyn DrawCtx) {
paint_subtree_direct_inner(widget, ctx, false, true);
}
fn paint_subtree_direct_inner(
widget: &mut dyn Widget,
ctx: &mut dyn DrawCtx,
include_overlay: bool,
allow_compositing_layer: bool,
) {
if allow_compositing_layer && ctx.supports_compositing_layers() {
if let Some(layer) = widget.compositing_layer() {
paint_subtree_layer(widget, ctx, include_overlay, layer);
return;
}
}
let snap_this = widget.enforce_integer_bounds();
if snap_this {
ctx.save();
ctx.snap_to_pixel();
}
widget.paint(ctx);
let b = widget.bounds();
let (cx, cy, cw, ch) = widget
.clip_children_rect()
.unwrap_or((0.0, 0.0, b.width, b.height));
ctx.save();
ctx.clip_rect(cx, cy, cw, ch);
let clip = root_rect_from_local(ctx, cx, cy, cw, ch);
PAINT_CLIP_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
let clipped = if let Some(prev) = stack.last().copied() {
intersect_rects(prev, clip).unwrap_or_else(|| Rect::new(0.0, 0.0, 0.0, 0.0))
} else {
clip
};
stack.push(clipped);
});
if let Some(t) = widget.child_transform() {
let mut m = ctx.transform();
m.premultiply(&t);
ctx.set_transform(m);
}
let n = widget.children().len();
for i in 0..n {
let child_bounds = widget.children()[i].bounds();
let snap_to_pixel = widget.children()[i].enforce_integer_bounds();
ctx.save();
if snap_to_pixel {
ctx.translate(child_bounds.x.round(), child_bounds.y.round());
} else {
ctx.translate(child_bounds.x, child_bounds.y);
}
let child = &mut widget.children_mut()[i];
paint_subtree(child.as_mut(), ctx);
ctx.restore();
}
PAINT_CLIP_STACK.with(|stack| {
stack.borrow_mut().pop();
});
ctx.restore(); if include_overlay {
widget.paint_overlay(ctx);
}
widget.finish_paint(ctx);
if snap_this {
ctx.restore();
}
}
fn root_rect_from_local(ctx: &dyn DrawCtx, x: f64, y: f64, w: f64, h: f64) -> Rect {
let mut points = [(x, y), (x + w, y), (x, y + h), (x + w, y + h)];
let transform = ctx.root_transform();
for (px, py) in &mut points {
transform.transform(px, py);
}
let min_x = points.iter().map(|(x, _)| *x).fold(f64::INFINITY, f64::min);
let max_x = points
.iter()
.map(|(x, _)| *x)
.fold(f64::NEG_INFINITY, f64::max);
let min_y = points.iter().map(|(_, y)| *y).fold(f64::INFINITY, f64::min);
let max_y = points
.iter()
.map(|(_, y)| *y)
.fold(f64::NEG_INFINITY, f64::max);
Rect::new(
min_x,
min_y,
(max_x - min_x).max(0.0),
(max_y - min_y).max(0.0),
)
}
fn intersect_rects(a: Rect, b: Rect) -> Option<Rect> {
let x0 = a.x.max(b.x);
let y0 = a.y.max(b.y);
let x1 = (a.x + a.width).min(b.x + b.width);
let y1 = (a.y + a.height).min(b.y + b.height);
(x1 >= x0 && y1 >= y0).then(|| Rect::new(x0, y0, x1 - x0, y1 - y0))
}