use frust_core::{
AnyView, BoxConstraints, BuildCtx, ChangeFlags, ChildPod, EventCtx, EventResult, InputEvent,
LayoutCtx, PaintCtx, PaintScene, SemanticsCtx, SliverConstraints, SliverGeometry, SliverWidget,
View, Widget, any,
};
use kurbo::{Point, Size};
use super::adapter::{box_child_origin, box_geometry, finite_extent};
use super::{cache_offset, paint_offset};
pub struct SliverFillRemainingView<State: 'static> {
child: AnyView<State>,
has_scroll_body: bool,
}
pub fn sliver_fill_remaining<State: 'static, V: View<State>>(
child: V,
) -> SliverFillRemainingView<State> {
SliverFillRemainingView {
child: any(child),
has_scroll_body: true,
}
}
impl<State: 'static> SliverFillRemainingView<State> {
pub fn has_scroll_body(mut self, has_scroll_body: bool) -> Self {
self.has_scroll_body = has_scroll_body;
self
}
}
pub struct SliverFillRemainingWidget {
child: ChildPod,
has_scroll_body: bool,
clip: bool,
}
impl<State: 'static> View<State> for SliverFillRemainingView<State> {
type Element = SliverFillRemainingWidget;
fn build(&self, ctx: &mut BuildCtx<'_>) -> SliverFillRemainingWidget {
SliverFillRemainingWidget {
child: crate::authoring::build_child(&self.child, ctx),
has_scroll_body: self.has_scroll_body,
clip: false,
}
}
fn rebuild(
&self,
prev: &Self,
element: &mut SliverFillRemainingWidget,
ctx: &mut BuildCtx<'_>,
) -> ChangeFlags {
let mut flags =
crate::authoring::rebuild_child(&prev.child, &self.child, &mut element.child, ctx);
if element.has_scroll_body != self.has_scroll_body {
element.has_scroll_body = self.has_scroll_body;
flags |= ChangeFlags::LAYOUT | ChangeFlags::PAINT;
}
flags
}
fn teardown(&self, element: &mut SliverFillRemainingWidget, ctx: &mut BuildCtx<'_>) {
crate::authoring::teardown_child(&self.child, &mut element.child, ctx);
}
}
impl SliverFillRemainingWidget {
fn layout_scroll_body(
&mut self,
ctx: &mut LayoutCtx,
sc: &SliverConstraints,
) -> Option<SliverGeometry> {
let extent = sc.remaining_paint_extent - sc.overlap.min(0.0);
if !extent.is_finite() {
return None;
}
let viewport = finite_extent(sc.viewport_main_axis_extent);
let cache = cache_offset(sc, 0.0, viewport);
let max_extent = if extent == 0.0 && cache > 0.0 {
cache
} else {
extent
};
let axis = sc.axis;
let cross = sc.cross_axis_extent;
self.child.layout_child(
ctx,
&BoxConstraints::new(axis.size(extent, cross), axis.size(max_extent, cross)),
);
let paint_extent = paint_offset(sc, 0.0, extent);
Some(SliverGeometry {
cache_extent: cache.max(paint_extent),
has_visual_overflow: extent > sc.remaining_paint_extent || sc.scroll_offset > 0.0,
..SliverGeometry::new(viewport, paint_extent, paint_extent)
})
}
fn layout_remainder(&mut self, ctx: &mut LayoutCtx, sc: &SliverConstraints) -> SliverGeometry {
let axis = sc.axis;
let cross = sc.cross_axis_extent;
let remainder = finite_extent(sc.viewport_main_axis_extent - sc.preceding_scroll_extent);
let size = self.child.layout_child(
ctx,
&BoxConstraints::new(axis.size(remainder, cross), axis.size(f64::INFINITY, cross)),
);
box_geometry(finite_extent(axis.main_extent(size)).max(remainder), sc)
}
}
impl Widget for SliverFillRemainingWidget {
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
let size = self.child.layout_child(ctx, bc);
self.child.set_origin(Point::ZERO);
self.clip = false;
size
}
fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
if self.clip {
scene.push_clip(ctx.origin(), ctx.size());
}
self.child.paint_child(ctx, scene);
if self.clip {
scene.pop_clip();
}
}
fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
crate::authoring::route_event_single(&mut self.child, ctx, event)
}
fn semantics(&self, ctx: &mut SemanticsCtx) {
self.child.semantics_child(ctx);
}
fn as_sliver(&mut self) -> Option<&mut dyn SliverWidget> {
Some(self)
}
crate::authoring::visit_children!(child);
}
impl SliverWidget for SliverFillRemainingWidget {
fn layout_sliver(&mut self, ctx: &mut LayoutCtx, sc: &SliverConstraints) -> SliverGeometry {
let filled = if self.has_scroll_body {
self.layout_scroll_body(ctx, sc)
} else {
Some(self.layout_remainder(ctx, sc))
};
let geometry = filled.unwrap_or_else(|| {
let size = self.child.layout_child(ctx, &sc.as_box_constraints());
box_geometry(sc.axis.main_extent(size), sc)
});
self.child.set_origin(box_child_origin(sc));
self.clip = geometry.has_visual_overflow;
geometry
}
}