use {
crate::{
components::{Component, effect::ScreenEffect},
surface::Surface,
},
futures_signals::signal::Mutable,
ratatui::prelude::{Buffer, Frame, Rect},
};
pub struct Layer {
render: Box<dyn FnMut(Rect, &mut Buffer)>,
effect: Option<Box<dyn ScreenEffect>>,
visible: Mutable<bool>,
opacity: Mutable<f64>,
x_offset: u16,
y_offset: u16,
}
impl Layer {
pub fn new<F: FnMut(Rect, &mut Buffer) + 'static>(f: F) -> Self {
Self {
render: Box::new(f),
effect: None,
visible: Mutable::new(true),
opacity: Mutable::new(1.0),
x_offset: 0,
y_offset: 0,
}
}
pub fn with_effect(mut self, effect: impl ScreenEffect + 'static) -> Self {
self.effect = Some(Box::new(effect));
self
}
pub fn x(mut self, v: u16) -> Self {
self.x_offset = v;
self
}
pub fn y(mut self, v: u16) -> Self {
self.y_offset = v;
self
}
pub fn set_visible(&self, v: bool) { self.visible.set(v); }
pub fn set_opacity(&self, v: f64) { self.opacity.set(v.clamp(0.0, 1.0)); }
}
pub struct LayerStack {
layers: Vec<Layer>,
}
impl LayerStack {
pub fn new() -> Self { Self { layers: Vec::new() } }
pub fn push(&mut self, layer: Layer) { self.layers.push(layer); }
pub fn pop(&mut self) -> Option<Layer> { self.layers.pop() }
pub fn len(&self) -> usize { self.layers.len() }
pub fn is_empty(&self) -> bool { self.layers.is_empty() }
pub fn render_to_buf(&mut self, area: Rect, buf: &mut Buffer, t: f64) {
for layer in &mut self.layers {
if !layer.visible.get() {
continue;
}
let layer_area = Rect {
x: area.x + layer.x_offset,
y: area.y + layer.y_offset,
width: area.width.saturating_sub(layer.x_offset),
height: area.height.saturating_sub(layer.y_offset),
};
if layer_area.width == 0 || layer_area.height == 0 {
continue;
}
let mut scratch = Buffer::empty(layer_area);
scratch.merge(buf);
(layer.render)(layer_area, &mut scratch);
if let Some(fx) = &layer.effect {
fx.apply(layer_area, &mut scratch, t);
}
buf.merge(&scratch);
}
}
}
impl Component for LayerStack {
fn render(&mut self, ctx: &mut Frame<'_>, theme: &crate::theme::Theme) {
let area = ctx.area();
let buf = ctx.buffer_mut();
for y in area.top()..area.bottom() {
for x in area.left()..area.right() {
buf[(x, y)].set_bg(theme.global_bg);
}
}
self.render_to_buf(area, buf, 0.0);
}
}
#[derive(Debug, Clone)]
pub struct CompositorLayer {
pub surface: Surface,
pub x: u16,
pub y: u16,
pub visible: bool,
}
impl CompositorLayer {
pub fn new(surface: Surface) -> Self { Self { surface, x: 0, y: 0, visible: true } }
pub fn x(mut self, v: u16) -> Self {
self.x = v;
self
}
pub fn y(mut self, v: u16) -> Self {
self.y = v;
self
}
}
pub struct Compositor {
layers: Vec<CompositorLayer>,
}
impl Compositor {
pub fn new(layers: Vec<CompositorLayer>) -> Self { Self { layers } }
pub fn push(&mut self, layer: CompositorLayer) { self.layers.push(layer); }
pub fn render_to_buf(&self, area: Rect, buf: &mut Buffer) {
for layer in &self.layers {
if !layer.visible {
continue;
}
layer.surface.blit(buf, area.x + layer.x, area.y + layer.y);
}
}
}