use crate::layout_algorithm::axis_utils::Axis;
use crate::layout_algorithm::sizing::{get_child_axis_max, get_children_axis_total};
use crate::layout_struct::info_types::{ClipElement, ClipElementConfig, FloatingElementStyle};
use cotis::utils::ElementId;
use cotis_defaults::element_configs::style::sizing::{AxisSizing, DoubleAxisSizing, Sizing};
use cotis_defaults::element_configs::style::types::{
Alignment, LayoutAlignmentX, LayoutAlignmentY, LayoutDirection, Padding,
};
use cotis_utils::math::{BoundingBox, Vector2};
use std::fmt::Debug;
#[derive(Clone, Debug)]
pub struct LayoutElement {
pub id: ElementId,
pub name: String,
pub info: LayoutElementInfo,
pub config: LayoutElementConfig,
}
impl LayoutElement {
pub(crate) fn new(id: ElementId, name: String) -> Self {
Self {
id,
name,
info: LayoutElementInfo::NotInitialized,
config: Default::default(),
}
}
pub(crate) fn calc_start_dim(&self) -> element_info_states::SelfMinSize {
let min_w = Self::min_axis_from_sizing(&self.config.layout.sizing, true);
let min_h = Self::min_axis_from_sizing(&self.config.layout.sizing, false);
element_info_states::SelfMinSize {
min_size_width: min_w,
min_size_height: min_h,
preferred_size_width: self.internal_space_width().max(min_w),
preferred_size_height: self.internal_space_height().max(min_h),
}
}
fn min_axis_from_sizing(sizing: &Sizing, width: bool) -> f32 {
match sizing {
Sizing::DoubleAxis(axis_sizing) => {
let axis = if width {
axis_sizing.width
} else {
axis_sizing.height
};
match axis {
AxisSizing::Fit(min, max) | AxisSizing::Grow(min, max) => {
debug_assert!(min <= max);
min
}
AxisSizing::Fixed(v) => v,
AxisSizing::Percent(_) | AxisSizing::InnerPercent(_) => 0.0,
}
}
}
}
pub(crate) fn declared_max_axis(&self, width: bool) -> f32 {
match &self.config.layout.sizing {
Sizing::DoubleAxis(axis_sizing) => {
let axis = if width {
axis_sizing.width
} else {
axis_sizing.height
};
match axis {
AxisSizing::Fit(_, max) | AxisSizing::Grow(_, max) => max,
AxisSizing::Fixed(v) => v,
AxisSizing::Percent(_) | AxisSizing::InnerPercent(_) => f32::MAX,
}
}
}
}
pub(crate) fn internal_space_width(&self) -> f32 {
self.config.layout.padding.left + self.config.layout.padding.right
}
pub(crate) fn internal_space_height(&self) -> f32 {
self.config.layout.padding.top + self.config.layout.padding.bottom
}
}
pub(crate) fn clip_content_size(
parent: &LayoutElement,
children: &[&LayoutElement],
) -> Option<Vector2> {
if !parent.config.clip.horizontal && !parent.config.clip.vertical {
return None;
}
let pad = &parent.config.layout.padding;
let gap = parent.config.layout.child_gap;
let child_iter = children.iter().copied();
let (children_w, children_h) = match parent.config.layout.layout_direction {
LayoutDirection::LeftToRight => (
get_children_axis_total(child_iter.clone(), Axis::Width, gap),
get_child_axis_max(child_iter, Axis::Height),
),
LayoutDirection::TopToBottom => (
get_child_axis_max(child_iter.clone(), Axis::Width),
get_children_axis_total(child_iter, Axis::Height, gap),
),
};
Some(Vector2 {
x: pad.left + children_w + pad.right,
y: pad.top + children_h + pad.bottom,
})
}
pub mod element_info_states {
#[derive(Debug, Clone)]
pub struct SelfMinSize {
pub min_size_width: f32,
pub min_size_height: f32,
pub preferred_size_width: f32,
pub preferred_size_height: f32,
}
#[derive(Debug, Clone)]
pub struct TotalMinSize {
pub min_size_width: f32,
pub min_size_height: f32,
pub preferred_size_width: f32,
pub preferred_size_height: f32,
}
#[derive(Debug, Clone)]
pub struct TotalSizeWidthMinHeight {
pub width: f32,
pub min_height: f32,
pub preferred_size_height: f32,
}
#[derive(Debug, Clone)]
pub struct TextWrappedMinHeight {
pub width: f32,
pub min_height: f32,
pub preferred_size_height: f32,
}
#[derive(Debug, Clone)]
pub struct TotalSize {
pub width: f32,
pub height: f32,
}
#[derive(Debug, Clone)]
pub struct TotalSizedAndPosition {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl SelfMinSize {
pub fn into_total_min_size(self) -> TotalMinSize {
TotalMinSize {
min_size_width: self.min_size_width,
min_size_height: self.min_size_height,
preferred_size_width: self.preferred_size_width,
preferred_size_height: self.preferred_size_height,
}
}
}
}
#[derive(Debug, Clone)]
pub enum LayoutElementInfo {
NotInitialized,
SelfMinSize(element_info_states::SelfMinSize),
TotalMinSize(element_info_states::TotalMinSize),
TotalSizeWidthMinHeight(element_info_states::TotalSizeWidthMinHeight),
TextWrappedMinHeight(element_info_states::TextWrappedMinHeight),
TotalSize(element_info_states::TotalSize),
TotalSizedAndPosition(element_info_states::TotalSizedAndPosition),
}
impl LayoutElementInfo {
pub(crate) fn upgrade_width(&mut self) {
match self {
LayoutElementInfo::SelfMinSize(old) => {
*self = LayoutElementInfo::TotalSizeWidthMinHeight(
element_info_states::TotalSizeWidthMinHeight {
width: old.preferred_size_width.max(old.min_size_width),
min_height: old.min_size_height,
preferred_size_height: old.preferred_size_height,
},
)
}
LayoutElementInfo::TotalMinSize(old) => {
*self = LayoutElementInfo::TotalSizeWidthMinHeight(
element_info_states::TotalSizeWidthMinHeight {
width: old.preferred_size_width.max(old.min_size_width),
min_height: old.min_size_height,
preferred_size_height: old.preferred_size_height,
},
)
}
LayoutElementInfo::NotInitialized => panic!("Cant upgrade none init"),
_ => {}
}
}
pub(crate) fn upgrade_height(&mut self) {
match self {
LayoutElementInfo::TotalSizeWidthMinHeight(old) => {
*self = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
width: old.width,
height: old.min_height.max(old.preferred_size_height),
});
}
LayoutElementInfo::TextWrappedMinHeight(old) => {
*self = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
width: old.width,
height: old.min_height.max(old.preferred_size_height),
});
}
LayoutElementInfo::TotalSize(_) => {}
LayoutElementInfo::TotalSizedAndPosition(_) => {}
_ => panic!("Cant upgrade not final width"),
}
}
}
impl LayoutElementInfo {
pub(crate) fn temp_width(&self) -> Option<f32> {
match self {
LayoutElementInfo::SelfMinSize(tmp) => Some(tmp.preferred_size_width),
LayoutElementInfo::TotalMinSize(tmp) => Some(tmp.preferred_size_width),
_ => None,
}
}
pub(crate) fn min_width(&self) -> Option<f32> {
match self {
LayoutElementInfo::SelfMinSize(tmp) => Some(tmp.min_size_width),
LayoutElementInfo::TotalMinSize(tmp) => Some(tmp.min_size_width),
_ => None,
}
}
pub(crate) fn temp_height(&self) -> Option<f32> {
match self {
LayoutElementInfo::SelfMinSize(tmp) => Some(tmp.preferred_size_height),
LayoutElementInfo::TotalMinSize(tmp) => Some(tmp.preferred_size_height),
LayoutElementInfo::TotalSizeWidthMinHeight(tmp) => Some(tmp.preferred_size_height),
LayoutElementInfo::TextWrappedMinHeight(tmp) => Some(tmp.preferred_size_height),
_ => None,
}
}
pub(crate) fn min_height(&self) -> Option<f32> {
match self {
LayoutElementInfo::SelfMinSize(tmp) => Some(tmp.min_size_height),
LayoutElementInfo::TotalMinSize(tmp) => Some(tmp.min_size_height),
LayoutElementInfo::TotalSizeWidthMinHeight(tmp) => Some(tmp.min_height),
LayoutElementInfo::TextWrappedMinHeight(tmp) => Some(tmp.min_height),
_ => None,
}
}
pub fn get_final_width(&self) -> Option<f32> {
match self {
LayoutElementInfo::TotalSizeWidthMinHeight(old) => Some(old.width),
LayoutElementInfo::TextWrappedMinHeight(old) => Some(old.width),
LayoutElementInfo::TotalSize(old) => Some(old.width),
LayoutElementInfo::TotalSizedAndPosition(old) => Some(old.width),
_ => None,
}
}
pub fn get_final_height(&self) -> Option<f32> {
match self {
LayoutElementInfo::TotalSize(old) => Some(old.height),
LayoutElementInfo::TotalSizedAndPosition(old) => Some(old.height),
_ => None,
}
}
}
impl LayoutElementInfo {
pub fn set_x(&mut self, x: f32) {
match self {
LayoutElementInfo::TotalSize(old) => {
*self = LayoutElementInfo::TotalSizedAndPosition(
element_info_states::TotalSizedAndPosition {
x,
y: 0.0,
width: old.width,
height: old.height,
},
)
}
LayoutElementInfo::TotalSizedAndPosition(old) => {
old.x = x;
}
_ => {
panic!("tried setting x for not sized info")
}
}
}
pub fn set_y(&mut self, y: f32) {
match self {
LayoutElementInfo::TotalSize(old) => {
*self = LayoutElementInfo::TotalSizedAndPosition(
element_info_states::TotalSizedAndPosition {
x: 0.0,
y,
width: old.width,
height: old.height,
},
)
}
LayoutElementInfo::TotalSizedAndPosition(old) => {
old.y = y;
}
_ => {
panic!("tried setting y for not sized info")
}
}
}
pub fn to_bounding_box(&self) -> BoundingBox {
match self {
LayoutElementInfo::TotalSizedAndPosition(info) => BoundingBox {
x: info.x,
y: info.y,
width: info.width,
height: info.height,
},
_ => {
panic!("Tried to get bounding box for not positioned info")
}
}
}
pub fn set_tmp_width(&mut self, width: f32) {
match self {
LayoutElementInfo::SelfMinSize(tmp) => {
tmp.preferred_size_width = width.max(tmp.min_size_width)
}
LayoutElementInfo::TotalMinSize(tmp) => {
tmp.preferred_size_width = width.max(tmp.min_size_width)
}
_ => {
panic!("tried to set tmp width of final width")
}
}
}
pub fn set_tmp_height(&mut self, height: f32) {
match self {
LayoutElementInfo::SelfMinSize(tmp) => {
tmp.preferred_size_height = height.max(tmp.min_size_height)
}
LayoutElementInfo::TotalMinSize(tmp) => {
tmp.preferred_size_height = height.max(tmp.min_size_height)
}
LayoutElementInfo::TotalSizeWidthMinHeight(tmp) => {
tmp.preferred_size_height = height.max(tmp.min_height)
}
LayoutElementInfo::TextWrappedMinHeight(tmp) => {
tmp.preferred_size_height = height.max(tmp.min_height)
}
_ => {
panic!("tried to set tmp width of final width")
}
}
}
pub fn set_min_height(&mut self, height: f32) {
match self {
LayoutElementInfo::SelfMinSize(tmp) => {
tmp.min_size_height = height.max(tmp.min_size_height)
}
LayoutElementInfo::TotalMinSize(tmp) => {
tmp.min_size_height = height.max(tmp.min_size_height)
}
LayoutElementInfo::TotalSizeWidthMinHeight(tmp) => {
tmp.min_height = height.max(tmp.min_height)
}
LayoutElementInfo::TextWrappedMinHeight(tmp) => {
tmp.min_height = height.max(tmp.min_height)
}
_ => {
panic!("tried to set tmp width of final width")
}
}
}
}
#[derive(Debug, Clone)]
pub struct LayoutElementConfig {
pub layout: LayoutElementStyle,
pub floating: FloatingElementStyle,
pub clip: ClipElementConfig,
}
impl Default for LayoutElementConfig {
fn default() -> Self {
Self {
layout: LayoutElementStyle {
sizing: Sizing::DoubleAxis(DoubleAxisSizing {
width: AxisSizing::Fit(0.0, f32::MAX),
height: AxisSizing::Fit(0.0, f32::MAX),
}),
padding: Padding {
top: 0.0,
bottom: 0.0,
right: 0.0,
left: 0.0,
},
child_gap: 0.0,
child_alignment: Alignment {
x: LayoutAlignmentX::Left,
y: LayoutAlignmentY::Top,
},
layout_direction: LayoutDirection::LeftToRight,
wrapping: ChildWrapping::None,
},
floating: FloatingElementStyle {
config: None,
z_index: 0,
},
clip: ClipElement {
horizontal: false,
vertical: false,
offset: Default::default(),
},
}
}
}
#[derive(Debug, Clone)]
pub struct LayoutElementStyle {
pub sizing: Sizing,
pub padding: Padding,
pub child_gap: f32,
pub child_alignment: Alignment,
pub layout_direction: LayoutDirection,
pub wrapping: ChildWrapping,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ChildWrapping {
None,
Text,
}