use std::any::{Any, TypeId};
use std::fmt::{self, Debug, Display};
use std::hash::Hasher;
use std::ops::{Deref, DerefMut};
use inset_embedder::{Matrix4, Offset, Rect, Size, clamp_double};
use inset_foundation::{App, Handle, HandleId, PRECISION_ERROR_TOLERANCE};
use inset_gestures::{HitTestEntry, HitTestResult, HitTestTarget, PointerEvent};
use inset_painting::{
Axis, AxisDirection, axis_direction_is_reversed, axis_direction_to_axis, flip_axis_direction,
};
use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult};
use crate::object::{
AnyRenderObject, Constraints, ContainerParentData, ContainerParentDataMixin, ParentData,
RenderHandle, RenderObject, RenderObjectVTable, RenderObjectWithChildData,
RenderObjectWithChildMixin, create, resolve, translate,
};
use crate::painting_context::PaintingContext;
use crate::pipeline_owner::PipelineOwner;
use crate::viewport_offset::{ScrollDirection, flip_scroll_direction};
pub type ItemExtentBuilder = std::rc::Rc<dyn Fn(i32, SliverLayoutDimensions) -> Option<f64>>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SliverLayoutDimensions {
pub scroll_offset: f64,
pub preceding_scroll_extent: f64,
pub viewport_main_axis_extent: f64,
pub cross_axis_extent: f64,
}
impl SliverLayoutDimensions {
pub const fn new(
scroll_offset: f64,
preceding_scroll_extent: f64,
viewport_main_axis_extent: f64,
cross_axis_extent: f64,
) -> SliverLayoutDimensions {
SliverLayoutDimensions {
scroll_offset,
preceding_scroll_extent,
viewport_main_axis_extent,
cross_axis_extent,
}
}
}
impl Display for SliverLayoutDimensions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"scrollOffset: {} precedingScrollExtent: {} viewportMainAxisExtent: {} \
crossAxisExtent: {}",
self.scroll_offset,
self.preceding_scroll_extent,
self.viewport_main_axis_extent,
self.cross_axis_extent
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GrowthDirection {
Forward,
Reverse,
}
pub fn apply_growth_direction_to_axis_direction(
axis_direction: AxisDirection,
growth_direction: GrowthDirection,
) -> AxisDirection {
match growth_direction {
GrowthDirection::Forward => axis_direction,
GrowthDirection::Reverse => flip_axis_direction(axis_direction),
}
}
pub fn apply_growth_direction_to_scroll_direction(
scroll_direction: ScrollDirection,
growth_direction: GrowthDirection,
) -> ScrollDirection {
match growth_direction {
GrowthDirection::Forward => scroll_direction,
GrowthDirection::Reverse => flip_scroll_direction(scroll_direction),
}
}
#[derive(Clone, Copy, Debug)]
pub struct SliverConstraints {
pub axis_direction: AxisDirection,
pub growth_direction: GrowthDirection,
pub user_scroll_direction: ScrollDirection,
pub scroll_offset: f64,
pub preceding_scroll_extent: f64,
pub overlap: f64,
pub remaining_paint_extent: f64,
pub cross_axis_extent: f64,
pub cross_axis_direction: AxisDirection,
pub viewport_main_axis_extent: f64,
pub remaining_cache_extent: f64,
pub cache_origin: f64,
}
impl SliverConstraints {
#[allow(clippy::too_many_arguments)]
pub const fn new(
axis_direction: AxisDirection,
growth_direction: GrowthDirection,
user_scroll_direction: ScrollDirection,
scroll_offset: f64,
preceding_scroll_extent: f64,
overlap: f64,
remaining_paint_extent: f64,
cross_axis_extent: f64,
cross_axis_direction: AxisDirection,
viewport_main_axis_extent: f64,
remaining_cache_extent: f64,
cache_origin: f64,
) -> SliverConstraints {
SliverConstraints {
axis_direction,
growth_direction,
user_scroll_direction,
scroll_offset,
preceding_scroll_extent,
overlap,
remaining_paint_extent,
cross_axis_extent,
cross_axis_direction,
viewport_main_axis_extent,
remaining_cache_extent,
cache_origin,
}
}
pub const fn copy_with(self) -> SliverConstraints {
self
}
pub const fn axis_direction(mut self, value: AxisDirection) -> SliverConstraints {
self.axis_direction = value;
self
}
pub const fn growth_direction(mut self, value: GrowthDirection) -> SliverConstraints {
self.growth_direction = value;
self
}
pub const fn user_scroll_direction(mut self, value: ScrollDirection) -> SliverConstraints {
self.user_scroll_direction = value;
self
}
pub const fn scroll_offset(mut self, value: f64) -> SliverConstraints {
self.scroll_offset = value;
self
}
pub const fn preceding_scroll_extent(mut self, value: f64) -> SliverConstraints {
self.preceding_scroll_extent = value;
self
}
pub const fn overlap(mut self, value: f64) -> SliverConstraints {
self.overlap = value;
self
}
pub const fn remaining_paint_extent(mut self, value: f64) -> SliverConstraints {
self.remaining_paint_extent = value;
self
}
pub const fn cross_axis_extent(mut self, value: f64) -> SliverConstraints {
self.cross_axis_extent = value;
self
}
pub const fn cross_axis_direction(mut self, value: AxisDirection) -> SliverConstraints {
self.cross_axis_direction = value;
self
}
pub const fn viewport_main_axis_extent(mut self, value: f64) -> SliverConstraints {
self.viewport_main_axis_extent = value;
self
}
pub const fn remaining_cache_extent(mut self, value: f64) -> SliverConstraints {
self.remaining_cache_extent = value;
self
}
pub const fn cache_origin(mut self, value: f64) -> SliverConstraints {
self.cache_origin = value;
self
}
pub fn axis(self) -> Axis {
axis_direction_to_axis(self.axis_direction)
}
pub fn normalized_growth_direction(self) -> GrowthDirection {
if axis_direction_is_reversed(self.axis_direction) {
match self.growth_direction {
GrowthDirection::Forward => GrowthDirection::Reverse,
GrowthDirection::Reverse => GrowthDirection::Forward,
}
} else {
self.growth_direction
}
}
pub fn as_box_constraints(
self,
min_extent: f64,
max_extent: f64,
cross_axis_extent: Option<f64>,
) -> BoxConstraints {
let cross = cross_axis_extent.unwrap_or(self.cross_axis_extent);
match self.axis() {
Axis::Horizontal => BoxConstraints {
min_width: min_extent,
max_width: max_extent,
min_height: cross,
max_height: cross,
},
Axis::Vertical => BoxConstraints {
min_width: cross,
max_width: cross,
min_height: min_extent,
max_height: max_extent,
},
}
}
}
impl Constraints for SliverConstraints {
fn is_tight(&self) -> bool {
false
}
fn is_normalized(&self) -> bool {
self.scroll_offset >= 0.0
&& self.cross_axis_extent >= 0.0
&& axis_direction_to_axis(self.axis_direction)
!= axis_direction_to_axis(self.cross_axis_direction)
&& self.viewport_main_axis_extent >= 0.0
&& self.remaining_paint_extent >= 0.0
}
}
impl PartialEq for SliverConstraints {
fn eq(&self, other: &SliverConstraints) -> bool {
debug_assert!(self.debug_assert_is_valid(false));
debug_assert!(other.debug_assert_is_valid(false));
self.axis_direction == other.axis_direction
&& self.growth_direction == other.growth_direction
&& self.user_scroll_direction == other.user_scroll_direction
&& self.scroll_offset == other.scroll_offset
&& self.preceding_scroll_extent == other.preceding_scroll_extent
&& self.overlap == other.overlap
&& self.remaining_paint_extent == other.remaining_paint_extent
&& self.cross_axis_extent == other.cross_axis_extent
&& self.cross_axis_direction == other.cross_axis_direction
&& self.viewport_main_axis_extent == other.viewport_main_axis_extent
&& self.remaining_cache_extent == other.remaining_cache_extent
&& self.cache_origin == other.cache_origin
}
}
#[derive(Clone, Copy, Debug)]
pub struct SliverGeometry {
pub scroll_extent: f64,
pub paint_origin: f64,
pub paint_extent: f64,
pub layout_extent: f64,
pub max_paint_extent: f64,
pub max_scroll_obstruction_extent: f64,
pub hit_test_extent: f64,
pub visible: bool,
pub has_visual_overflow: bool,
pub scroll_offset_correction: Option<f64>,
pub cache_extent: f64,
pub cross_axis_extent: Option<f64>,
}
impl SliverGeometry {
pub const ZERO: SliverGeometry = SliverGeometry {
scroll_extent: 0.0,
paint_origin: 0.0,
paint_extent: 0.0,
layout_extent: 0.0,
max_paint_extent: 0.0,
max_scroll_obstruction_extent: 0.0,
hit_test_extent: 0.0,
visible: false,
has_visual_overflow: false,
scroll_offset_correction: None,
cache_extent: 0.0,
cross_axis_extent: None,
};
pub const fn builder() -> SliverGeometryBuilder {
SliverGeometryBuilder {
scroll_extent: 0.0,
paint_origin: 0.0,
paint_extent: 0.0,
layout_extent: None,
max_paint_extent: 0.0,
max_scroll_obstruction_extent: 0.0,
hit_test_extent: None,
visible: None,
has_visual_overflow: false,
scroll_offset_correction: None,
cache_extent: None,
cross_axis_extent: None,
}
}
pub const fn copy_with(self) -> SliverGeometryBuilder {
SliverGeometryBuilder {
scroll_extent: self.scroll_extent,
paint_origin: self.paint_origin,
paint_extent: self.paint_extent,
layout_extent: Some(self.layout_extent),
max_paint_extent: self.max_paint_extent,
max_scroll_obstruction_extent: self.max_scroll_obstruction_extent,
hit_test_extent: Some(self.hit_test_extent),
visible: Some(self.visible),
has_visual_overflow: self.has_visual_overflow,
scroll_offset_correction: self.scroll_offset_correction,
cache_extent: Some(self.cache_extent),
cross_axis_extent: self.cross_axis_extent,
}
}
pub fn is_zero(&self) -> bool {
self.scroll_extent == SliverGeometry::ZERO.scroll_extent
&& self.paint_origin == SliverGeometry::ZERO.paint_origin
&& self.paint_extent == SliverGeometry::ZERO.paint_extent
&& self.layout_extent == SliverGeometry::ZERO.layout_extent
&& self.max_paint_extent == SliverGeometry::ZERO.max_paint_extent
&& self.max_scroll_obstruction_extent
== SliverGeometry::ZERO.max_scroll_obstruction_extent
&& self.hit_test_extent == SliverGeometry::ZERO.hit_test_extent
&& self.visible == SliverGeometry::ZERO.visible
&& self.has_visual_overflow == SliverGeometry::ZERO.has_visual_overflow
&& self.scroll_offset_correction == SliverGeometry::ZERO.scroll_offset_correction
&& self.cache_extent == SliverGeometry::ZERO.cache_extent
&& self.cross_axis_extent == SliverGeometry::ZERO.cross_axis_extent
}
pub fn debug_assert_is_valid(&self) -> bool {
debug_assert!(self.scroll_extent >= 0.0, "The scroll_extent is negative.");
debug_assert!(self.paint_extent >= 0.0, "The paint_extent is negative.");
debug_assert!(self.layout_extent >= 0.0, "The layout_extent is negative.");
debug_assert!(self.cache_extent >= 0.0, "The cache_extent is negative.");
debug_assert!(
self.layout_extent <= self.paint_extent,
"The layout_extent ({}) exceeds the paint_extent ({}).",
self.layout_extent,
self.paint_extent
);
debug_assert!(
self.paint_extent - self.max_paint_extent <= PRECISION_ERROR_TOLERANCE,
"The max_paint_extent ({}) is less than the paint_extent ({}). By definition, a \
sliver can't paint more than the maximum that it can paint!",
self.max_paint_extent,
self.paint_extent
);
debug_assert!(
self.hit_test_extent >= 0.0,
"The hit_test_extent is negative."
);
debug_assert!(
self.scroll_offset_correction != Some(0.0),
"The scroll_offset_correction is zero."
);
true
}
}
#[derive(Clone, Copy, Debug)]
pub struct SliverGeometryBuilder {
scroll_extent: f64,
paint_origin: f64,
paint_extent: f64,
layout_extent: Option<f64>,
max_paint_extent: f64,
max_scroll_obstruction_extent: f64,
hit_test_extent: Option<f64>,
visible: Option<bool>,
has_visual_overflow: bool,
scroll_offset_correction: Option<f64>,
cache_extent: Option<f64>,
cross_axis_extent: Option<f64>,
}
impl SliverGeometryBuilder {
pub const fn scroll_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.scroll_extent = value;
self
}
pub const fn paint_origin(mut self, value: f64) -> SliverGeometryBuilder {
self.paint_origin = value;
self
}
pub const fn paint_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.paint_extent = value;
self
}
pub const fn layout_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.layout_extent = Some(value);
self
}
pub const fn max_paint_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.max_paint_extent = value;
self
}
pub const fn max_scroll_obstruction_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.max_scroll_obstruction_extent = value;
self
}
pub const fn hit_test_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.hit_test_extent = Some(value);
self
}
pub const fn visible(mut self, value: bool) -> SliverGeometryBuilder {
self.visible = Some(value);
self
}
pub const fn has_visual_overflow(mut self, value: bool) -> SliverGeometryBuilder {
self.has_visual_overflow = value;
self
}
pub fn scroll_offset_correction(mut self, value: f64) -> SliverGeometryBuilder {
debug_assert!(value != 0.0);
self.scroll_offset_correction = Some(value);
self
}
pub const fn cache_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.cache_extent = Some(value);
self
}
pub const fn cross_axis_extent(mut self, value: f64) -> SliverGeometryBuilder {
self.cross_axis_extent = Some(value);
self
}
pub fn build(self) -> SliverGeometry {
SliverGeometry {
scroll_extent: self.scroll_extent,
paint_origin: self.paint_origin,
paint_extent: self.paint_extent,
layout_extent: self.layout_extent.unwrap_or(self.paint_extent),
max_paint_extent: self.max_paint_extent,
max_scroll_obstruction_extent: self.max_scroll_obstruction_extent,
hit_test_extent: self.hit_test_extent.unwrap_or(self.paint_extent),
visible: self.visible.unwrap_or(self.paint_extent > 0.0),
has_visual_overflow: self.has_visual_overflow,
scroll_offset_correction: self.scroll_offset_correction,
cache_extent: self
.cache_extent
.or(self.layout_extent)
.unwrap_or(self.paint_extent),
cross_axis_extent: self.cross_axis_extent,
}
}
}
impl Default for SliverGeometry {
fn default() -> SliverGeometry {
SliverGeometry::builder().build()
}
}
impl Display for SliverGeometry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SliverGeometry({}x{})",
self.scroll_extent, self.paint_extent
)
}
}
pub type SliverHitTest<'r> = dyn FnOnce(&mut SliverHitTestResult<'r>, f64, f64) -> bool;
pub struct SliverHitTestResult<'a>(&'a mut HitTestResult);
impl<'a> SliverHitTestResult<'a> {
pub fn wrap(result: &'a mut HitTestResult) -> SliverHitTestResult<'a> {
SliverHitTestResult(result)
}
pub fn add_with_axis_offset(
&mut self,
paint_offset: Option<Offset>,
main_axis_offset: f64,
cross_axis_offset: f64,
main_axis_position: f64,
cross_axis_position: f64,
hit_test: impl FnOnce(&mut SliverHitTestResult<'_>, f64, f64) -> bool,
) -> bool {
if let Some(paint_offset) = paint_offset {
self.0.push_offset(Offset::ZERO - paint_offset);
}
let is_hit = hit_test(
self,
main_axis_position - main_axis_offset,
cross_axis_position - cross_axis_offset,
);
if paint_offset.is_some() {
self.0.pop_transform();
}
is_hit
}
}
impl Deref for SliverHitTestResult<'_> {
type Target = HitTestResult;
fn deref(&self) -> &HitTestResult {
self.0
}
}
impl DerefMut for SliverHitTestResult<'_> {
fn deref_mut(&mut self) -> &mut HitTestResult {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct SliverHitTestEntry {
target: AnyRenderSliver,
main_axis_position: f64,
cross_axis_position: f64,
}
impl SliverHitTestEntry {
pub fn new(
target: AnyRenderSliver,
main_axis_position: f64,
cross_axis_position: f64,
) -> SliverHitTestEntry {
SliverHitTestEntry {
target,
main_axis_position,
cross_axis_position,
}
}
pub fn target(&self) -> AnyRenderSliver {
self.target
}
pub fn main_axis_position(&self) -> f64 {
self.main_axis_position
}
pub fn cross_axis_position(&self) -> f64 {
self.cross_axis_position
}
}
impl HitTestTarget for SliverHitTestEntry {
fn retained_handle(&self) -> Option<HandleId> {
Some(self.target.id)
}
fn handle_event(&self, app: &mut App, event: &PointerEvent, _entry: &HitTestEntry) {
self.target.handle_event(app, event, self);
}
}
impl From<SliverHitTestEntry> for HitTestEntry {
fn from(entry: SliverHitTestEntry) -> HitTestEntry {
HitTestEntry::new(entry)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct SliverLogicalParentData {
pub layout_offset: Option<f64>,
}
impl SliverLogicalParentData {
pub const fn new() -> SliverLogicalParentData {
SliverLogicalParentData {
layout_offset: None,
}
}
}
impl ParentData for SliverLogicalParentData {
fn provide(&self, id: TypeId) -> Option<&dyn Any> {
(id == TypeId::of::<SliverLogicalParentData>()).then_some(self)
}
fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
(id == TypeId::of::<SliverLogicalParentData>()).then_some(self)
}
}
impl Display for SliverLogicalParentData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.layout_offset {
None => f.write_str("layoutOffset=None"),
Some(offset) => write!(f, "layoutOffset={offset:.1}"),
}
}
}
#[derive(Debug)]
pub struct SliverLogicalContainerParentData {
sliver_logical: SliverLogicalParentData,
container: ContainerParentData<AnyRenderSliver>,
}
impl SliverLogicalContainerParentData {
pub const fn new() -> SliverLogicalContainerParentData {
SliverLogicalContainerParentData {
sliver_logical: SliverLogicalParentData::new(),
container: ContainerParentData::new(),
}
}
pub fn sliver_logical_parent_data(&self) -> &SliverLogicalParentData {
&self.sliver_logical
}
pub fn sliver_logical_parent_data_mut(&mut self) -> &mut SliverLogicalParentData {
&mut self.sliver_logical
}
pub fn layout_offset(&self) -> Option<f64> {
self.sliver_logical.layout_offset
}
pub fn set_layout_offset(&mut self, value: Option<f64>) {
self.sliver_logical.layout_offset = value;
}
}
impl Default for SliverLogicalContainerParentData {
fn default() -> SliverLogicalContainerParentData {
SliverLogicalContainerParentData::new()
}
}
impl ParentData for SliverLogicalContainerParentData {
fn detach(&mut self) {
ContainerParentDataMixin::detach(self);
}
fn provide(&self, id: TypeId) -> Option<&dyn Any> {
if id == TypeId::of::<SliverLogicalContainerParentData>() {
return Some(self);
}
self.sliver_logical.provide(id)
}
fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
if id == TypeId::of::<SliverLogicalContainerParentData>() {
return Some(self);
}
self.sliver_logical.provide_mut(id)
}
}
impl ContainerParentDataMixin for SliverLogicalContainerParentData {
type ChildType = AnyRenderSliver;
fn container_parent_data(&self) -> &ContainerParentData<AnyRenderSliver> {
&self.container
}
fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderSliver> {
&mut self.container
}
}
impl Display for SliverLogicalContainerParentData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.sliver_logical, f)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SliverPhysicalParentData {
pub paint_offset: Offset,
pub cross_axis_flex: Option<i32>,
}
impl SliverPhysicalParentData {
pub const fn new() -> SliverPhysicalParentData {
SliverPhysicalParentData {
paint_offset: Offset::ZERO,
cross_axis_flex: None,
}
}
pub fn apply_paint_transform(&self, transform: &mut Matrix4) {
translate(transform, self.paint_offset.dx(), self.paint_offset.dy());
}
}
impl Default for SliverPhysicalParentData {
fn default() -> SliverPhysicalParentData {
SliverPhysicalParentData::new()
}
}
impl ParentData for SliverPhysicalParentData {
fn provide(&self, id: TypeId) -> Option<&dyn Any> {
(id == TypeId::of::<SliverPhysicalParentData>()).then_some(self)
}
fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
(id == TypeId::of::<SliverPhysicalParentData>()).then_some(self)
}
}
impl Display for SliverPhysicalParentData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "paintOffset={:?}", self.paint_offset)
}
}
#[derive(Debug)]
pub struct SliverPhysicalContainerParentData {
sliver_physical: SliverPhysicalParentData,
container: ContainerParentData<AnyRenderSliver>,
}
impl SliverPhysicalContainerParentData {
pub const fn new() -> SliverPhysicalContainerParentData {
SliverPhysicalContainerParentData {
sliver_physical: SliverPhysicalParentData::new(),
container: ContainerParentData::new(),
}
}
pub fn sliver_physical_parent_data(&self) -> &SliverPhysicalParentData {
&self.sliver_physical
}
pub fn sliver_physical_parent_data_mut(&mut self) -> &mut SliverPhysicalParentData {
&mut self.sliver_physical
}
pub fn paint_offset(&self) -> Offset {
self.sliver_physical.paint_offset
}
pub fn set_paint_offset(&mut self, value: Offset) {
self.sliver_physical.paint_offset = value;
}
pub fn apply_paint_transform(&self, transform: &mut Matrix4) {
self.sliver_physical.apply_paint_transform(transform);
}
}
impl Default for SliverPhysicalContainerParentData {
fn default() -> SliverPhysicalContainerParentData {
SliverPhysicalContainerParentData::new()
}
}
impl ParentData for SliverPhysicalContainerParentData {
fn detach(&mut self) {
ContainerParentDataMixin::detach(self);
}
fn provide(&self, id: TypeId) -> Option<&dyn Any> {
if id == TypeId::of::<SliverPhysicalContainerParentData>() {
return Some(self);
}
self.sliver_physical.provide(id)
}
fn provide_mut(&mut self, id: TypeId) -> Option<&mut dyn Any> {
if id == TypeId::of::<SliverPhysicalContainerParentData>() {
return Some(self);
}
self.sliver_physical.provide_mut(id)
}
}
impl ContainerParentDataMixin for SliverPhysicalContainerParentData {
type ChildType = AnyRenderSliver;
fn container_parent_data(&self) -> &ContainerParentData<AnyRenderSliver> {
&self.container
}
fn container_parent_data_mut(&mut self) -> &mut ContainerParentData<AnyRenderSliver> {
&mut self.container
}
}
impl Display for SliverPhysicalContainerParentData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.sliver_physical, f)
}
}
pub(crate) fn calculate_paint_offset(constraints: SliverConstraints, from: f64, to: f64) -> f64 {
debug_assert!(from <= to);
let a = constraints.scroll_offset;
let b = constraints.scroll_offset + constraints.remaining_paint_extent;
clamp_double(
clamp_double(to, a, b) - clamp_double(from, a, b),
0.0,
constraints.remaining_paint_extent,
)
}
pub(crate) fn calculate_cache_offset(constraints: SliverConstraints, from: f64, to: f64) -> f64 {
debug_assert!(from <= to);
let a = constraints.scroll_offset + constraints.cache_origin;
let b = constraints.scroll_offset + constraints.remaining_cache_extent;
clamp_double(
clamp_double(to, a, b) - clamp_double(from, a, b),
0.0,
constraints.remaining_cache_extent,
)
}
pub struct RenderSliverData {
pub(crate) geometry: Option<SliverGeometry>,
pub(crate) constraints: Option<SliverConstraints>,
}
impl RenderSliverData {
pub fn new() -> RenderSliverData {
RenderSliverData {
geometry: None,
constraints: None,
}
}
}
impl Default for RenderSliverData {
fn default() -> RenderSliverData {
RenderSliverData::new()
}
}
#[macro_export]
macro_rules! render_sliver_accessors {
() => {
fn render_sliver_data(
self: $crate::RenderHandle<Self>,
app: &::inset_foundation::App,
) -> &$crate::RenderSliverData {
&self.get(app).render_sliver
}
fn render_sliver_data_mut(
self: $crate::RenderHandle<Self>,
app: &mut ::inset_foundation::App,
) -> &mut $crate::RenderSliverData {
&mut self.get_mut(app).render_sliver
}
};
}
pub trait RenderSliver: RenderObject {
fn render_sliver_data(self: RenderHandle<Self>, app: &App) -> &RenderSliverData;
fn render_sliver_data_mut(self: RenderHandle<Self>, app: &mut App) -> &mut RenderSliverData;
fn constraints(self: RenderHandle<Self>, app: &App) -> SliverConstraints {
self.render_sliver_data(app).constraints.unwrap_or_else(|| {
panic!("A RenderObject does not have any constraints before it has been laid out.")
})
}
fn geometry(self: RenderHandle<Self>, app: &App) -> SliverGeometry {
self.as_sliver().geometry(app)
}
fn set_geometry(self: RenderHandle<Self>, app: &mut App, geometry: SliverGeometry) {
self.as_sliver().set_geometry(app, geometry)
}
fn paint_bounds(self: RenderHandle<Self>, app: &App) -> Rect {
let constraints = self.constraints(app);
let geometry = self.geometry(app);
match constraints.axis() {
Axis::Horizontal => Rect::from_ltwh(
0.0,
0.0,
geometry.paint_extent,
constraints.cross_axis_extent,
),
Axis::Vertical => Rect::from_ltwh(
0.0,
0.0,
constraints.cross_axis_extent,
geometry.paint_extent,
),
}
}
fn center_offset_adjustment(self: RenderHandle<Self>, _app: &App) -> f64 {
let _ = self;
0.0
}
fn hit_test(
self: RenderHandle<Self>,
app: &mut App,
result: &mut SliverHitTestResult<'_>,
main_axis_position: f64,
cross_axis_position: f64,
) -> bool {
if main_axis_position >= 0.0
&& main_axis_position < self.geometry(app).hit_test_extent
&& cross_axis_position >= 0.0
&& cross_axis_position < self.constraints(app).cross_axis_extent
&& (self.hit_test_children(app, result, main_axis_position, cross_axis_position)
|| self.hit_test_self(app, main_axis_position, cross_axis_position))
{
result.add(
SliverHitTestEntry::new(self.as_sliver(), main_axis_position, cross_axis_position)
.into(),
);
return true;
}
false
}
fn hit_test_self(
self: RenderHandle<Self>,
_app: &App,
_main_axis_position: f64,
_cross_axis_position: f64,
) -> bool {
let _ = self;
false
}
fn hit_test_children(
self: RenderHandle<Self>,
_app: &mut App,
_result: &mut SliverHitTestResult<'_>,
_main_axis_position: f64,
_cross_axis_position: f64,
) -> bool {
let _ = self;
false
}
fn handle_event(
self: RenderHandle<Self>,
_app: &mut App,
_event: &PointerEvent,
_entry: &SliverHitTestEntry,
) {
let _ = self;
}
fn calculate_paint_offset(
self: RenderHandle<Self>,
_app: &App,
constraints: SliverConstraints,
from: f64,
to: f64,
) -> f64 {
let _ = self;
calculate_paint_offset(constraints, from, to)
}
fn calculate_cache_offset(
self: RenderHandle<Self>,
_app: &App,
constraints: SliverConstraints,
from: f64,
to: f64,
) -> f64 {
let _ = self;
calculate_cache_offset(constraints, from, to)
}
fn child_main_axis_position(
self: RenderHandle<Self>,
_app: &App,
_child: AnyRenderObject,
) -> f64 {
let _ = self;
panic!("this RenderSliver does not implement child_main_axis_position")
}
fn child_cross_axis_position(
self: RenderHandle<Self>,
_app: &App,
_child: AnyRenderObject,
) -> f64 {
let _ = self;
0.0
}
fn child_scroll_offset(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderObject,
) -> Option<f64> {
debug_assert_eq!(child.parent(app), Some(self.as_object()));
Some(0.0)
}
fn get_absolute_size_relative_to_origin(self: RenderHandle<Self>, app: &App) -> Size {
debug_assert!(!self.as_object().debug_needs_layout(app));
let constraints = self.constraints(app);
let paint_extent = self.geometry(app).paint_extent;
match apply_growth_direction_to_axis_direction(
constraints.axis_direction,
constraints.growth_direction,
) {
AxisDirection::Up => Size::new(constraints.cross_axis_extent, -paint_extent),
AxisDirection::Down => Size::new(constraints.cross_axis_extent, paint_extent),
AxisDirection::Left => Size::new(-paint_extent, constraints.cross_axis_extent),
AxisDirection::Right => Size::new(paint_extent, constraints.cross_axis_extent),
}
}
fn get_absolute_size(self: RenderHandle<Self>, app: &App) -> Size {
debug_assert!(!self.as_object().debug_needs_layout(app));
let constraints = self.constraints(app);
let paint_extent = self.geometry(app).paint_extent;
match constraints.axis_direction {
AxisDirection::Up | AxisDirection::Down => {
Size::new(constraints.cross_axis_extent, paint_extent)
}
AxisDirection::Right | AxisDirection::Left => {
Size::new(paint_extent, constraints.cross_axis_extent)
}
}
}
fn get_max_paint_rect(self: RenderHandle<Self>, app: &App) -> Rect {
let Some(geometry) = self.render_sliver_data(app).geometry else {
return Rect::ZERO;
};
if geometry.is_zero() {
return Rect::ZERO;
}
let constraints = self.constraints(app);
let mut max_paint_extent = geometry.max_paint_extent;
if max_paint_extent.is_infinite() {
max_paint_extent =
constraints.scroll_offset + geometry.cache_extent + constraints.cache_origin;
}
let paint_extent = geometry.paint_extent;
let leading_offset = clamp_double(
constraints.scroll_offset,
0.0,
geometry.scroll_extent - geometry.max_scroll_obstruction_extent,
);
let cross_axis_extent = geometry
.cross_axis_extent
.unwrap_or(constraints.cross_axis_extent);
let rect = match constraints.axis() {
Axis::Horizontal => {
Rect::from_ltwh(-leading_offset, 0.0, max_paint_extent, cross_axis_extent)
}
Axis::Vertical => {
Rect::from_ltwh(0.0, -leading_offset, cross_axis_extent, max_paint_extent)
}
};
match apply_growth_direction_to_axis_direction(
constraints.axis_direction,
constraints.growth_direction,
) {
AxisDirection::Right | AxisDirection::Down => rect,
AxisDirection::Left => Rect::from_ltrb(
paint_extent - rect.right,
rect.top,
paint_extent - rect.left,
rect.bottom,
),
AxisDirection::Up => Rect::from_ltrb(
rect.left,
paint_extent - rect.bottom,
rect.right,
paint_extent - rect.top,
),
}
}
fn layout(
self: RenderHandle<Self>,
app: &mut App,
constraints: SliverConstraints,
parent_uses_size: bool,
) {
self.as_sliver().layout(app, constraints, parent_uses_size)
}
fn as_sliver(self: RenderHandle<Self>) -> AnyRenderSliver {
AnyRenderSliver {
id: self.id(),
vtable: const { &RenderSliverVTable::of::<Self>() },
}
}
fn as_object(self: RenderHandle<Self>) -> AnyRenderObject {
self.as_sliver().as_object()
}
fn adopt_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
self.as_object().adopt_child(app, child)
}
fn drop_child(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
self.as_object().drop_child(app, child)
}
fn mark_needs_layout(self: RenderHandle<Self>, app: &mut App) {
self.as_object().mark_needs_layout(app)
}
fn mark_needs_paint(self: RenderHandle<Self>, app: &mut App) {
self.as_object().mark_needs_paint(app)
}
fn mark_needs_composited_layer_update(self: RenderHandle<Self>, app: &mut App) {
self.as_object().mark_needs_composited_layer_update(app)
}
fn schedule_initial_layout(self: RenderHandle<Self>, app: &mut App) {
self.as_object().schedule_initial_layout(app)
}
fn parent(self: RenderHandle<Self>, app: &App) -> Option<AnyRenderObject> {
self.as_object().parent(app)
}
fn owner(self: RenderHandle<Self>, app: &App) -> Option<Handle<PipelineOwner>> {
self.as_object().owner(app)
}
fn attached(self: RenderHandle<Self>, app: &App) -> bool {
self.as_object().attached(app)
}
fn debug_needs_layout(self: RenderHandle<Self>, app: &App) -> bool {
self.as_object().debug_needs_layout(app)
}
}
pub(crate) struct RenderSliverVTable {
pub object: RenderObjectVTable,
pub sliver_data: fn(&App, HandleId) -> &RenderSliverData,
pub sliver_data_mut: fn(&mut App, HandleId) -> &mut RenderSliverData,
pub hit_test: fn(&mut App, HandleId, &mut SliverHitTestResult<'_>, f64, f64) -> bool,
pub handle_event: fn(&mut App, HandleId, &PointerEvent, &SliverHitTestEntry),
pub center_offset_adjustment: fn(&App, HandleId) -> f64,
pub child_scroll_offset: fn(&App, HandleId, AnyRenderObject) -> Option<f64>,
pub child_main_axis_position: fn(&App, HandleId, AnyRenderObject) -> f64,
}
impl RenderSliverVTable {
const fn of<T: RenderSliver>() -> RenderSliverVTable {
RenderSliverVTable {
object: RenderObjectVTable::of::<T>(
|app, id, child| <T as RenderObject>::setup_parent_data(resolve(id), app, child),
|app, id| T::paint_bounds(resolve(id), app),
|app, id, child, transform| {
<T as RenderObject>::apply_paint_transform(resolve(id), app, child, transform)
},
|app, id| <T as RenderObject>::perform_resize(resolve(id), app),
|app, id| crate::object::RenderObjectBase::mark_needs_layout(resolve::<T>(id), app),
None,
Some(|| const { &RenderSliverVTable::of::<T>() }),
),
sliver_data: |app, id| T::render_sliver_data(resolve(id), app),
sliver_data_mut: |app, id| T::render_sliver_data_mut(resolve(id), app),
hit_test: |app, id, result, main, cross| {
T::hit_test(resolve(id), app, result, main, cross)
},
handle_event: |app, id, event, entry| T::handle_event(resolve(id), app, event, entry),
center_offset_adjustment: |app, id| T::center_offset_adjustment(resolve(id), app),
child_scroll_offset: |app, id, child| T::child_scroll_offset(resolve(id), app, child),
child_main_axis_position: |app, id, child| {
T::child_main_axis_position(resolve(id), app, child)
},
}
}
}
impl<T: RenderSliver> RenderHandle<T> {
pub fn new_sliver(app: &mut App, object: T) -> RenderHandle<T> {
let this = create(app, object);
let data = this.render_object_data_mut(app);
data.object_vtable = Some(&const { RenderSliverVTable::of::<T>() }.object);
let is_repaint_boundary = this.is_repaint_boundary(app);
let always_needs_compositing = this.always_needs_compositing(app);
let data = this.render_object_data_mut(app);
data.was_repaint_boundary = is_repaint_boundary;
data.needs_compositing = is_repaint_boundary || always_needs_compositing;
this
}
}
#[derive(Clone, Copy)]
pub struct AnyRenderSliver {
id: HandleId,
vtable: &'static RenderSliverVTable,
}
impl PartialEq for AnyRenderSliver {
fn eq(&self, other: &AnyRenderSliver) -> bool {
self.id == other.id
}
}
impl Eq for AnyRenderSliver {}
impl std::hash::Hash for AnyRenderSliver {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Debug for AnyRenderSliver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AnyRenderSliver({:?})", self.id)
}
}
impl AnyRenderSliver {
pub(crate) fn from_vtable(
id: HandleId,
vtable: &'static RenderSliverVTable,
) -> AnyRenderSliver {
AnyRenderSliver { id, vtable }
}
pub fn as_object(self) -> AnyRenderObject {
AnyRenderObject::from_vtable(self.id, &self.vtable.object)
}
fn sliver_data(self, app: &App) -> &RenderSliverData {
(self.vtable.sliver_data)(app, self.id)
}
fn sliver_data_mut(self, app: &mut App) -> &mut RenderSliverData {
(self.vtable.sliver_data_mut)(app, self.id)
}
pub fn layout(self, app: &mut App, constraints: SliverConstraints, parent_uses_size: bool) {
debug_assert!(constraints.debug_assert_is_valid(true));
let same = self.sliver_data(app).constraints == Some(constraints);
self.as_object()
.run_layout(app, parent_uses_size, constraints.is_tight(), same, |app| {
self.sliver_data_mut(app).constraints = Some(constraints)
});
}
pub fn constraints(self, app: &App) -> SliverConstraints {
self.sliver_data(app).constraints.unwrap_or_else(|| {
panic!("A RenderObject does not have any constraints before it has been laid out.")
})
}
pub fn hit_test(
self,
app: &mut App,
result: &mut SliverHitTestResult<'_>,
main_axis_position: f64,
cross_axis_position: f64,
) -> bool {
(self.vtable.hit_test)(
app,
self.id,
result,
main_axis_position,
cross_axis_position,
)
}
pub fn handle_event(self, app: &mut App, event: &PointerEvent, entry: &SliverHitTestEntry) {
(self.vtable.handle_event)(app, self.id, event, entry)
}
pub fn center_offset_adjustment(self, app: &App) -> f64 {
(self.vtable.center_offset_adjustment)(app, self.id)
}
pub fn child_scroll_offset(self, app: &App, child: AnyRenderObject) -> Option<f64> {
(self.vtable.child_scroll_offset)(app, self.id, child)
}
pub fn child_main_axis_position(self, app: &App, child: AnyRenderObject) -> f64 {
(self.vtable.child_main_axis_position)(app, self.id, child)
}
pub fn geometry(self, app: &App) -> SliverGeometry {
self.sliver_data(app)
.geometry
.unwrap_or_else(|| panic!("RenderSliver was not laid out: {self:?}"))
}
pub fn set_geometry(self, app: &mut App, geometry: SliverGeometry) {
self.sliver_data_mut(app).geometry = Some(geometry);
}
}
impl crate::object::ErasedRenderObject for AnyRenderSliver {
fn as_object(self) -> AnyRenderObject {
AnyRenderSliver::as_object(self)
}
fn from_object(object: AnyRenderObject) -> AnyRenderSliver {
object
.as_sliver()
.expect("the render object is not a RenderSliver")
}
}
impl From<AnyRenderSliver> for AnyRenderObject {
fn from(sliver: AnyRenderSliver) -> AnyRenderObject {
sliver.as_object()
}
}
fn get_right_way_up(constraints: SliverConstraints) -> bool {
let reversed = axis_direction_is_reversed(constraints.axis_direction);
match constraints.growth_direction {
GrowthDirection::Forward => !reversed,
GrowthDirection::Reverse => reversed,
}
}
pub trait RenderSliverHelpers: RenderSliver {
fn hit_test_box_child(
self: RenderHandle<Self>,
app: &mut App,
result: &mut BoxHitTestResult<'_>,
child: AnyRenderBox,
main_axis_position: f64,
cross_axis_position: f64,
) -> bool {
let constraints = self.constraints(app);
let right_way_up = get_right_way_up(constraints);
let mut delta = self.child_main_axis_position(app, child.as_object());
let cross_axis_delta = self.child_cross_axis_position(app, child.as_object());
let mut absolute_position = main_axis_position - delta;
let absolute_cross_axis_position = cross_axis_position - cross_axis_delta;
let size = child.size(app);
let paint_extent = self.geometry(app).paint_extent;
let (paint_offset, transformed_position) = match constraints.axis() {
Axis::Horizontal => {
if !right_way_up {
absolute_position = size.width() - absolute_position;
delta = paint_extent - size.width() - delta;
}
(
Offset::new(delta, cross_axis_delta),
Offset::new(absolute_position, absolute_cross_axis_position),
)
}
Axis::Vertical => {
if !right_way_up {
absolute_position = size.height() - absolute_position;
delta = paint_extent - size.height() - delta;
}
(
Offset::new(cross_axis_delta, delta),
Offset::new(absolute_cross_axis_position, absolute_position),
)
}
};
result.add_with_out_of_band_position(Some(paint_offset), None, None, |result| {
child.hit_test(app, result, transformed_position)
})
}
fn apply_paint_transform_for_box_child(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderBox,
transform: &mut Matrix4,
) {
let constraints = self.constraints(app);
let right_way_up = get_right_way_up(constraints);
let mut delta = self.child_main_axis_position(app, child.as_object());
let cross_axis_delta = self.child_cross_axis_position(app, child.as_object());
let size = child.size(app);
let paint_extent = self.geometry(app).paint_extent;
match constraints.axis() {
Axis::Horizontal => {
if !right_way_up {
delta = paint_extent - size.width() - delta;
}
translate(transform, delta, cross_axis_delta);
}
Axis::Vertical => {
if !right_way_up {
delta = paint_extent - size.height() - delta;
}
translate(transform, cross_axis_delta, delta);
}
}
}
}
pub trait RenderSliverSingleBoxAdapter:
RenderSliver + RenderObjectWithChildMixin<ChildType = AnyRenderBox> + RenderSliverHelpers
{
fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
if !child.parent_data_is::<SliverPhysicalParentData>(app) {
child.set_parent_data(app, SliverPhysicalParentData::new());
}
}
fn set_child_parent_data(
self: RenderHandle<Self>,
app: &mut App,
child: AnyRenderObject,
constraints: SliverConstraints,
geometry: SliverGeometry,
) {
let paint_offset = match apply_growth_direction_to_axis_direction(
constraints.axis_direction,
constraints.growth_direction,
) {
AxisDirection::Up => Offset::new(
0.0,
geometry.paint_extent + constraints.scroll_offset - geometry.scroll_extent,
),
AxisDirection::Left => Offset::new(
geometry.paint_extent + constraints.scroll_offset - geometry.scroll_extent,
0.0,
),
AxisDirection::Right => Offset::new(-constraints.scroll_offset, 0.0),
AxisDirection::Down => Offset::new(0.0, -constraints.scroll_offset),
};
child
.parent_data_of_mut::<SliverPhysicalParentData>(app)
.paint_offset = paint_offset;
}
fn hit_test_children(
self: RenderHandle<Self>,
app: &mut App,
result: &mut SliverHitTestResult<'_>,
main_axis_position: f64,
cross_axis_position: f64,
) -> bool {
debug_assert!(self.geometry(app).hit_test_extent > 0.0);
let Some(child) = self.child(app) else {
return false;
};
self.hit_test_box_child(
app,
&mut BoxHitTestResult::wrap(result),
child,
main_axis_position,
cross_axis_position,
)
}
fn child_main_axis_position(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderObject,
) -> f64 {
debug_assert_eq!(Some(child), self.child(app).map(AnyRenderBox::as_object));
-self.constraints(app).scroll_offset
}
fn apply_paint_transform(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderObject,
transform: &mut Matrix4,
) {
debug_assert_eq!(Some(child), self.child(app).map(AnyRenderBox::as_object));
child
.parent_data_of::<SliverPhysicalParentData>(app)
.apply_paint_transform(transform);
}
fn paint(
self: RenderHandle<Self>,
app: &mut App,
context: &mut PaintingContext,
offset: Offset,
) {
let Some(child) = self.child(app) else {
return;
};
if !self.geometry(app).visible {
return;
}
let paint_offset = child
.as_object()
.parent_data_of::<SliverPhysicalParentData>(app)
.paint_offset;
context.paint_child(app, child.as_object(), offset + paint_offset);
}
}
pub struct RenderSliverToBoxAdapter {
render_object: crate::object::RenderObjectData,
render_sliver: RenderSliverData,
child: RenderObjectWithChildData<AnyRenderBox>,
}
impl RenderSliverToBoxAdapter {
pub fn new(
app: &mut App,
child: Option<AnyRenderBox>,
) -> RenderHandle<RenderSliverToBoxAdapter> {
let this = RenderHandle::new_sliver(
app,
RenderSliverToBoxAdapter {
render_object: crate::object::RenderObjectData::new(),
render_sliver: RenderSliverData::new(),
child: RenderObjectWithChildData::new(),
},
);
this.set_child(app, child);
this
}
}
impl RenderObjectWithChildMixin for RenderSliverToBoxAdapter {
type ChildType = AnyRenderBox;
fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
&self.get(app).child
}
fn child_data_mut(
self: RenderHandle<Self>,
app: &mut App,
) -> &mut RenderObjectWithChildData<AnyRenderBox> {
&mut self.get_mut(app).child
}
}
impl RenderSliverHelpers for RenderSliverToBoxAdapter {}
impl RenderSliverSingleBoxAdapter for RenderSliverToBoxAdapter {}
impl RenderObject for RenderSliverToBoxAdapter {
crate::render_object_accessors!();
fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
RenderSliverSingleBoxAdapter::setup_parent_data(self, app, child)
}
fn visit_children(
self: RenderHandle<Self>,
app: &App,
visitor: &mut dyn FnMut(AnyRenderObject),
) {
if let Some(child) = self.child(app) {
visitor(child.as_object());
}
}
fn apply_paint_transform(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderObject,
transform: &mut Matrix4,
) {
RenderSliverSingleBoxAdapter::apply_paint_transform(self, app, child, transform)
}
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let Some(child) = self.child(app) else {
self.set_geometry(app, SliverGeometry::ZERO);
return;
};
let constraints = self.constraints(app);
child.layout(
app,
constraints.as_box_constraints(0.0, f64::INFINITY, None),
true,
);
let child_extent = match constraints.axis() {
Axis::Horizontal => child.size(app).width(),
Axis::Vertical => child.size(app).height(),
};
let painted_child_size = calculate_paint_offset(constraints, 0.0, child_extent);
let cache_extent = calculate_cache_offset(constraints, 0.0, child_extent);
debug_assert!(painted_child_size.is_finite());
debug_assert!(painted_child_size >= 0.0);
let geometry = SliverGeometry::builder()
.scroll_extent(child_extent)
.paint_extent(painted_child_size)
.cache_extent(cache_extent)
.max_paint_extent(child_extent)
.hit_test_extent(painted_child_size)
.has_visual_overflow(
child_extent > constraints.remaining_paint_extent
|| constraints.scroll_offset > 0.0,
)
.build();
self.set_geometry(app, geometry);
self.set_child_parent_data(app, child.as_object(), constraints, geometry);
}
fn paint(
self: RenderHandle<Self>,
app: &mut App,
context: &mut PaintingContext,
offset: Offset,
) {
RenderSliverSingleBoxAdapter::paint(self, app, context, offset)
}
}
impl RenderSliver for RenderSliverToBoxAdapter {
render_sliver_accessors!();
fn hit_test_children(
self: RenderHandle<Self>,
app: &mut App,
result: &mut SliverHitTestResult<'_>,
main_axis_position: f64,
cross_axis_position: f64,
) -> bool {
RenderSliverSingleBoxAdapter::hit_test_children(
self,
app,
result,
main_axis_position,
cross_axis_position,
)
}
fn child_main_axis_position(
self: RenderHandle<Self>,
app: &App,
child: AnyRenderObject,
) -> f64 {
RenderSliverSingleBoxAdapter::child_main_axis_position(self, app, child)
}
}
#[cfg(test)]
mod tests {
use inset_foundation::AppCell;
use inset_gestures::HitTestResult;
use super::*;
use crate::box_::{RenderBox, RenderBoxData};
use crate::proxy_box::RenderConstrainedBox;
struct TestSliver {
render_object: crate::object::RenderObjectData,
render_sliver: RenderSliverData,
}
impl RenderObject for TestSliver {
crate::render_object_accessors!();
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let constraints = self.constraints(app);
let extent = constraints.remaining_paint_extent.min(20.0);
self.set_geometry(
app,
SliverGeometry::builder()
.scroll_extent(20.0)
.paint_extent(extent)
.max_paint_extent(20.0)
.build(),
);
}
}
impl RenderSliver for TestSliver {
render_sliver_accessors!();
}
fn vertical_constraints(remaining_paint: f64) -> SliverConstraints {
SliverConstraints::new(
AxisDirection::Down,
GrowthDirection::Forward,
ScrollDirection::Idle,
0.0,
0.0,
0.0,
remaining_paint,
100.0,
AxisDirection::Right,
400.0,
remaining_paint,
0.0,
)
}
#[test]
fn sliver_layout_sets_geometry() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let sliver = RenderHandle::new_sliver(
&mut app,
TestSliver {
render_object: crate::object::RenderObjectData::new(),
render_sliver: RenderSliverData::new(),
},
);
sliver.layout(&mut app, vertical_constraints(50.0), false);
let geometry = sliver.geometry(&app);
assert_eq!(geometry.scroll_extent, 20.0);
assert_eq!(geometry.paint_extent, 20.0);
assert!(geometry.visible);
}
struct HitTestableBox {
render_object: crate::object::RenderObjectData,
render_box: RenderBoxData,
}
impl RenderObject for HitTestableBox {
crate::render_object_accessors!();
fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
let size = self.constraints(app).constrain(Size::new(60.0, 60.0));
self.set_size(app, size);
}
}
impl RenderBox for HitTestableBox {
crate::render_box_accessors!();
fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
let _ = self;
true
}
}
fn hit_testable_box(app: &mut App) -> AnyRenderBox {
RenderHandle::new_box(
app,
HitTestableBox {
render_object: crate::object::RenderObjectData::new(),
render_box: RenderBoxData::new(),
},
)
.as_box()
}
fn adapter_constraints(scroll_offset: f64, remaining_paint: f64) -> SliverConstraints {
SliverConstraints::new(
AxisDirection::Down,
GrowthDirection::Forward,
ScrollDirection::Idle,
scroll_offset,
0.0,
0.0,
remaining_paint,
100.0,
AxisDirection::Right,
remaining_paint,
remaining_paint,
0.0,
)
}
#[test]
fn a_box_adapter_reports_its_child_extent() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let child =
RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(60.0)), None);
let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
let geometry = adapter.geometry(&app);
assert_eq!(geometry.scroll_extent, 60.0);
assert_eq!(geometry.paint_extent, 60.0);
assert_eq!(geometry.max_paint_extent, 60.0);
assert!(!geometry.has_visual_overflow);
assert_eq!(child.size(&app), Size::new(100.0, 60.0));
assert_eq!(
child
.as_box()
.as_object()
.parent_data_of::<SliverPhysicalParentData>(&app)
.paint_offset,
Offset::ZERO
);
}
#[test]
fn a_scrolled_box_adapter_offsets_its_child() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let child =
RenderConstrainedBox::new(&mut app, BoxConstraints::tight_for(None, Some(60.0)), None);
let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child.as_box()));
adapter.layout(&mut app, adapter_constraints(20.0, 400.0), true);
let geometry = adapter.geometry(&app);
assert_eq!(geometry.scroll_extent, 60.0);
assert_eq!(geometry.paint_extent, 40.0);
assert!(geometry.has_visual_overflow);
assert_eq!(
child
.as_box()
.as_object()
.parent_data_of::<SliverPhysicalParentData>(&app)
.paint_offset,
Offset::new(0.0, -20.0)
);
assert_eq!(
RenderSliver::child_main_axis_position(adapter, &app, child.as_object()),
-20.0
);
}
#[test]
fn a_childless_box_adapter_is_zero() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let adapter = RenderSliverToBoxAdapter::new(&mut app, None);
adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
assert!(adapter.geometry(&app).is_zero());
}
#[test]
fn a_box_adapter_hit_tests_its_child() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let child = hit_testable_box(&mut app);
let adapter = RenderSliverToBoxAdapter::new(&mut app, Some(child));
adapter.layout(&mut app, adapter_constraints(0.0, 400.0), true);
let mut result = HitTestResult::new();
let hit = adapter.hit_test(
&mut app,
&mut SliverHitTestResult::wrap(&mut result),
30.0,
50.0,
);
assert!(hit);
assert_eq!(result.path().len(), 2);
}
#[test]
fn paint_and_cache_offsets_clamp_to_the_window() {
let constraints = adapter_constraints(20.0, 50.0);
assert_eq!(calculate_paint_offset(constraints, 0.0, 100.0), 50.0);
assert_eq!(calculate_paint_offset(constraints, 0.0, 30.0), 10.0);
assert_eq!(calculate_paint_offset(constraints, 0.0, 10.0), 0.0);
assert_eq!(calculate_cache_offset(constraints, 0.0, 100.0), 50.0);
}
#[test]
fn a_consistent_geometry_is_valid() {
let geometry = SliverGeometry::builder()
.scroll_extent(100.0)
.paint_extent(40.0)
.max_paint_extent(100.0)
.build();
assert!(geometry.debug_assert_is_valid());
assert!(!geometry.is_zero());
assert!(SliverGeometry::ZERO.is_zero());
}
#[test]
fn extents_left_out_follow_the_ones_given_in_any_order() {
let geometry = SliverGeometry::builder()
.layout_extent(10.0)
.hit_test_extent(20.0)
.paint_extent(40.0)
.build();
assert_eq!(geometry.layout_extent, 10.0);
assert_eq!(geometry.hit_test_extent, 20.0);
assert_eq!(geometry.cache_extent, 10.0);
assert!(geometry.visible);
let geometry = SliverGeometry::builder()
.cache_extent(5.0)
.layout_extent(10.0)
.paint_extent(40.0)
.build();
assert_eq!(geometry.cache_extent, 5.0);
}
#[test]
fn a_copy_keeps_the_extents_it_was_made_with() {
let copy = SliverGeometry::builder()
.paint_extent(40.0)
.layout_extent(10.0)
.build()
.copy_with()
.paint_extent(0.0)
.build();
assert_eq!(copy.layout_extent, 10.0);
assert_eq!(copy.cache_extent, 10.0);
assert!(copy.visible);
}
}