// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
suberror ChicleError {
InvalidNodeId(Int)
CycleDetected(parent~ : Int, child~ : Int)
ChildNotFound(parent~ : Int, child~ : Int)
DuplicateChild(parent~ : Int, child~ : Int)
}
///|
pub type NodeId = Int
///|
pub(all) struct Layout {
order : Int
location : Point[Double]
size : Size[Double]
content_size : Size[Double]
scrollbar_size : Size[Double]
border : Rect[Double]
padding : Rect[Double]
}
///|
pub fn Layout::zero() -> Layout {
{
order: 0,
location: Point::zero(),
size: Size::zero(),
content_size: Size::zero(),
scrollbar_size: Size::zero(),
border: Rect(left=0.0, right=0.0, top=0.0, bottom=0.0),
padding: Rect(left=0.0, right=0.0, top=0.0, bottom=0.0),
}
}
///|
pub fn Layout::scroll_width(self : Layout) -> Double {
@util.max_double(
0.0,
self.content_size.width +
@util.min_double(self.scrollbar_size.width, self.size.width) -
self.size.width +
self.border.right,
)
}
///|
pub fn Layout::scroll_height(self : Layout) -> Double {
@util.max_double(
0.0,
self.content_size.height +
@util.min_double(self.scrollbar_size.height, self.size.height) -
self.size.height +
self.border.bottom,
)
}
///|
pub(all) enum RunMode {
PerformLayout
ComputeSize
PerformHiddenLayout
}
///|
pub(all) enum SizingMode {
ContentSize
InherentSize
}
///|
pub(all) enum RequestedAxis {
RequestedHorizontal
RequestedVertical
RequestedBoth
}
///|
pub(all) struct LayoutInput {
run_mode : RunMode
sizing_mode : SizingMode
axis : RequestedAxis
known_dimensions : Size[Double?]
parent_size : Size[Double?]
available_space : Size[AvailableSpace]
vertical_margins_are_collapsible : Line[Bool]
}
///|
pub fn LayoutInput::hidden() -> LayoutInput {
{
run_mode: PerformHiddenLayout,
sizing_mode: InherentSize,
axis: RequestedBoth,
known_dimensions: Size(width=None, height=None),
parent_size: Size(width=None, height=None),
available_space: Size(width=AvailMaxContent, height=AvailMaxContent),
vertical_margins_are_collapsible: Line(start=false, end=false),
}
}
///|
pub fn layout_input_for_perform_layout(
known_dimensions : Size[Double?],
available_space : Size[AvailableSpace],
) -> LayoutInput {
{
run_mode: PerformLayout,
sizing_mode: InherentSize,
axis: RequestedBoth,
known_dimensions,
parent_size: known_dimensions,
available_space,
vertical_margins_are_collapsible: Line(start=false, end=false),
}
}
///|
pub fn layout_input_for_compute_size(
known_dimensions : Size[Double?],
parent_size : Size[Double?],
available_space : Size[AvailableSpace],
sizing_mode : SizingMode,
axis : RequestedAxis,
vertical_margins_are_collapsible : Line[Bool],
) -> LayoutInput {
{
run_mode: ComputeSize,
sizing_mode,
axis,
known_dimensions,
parent_size,
available_space,
vertical_margins_are_collapsible,
}
}
///|
pub fn requested_axis_from_absolute(axis : AbsoluteAxis) -> RequestedAxis {
match axis {
Horizontal => RequestedHorizontal
Vertical => RequestedVertical
}
}
///|
priv struct CacheEntry[T] {
known_dimensions : Size[Double?]
available_space : Size[AvailableSpace]
content : T
}
///|
type LeafMeasureCacheEntry = CacheEntry[Size[Double]]
///|
priv struct NodeLayoutCacheEntry {
known_dimensions : Size[Double?]
available_space : Size[AvailableSpace]
content : LayoutOutput
layout : Layout
effective_margin_top : Double
effective_margin_bottom : Double
effective_margin_top_max_pos : Double
effective_margin_top_min_neg : Double
effective_margin_bottom_max_pos : Double
effective_margin_bottom_min_neg : Double
}
///|
priv struct ChicleConfig {
mut use_rounding : Bool
}
///|
fn ChicleConfig::ChicleConfig() -> ChicleConfig {
{ use_rounding: true }
}
///|
pub(all) struct LayoutOutput {
size : Size[Double]
content_size : Size[Double]
first_baselines : Point[Double?]
top_margin : (Double, Double)
bottom_margin : (Double, Double)
margins_can_collapse_through : Bool
}
///|
pub fn LayoutOutput::from_outer_size(size : Size[Double]) -> LayoutOutput {
{
size,
content_size: Size::zero(),
first_baselines: Point(x=None, y=None),
top_margin: (0.0, 0.0),
bottom_margin: (0.0, 0.0),
margins_can_collapse_through: false,
}
}
///|
pub fn LayoutOutput::from_layout(layout : Layout) -> LayoutOutput {
{
..LayoutOutput::from_outer_size(layout.size),
content_size: layout.content_size,
}
}
///|
fn LayoutOutput::from_layout_and_first_baselines(
layout : Layout,
first_baselines : Point[Double?],
) -> LayoutOutput {
{ ..LayoutOutput::from_layout(layout), first_baselines, }
}
///|
pub fn LayoutOutput::outer_size(self : LayoutOutput) -> Size[Double] {
ignore(self.content_size)
ignore(self.first_baselines)
ignore(self.top_margin)
ignore(self.bottom_margin)
ignore(self.margins_can_collapse_through)
self.size
}
///|
priv struct Cache {
mut final_layout_entry : NodeLayoutCacheEntry?
measure_entries : Array[LeafMeasureCacheEntry?]
}
///|
fn Cache::Cache() -> Cache {
{
final_layout_entry: None,
measure_entries: [None, None, None, None, None, None, None, None, None],
}
}
///|
struct NodeData {
mut style : Style
mut unrounded_layout : Layout
mut final_layout : Layout
mut first_baselines : Point[Double?]
cache : Cache
mut effective_margin_top : Double
mut effective_margin_bottom : Double
mut effective_margin_top_max_pos : Double
mut effective_margin_top_min_neg : Double
mut effective_margin_bottom_max_pos : Double
mut effective_margin_bottom_min_neg : Double
mut alive : Bool
mut dirty : Bool
}
///|
pub struct ChicleTree[C] {
priv nodes : Array[NodeData]
priv children : Array[Array[NodeId]]
priv parents : Array[NodeId?]
priv node_context_data : Array[C?]
priv config : ChicleConfig
}
///|
priv struct ChicleView[C] {
tree : ChicleTree[C]
measure_function : (Size[Double?], Size[AvailableSpace], NodeId, C?, Style) -> Size[
Double,
]
}