use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::rc::Rc;
use inset_embedder::{Offset, Size};
use inset_foundation::{App, Handle, Key, Listenable, Listener};
use crate::box_::{
AnyRenderBox, BoxConstraints, BoxHitTestResult, BoxParentData, ContainerBoxParentData,
RenderBox, RenderBoxContainerDefaultsMixin, RenderBoxData,
};
use crate::object::{
AnyRenderObject, Constraints, ContainerParentData, ContainerParentDataMixin,
ContainerRenderObjectData, ContainerRenderObjectMixin, ParentData, RenderHandle, RenderObject,
RenderObjectData,
};
use crate::painting_context::PaintingContext;
use crate::pipeline_owner::PipelineOwner;
pub type ChildLayoutId = Rc<dyn Key>;
#[derive(Debug)]
pub struct MultiChildLayoutParentData {
box_parent_data: BoxParentData,
container_parent_data: ContainerParentData<AnyRenderBox>,
pub id: Option<ChildLayoutId>,
}
impl MultiChildLayoutParentData {
pub const fn new() -> MultiChildLayoutParentData {
MultiChildLayoutParentData {
box_parent_data: BoxParentData::new(),
container_parent_data: ContainerParentData::new(),
id: None,
}
}
}
impl Default for MultiChildLayoutParentData {
fn default() -> MultiChildLayoutParentData {
MultiChildLayoutParentData::new()
}
}
impl ParentData for MultiChildLayoutParentData {
fn detach(&mut self) {
ContainerParentDataMixin::detach(self);
}
fn provide(&self, id: TypeId) -> Option<&dyn Any> {
if id == TypeId::of::<MultiChildLayoutParentData>() {
return Some(self);
}
self.box_parent_data.provide(id)
}
fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
if id == TypeId::of::<MultiChildLayoutParentData>() {
return Some(self);
}
self.box_parent_data.provide_mut(id)
}
}
impl ContainerParentDataMixin for MultiChildLayoutParentData {
type ChildType = AnyRenderBox;
fn container_parent_data(&self) -> &ContainerParentData<AnyRenderBox> {
&self.container_parent_data
}
fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderBox> {
&mut self.container_parent_data
}
}
impl ContainerBoxParentData for MultiChildLayoutParentData {
fn box_parent_data(&self) -> &BoxParentData {
&self.box_parent_data
}
fn box_parent_data_mut(&mut self) -> &mut BoxParentData {
&mut self.box_parent_data
}
}
impl fmt::Display for MultiChildLayoutParentData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}; id={:?}", self.box_parent_data, self.id)
}
}
pub struct MultiChildLayoutChildren {
id_to_child: HashMap<ChildLayoutId, AnyRenderBox>,
#[cfg(debug_assertions)]
children_needing_layout: std::collections::HashSet<AnyRenderBox>,
}
impl MultiChildLayoutChildren {
pub fn has_child(&self, child_id: &ChildLayoutId) -> bool {
self.id_to_child.contains_key(child_id)
}
pub fn layout_child(
&mut self,
app: &mut App,
child_id: &ChildLayoutId,
constraints: BoxConstraints,
) -> Size {
let child = *self.id_to_child.get(child_id).unwrap_or_else(|| {
panic!(
"The custom multichild layout delegate tried to lay out a non-existent child. \
There is no child with the id {child_id:?}."
)
});
#[cfg(debug_assertions)]
assert!(
self.children_needing_layout.remove(&child),
"The custom multichild layout delegate tried to lay out the child with id \
{child_id:?} more than once. Each child must be laid out exactly once."
);
debug_assert!(
constraints.debug_assert_is_valid(true),
"The custom multichild layout delegate provided invalid box constraints for the \
child with id {child_id:?}. The minimum width and height must be greater than or \
equal to zero. The maximum width must be greater than or equal to the minimum \
width. The maximum height must be greater than or equal to the minimum height."
);
child.layout(app, constraints, true);
child.size(app)
}
pub fn position_child(&self, app: &mut App, child_id: &ChildLayoutId, offset: Offset) {
let child = *self.id_to_child.get(child_id).unwrap_or_else(|| {
panic!(
"The custom multichild layout delegate tried to position out a non-existent \
child: there is no child with the id {child_id:?}."
)
});
child
.parent_data_of_mut::<MultiChildLayoutParentData>(app)
.set_offset(offset);
}
}
pub trait MultiChildLayoutDelegate: Debug + 'static {
fn relayout(&self) -> Option<&Rc<dyn Listenable>> {
None
}
fn get_size(&self, constraints: BoxConstraints) -> Size {
constraints.biggest()
}
fn perform_layout(&self, app: &mut App, children: &mut MultiChildLayoutChildren, size: Size);
fn should_relayout(&self, old_delegate: &dyn MultiChildLayoutDelegate) -> bool;
fn as_any(&self) -> &dyn Any;
}
pub struct RenderCustomMultiChildLayoutBox {
render_object: RenderObjectData,
render_box: RenderBoxData,
container: ContainerRenderObjectData<AnyRenderBox>,
delegate: Rc<dyn MultiChildLayoutDelegate>,
}
impl RenderCustomMultiChildLayoutBox {
pub fn new(
app: &mut App,
delegate: Rc<dyn MultiChildLayoutDelegate>,
) -> RenderHandle<RenderCustomMultiChildLayoutBox> {
RenderHandle::new_box(
app,
RenderCustomMultiChildLayoutBox {
render_object: RenderObjectData::new(),
render_box: RenderBoxData::new(),
container: ContainerRenderObjectData::new(),
delegate,
},
)
}
pub fn delegate(self: RenderHandle<Self>, app: &App) -> Rc<dyn MultiChildLayoutDelegate> {
Rc::clone(&self.get(app).delegate)
}
pub fn set_delegate(
self: RenderHandle<Self>,
app: &mut App,
new_delegate: Rc<dyn MultiChildLayoutDelegate>,
) {
let old_delegate = Rc::clone(&self.get(app).delegate);
if Rc::ptr_eq(&old_delegate, &new_delegate) {
return;
}
if new_delegate.as_any().type_id() != old_delegate.as_any().type_id()
|| new_delegate.should_relayout(&*old_delegate)
{
self.mark_needs_layout(app);
}
self.get_mut(app).delegate = Rc::clone(&new_delegate);
if self.attached(app) {
if let Some(relayout) = old_delegate.relayout() {
relayout.remove_listener(app, &self.relayout_listener());
}
if let Some(relayout) = new_delegate.relayout() {
relayout.add_listener(app, self.relayout_listener());
}
}
}
fn relayout_listener(self: RenderHandle<Self>) -> Listener {
Listener::handle_method(self.handle(), mark_needs_layout)
}
fn get_size(self: RenderHandle<Self>, app: &App, constraints: BoxConstraints) -> Size {
debug_assert!(constraints.debug_assert_is_valid(false));
constraints.constrain(self.get(app).delegate.get_size(constraints))
}
fn call_perform_layout(self: RenderHandle<Self>, app: &mut App, size: Size) {
let mut children = MultiChildLayoutChildren {
id_to_child: HashMap::new(),
#[cfg(debug_assertions)]
children_needing_layout: std::collections::HashSet::new(),
};
let mut child = self.first_child(app);
while let Some(current) = child {
let child_parent_data = current
.as_object()
.parent_data_of::<MultiChildLayoutParentData>(app);
let (id, next_sibling) = (
child_parent_data.id.clone(),
child_parent_data.next_sibling(),
);
let id = id.unwrap_or_else(|| {
panic!(
"Every child of a RenderCustomMultiChildLayoutBox must have an ID in its \
parent data. The following child has no ID: {current:?}"
)
});
children.id_to_child.insert(id, current);
#[cfg(debug_assertions)]
children.children_needing_layout.insert(current);
child = next_sibling;
}
let delegate = Rc::clone(&self.get(app).delegate);
delegate.perform_layout(app, &mut children, size);
#[cfg(debug_assertions)]
assert!(
children.children_needing_layout.is_empty(),
"Each child must be laid out exactly once. The custom multichild layout delegate \
forgot to lay out {:?}",
children.children_needing_layout
);
}
}
fn mark_needs_layout(this: Handle<RenderCustomMultiChildLayoutBox>, app: &mut App) {
RenderHandle::from_handle(this).mark_needs_layout(app);
}
impl ContainerRenderObjectMixin for RenderCustomMultiChildLayoutBox {
type ChildType = AnyRenderBox;
type ParentDataType = MultiChildLayoutParentData;
fn container_data(
self: RenderHandle<Self>,
app: &App,
) -> &ContainerRenderObjectData<AnyRenderBox> {
&self.get(app).container
}
fn container_data_mut(
self: RenderHandle<Self>,
app: &mut App,
) -> &mut ContainerRenderObjectData<AnyRenderBox> {
&mut self.get_mut(app).container
}
}
impl RenderBoxContainerDefaultsMixin for RenderCustomMultiChildLayoutBox {}
impl RenderObject for RenderCustomMultiChildLayoutBox {
crate::render_object_accessors!();
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let constraints = self.constraints(app);
let size = self.get_size(app, constraints);
self.set_size(app, size);
self.call_perform_layout(app, size);
}
fn visit_children(
self: RenderHandle<Self>,
app: &App,
visitor: &mut dyn FnMut(AnyRenderObject),
) {
ContainerRenderObjectMixin::visit_children(self, app, visitor)
}
fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
ContainerRenderObjectMixin::did_attach(self, app, owner);
if let Some(relayout) = self.delegate(app).relayout() {
relayout.add_listener(app, self.relayout_listener());
}
}
fn did_detach(self: RenderHandle<Self>, app: &mut App) {
if let Some(relayout) = self.delegate(app).relayout() {
relayout.remove_listener(app, &self.relayout_listener());
}
ContainerRenderObjectMixin::did_detach(self, app)
}
fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
ContainerRenderObjectMixin::redepth_children(self, app)
}
fn paint(
self: RenderHandle<Self>,
app: &mut App,
context: &mut PaintingContext,
offset: Offset,
) {
self.default_paint(app, context, offset);
}
}
impl RenderBox for RenderCustomMultiChildLayoutBox {
crate::render_box_accessors!();
fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
if !child.parent_data_is::<MultiChildLayoutParentData>(app) {
child.set_parent_data(app, MultiChildLayoutParentData::new());
}
}
fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
let width = self
.get_size(app, BoxConstraints::tight_for_finite(f64::INFINITY, height))
.width();
if width.is_finite() { width } else { 0.0 }
}
fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
let width = self
.get_size(app, BoxConstraints::tight_for_finite(f64::INFINITY, height))
.width();
if width.is_finite() { width } else { 0.0 }
}
fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
let height = self
.get_size(app, BoxConstraints::tight_for_finite(width, f64::INFINITY))
.height();
if height.is_finite() { height } else { 0.0 }
}
fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
let height = self
.get_size(app, BoxConstraints::tight_for_finite(width, f64::INFINITY))
.height();
if height.is_finite() { height } else { 0.0 }
}
fn compute_dry_layout(
self: RenderHandle<Self>,
app: &mut App,
constraints: BoxConstraints,
) -> Size {
self.get_size(app, constraints)
}
fn hit_test_children(
self: RenderHandle<Self>,
app: &mut App,
result: &mut BoxHitTestResult<'_>,
position: Offset,
) -> bool {
self.default_hit_test_children(app, result, position)
}
}