use std::sync::Arc;
use inset_embedder::{CompositionLayer, Offset, Size};
use inset_foundation::App;
use crate::box_::{BoxConstraints, RenderBox, RenderBoxData};
use crate::layer::{EmbeddedLayer, ErasedLayer};
use crate::object::{RenderHandle, RenderObject, RenderObjectData};
use crate::painting_context::PaintingContext;
pub struct RenderCompositionLayerView {
render_object: RenderObjectData,
render_box: RenderBoxData,
composition_layer: Arc<dyn CompositionLayer>,
}
impl RenderCompositionLayerView {
pub fn new(
app: &mut App,
composition_layer: Arc<dyn CompositionLayer>,
) -> RenderHandle<RenderCompositionLayerView> {
RenderHandle::new_box(
app,
RenderCompositionLayerView {
render_object: RenderObjectData::new(),
render_box: RenderBoxData::new(),
composition_layer,
},
)
}
pub fn composition_layer(self: RenderHandle<Self>, app: &App) -> &Arc<dyn CompositionLayer> {
&self.get(app).composition_layer
}
pub fn set_composition_layer(
self: RenderHandle<Self>,
app: &mut App,
composition_layer: Arc<dyn CompositionLayer>,
) {
if Arc::ptr_eq(&self.get(app).composition_layer, &composition_layer) {
return;
}
self.get_mut(app).composition_layer = composition_layer;
self.mark_needs_paint(app);
}
}
impl RenderObject for RenderCompositionLayerView {
crate::render_object_accessors!();
fn always_needs_compositing(self: RenderHandle<Self>, _app: &App) -> bool {
true
}
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let size = self.constraints(app).biggest();
self.set_size(app, size);
}
fn paint(
self: RenderHandle<Self>,
app: &mut App,
context: &mut PaintingContext,
offset: Offset,
) {
let composition_layer = Arc::clone(&self.get(app).composition_layer);
let layer = EmbeddedLayer::new(app, composition_layer, offset & self.size(app));
context.add_layer(app, layer.as_layer());
}
}
impl RenderBox for RenderCompositionLayerView {
crate::render_box_accessors!();
fn compute_dry_layout(
self: RenderHandle<Self>,
_app: &mut App,
constraints: BoxConstraints,
) -> Size {
constraints.biggest()
}
fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use inset_embedder::test_support::idle_layer;
use inset_embedder::{Clip, Matrix4, Mutator, Part, Rect, SceneBuilder};
use inset_foundation::{AppCell, Handle};
use super::*;
use crate::layer::{ContainerLayer, OffsetLayer};
use crate::object::AnyRenderObject;
use crate::pipeline_owner::PipelineOwner;
const CHILD_OFFSET: Offset = Offset::new(10.0, 5.0);
struct Parent {
render_object: RenderObjectData,
render_box: RenderBoxData,
child: RenderHandle<RenderCompositionLayerView>,
painting: ChildPainting,
}
#[derive(Clone, Copy)]
enum ChildPainting {
AtOffset,
HalvedUnderAClip,
}
impl RenderObject for Parent {
crate::render_object_accessors!();
fn is_repaint_boundary(self: RenderHandle<Self>, _app: &App) -> bool {
true
}
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let child = self.get(app).child;
child.layout(app, BoxConstraints::tight(Size::new(30.0, 20.0)), false);
self.set_size(app, Size::new(100.0, 100.0));
}
fn visit_children(
self: RenderHandle<Self>,
app: &App,
visitor: &mut dyn FnMut(AnyRenderObject),
) {
visitor(self.get(app).child.as_object());
}
fn paint(
self: RenderHandle<Self>,
app: &mut App,
context: &mut PaintingContext,
offset: Offset,
) {
let child = self.get(app).child.as_object();
match self.get(app).painting {
ChildPainting::AtOffset => context.paint_child(app, child, offset + CHILD_OFFSET),
ChildPainting::HalvedUnderAClip => {
let needs_compositing = self.as_object().needs_compositing(app);
context.push_clip_rect(
app,
needs_compositing,
offset,
Rect::from_ltwh(0.0, 0.0, 100.0, 10.0),
|app, context, offset| {
context.push_transform(
app,
needs_compositing,
offset + CHILD_OFFSET,
Matrix4::scale(0.5, 0.5),
|app, context, offset| context.paint_child(app, child, offset),
None,
);
},
Clip::HardEdge,
None,
);
}
}
}
}
impl RenderBox for Parent {
crate::render_box_accessors!();
}
fn scene_of_one_frame(
app: &mut App,
composition_layer: Arc<dyn CompositionLayer>,
painting: ChildPainting,
) -> (inset_embedder::Scene, Handle<PipelineOwner>) {
let owner = PipelineOwner::new(app, None, None);
let child = RenderCompositionLayerView::new(app, composition_layer);
let parent = RenderHandle::new_box(
app,
Parent {
render_object: RenderObjectData::new(),
render_box: RenderBoxData::new(),
child,
painting,
},
);
parent.adopt_child(app, child.as_object());
owner.set_root_node(app, Some(parent.as_object()));
parent.schedule_initial_layout(app);
let root = OffsetLayer::new(app, Offset::ZERO);
root.as_layer().attach(app, parent.as_object().id());
parent
.as_object()
.schedule_initial_paint(app, root.as_container_layer());
owner.flush_layout(app);
owner.flush_compositing_bits(app);
owner.flush_paint(app);
let scene = root
.as_container_layer()
.build_scene(app, SceneBuilder::new());
(scene, owner)
}
#[test]
fn the_box_is_placed_and_nothing_is_painted() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let composition_layer = idle_layer();
let (scene, _owner) = scene_of_one_frame(
&mut app,
Arc::clone(&composition_layer),
ChildPainting::AtOffset,
);
let slices = inset_embedder::slices(&scene, |_| None);
let [Part::CompositionLayer { layer, placement }] = slices.parts() else {
panic!("the composition layer alone: {:?}", slices.parts());
};
assert!(Arc::ptr_eq(layer, &composition_layer));
let placed = Rect::from_ltwh(10.0, 5.0, 30.0, 20.0);
assert_eq!(placement.bounds(), placed);
assert_eq!(placement.visible_bounds(), placed);
assert!(!slices.paints());
}
#[test]
fn a_composition_layer_is_placed_under_the_transform_and_the_clip_above_it() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let (scene, _owner) =
scene_of_one_frame(&mut app, idle_layer(), ChildPainting::HalvedUnderAClip);
let slices = inset_embedder::slices(&scene, |_| None);
let (_, placement) = slices.composition_layers().next().expect("placed");
assert!(
placement
.mutators
.contains(&Mutator::ClipRect(Rect::from_ltwh(0.0, 0.0, 100.0, 10.0)))
);
assert_eq!(placement.scale(), 0.5);
assert_eq!(placement.bounds(), Rect::from_ltwh(10.0, 5.0, 15.0, 10.0));
assert_eq!(
placement.visible_bounds(),
Rect::from_ltrb(10.0, 5.0, 25.0, 10.0)
);
}
}