use std::sync::Arc;
use cotis::utils::ElementIdConfig;
use cotis_defaults::colors::Color;
use cotis_defaults::element_configs::ElementConfig;
use cotis_defaults::element_configs::style::{BorderElementStyle, BorderWidthStyle};
use cotis_defaults::element_configs::style::sizing::{AxisSizing, DoubleAxisSizing, Sizing};
use cotis_defaults::element_configs::style::types::{LayoutDirection, Padding};
use cotis_defaults::element_configs::text_config::{TextElementConfig, TextElementConfigWrapMode};
use cotis_utils::math::Dimensions;
use crate::layout_management::CotisLayoutRun;
use crate::layout_struct::layout_states::{ChildWrapping, LayoutElementConfig};
use crate::layout_struct::TextDrawPayload;
use crate::text::{line_word_space_pieces, measure_text_fragment, newline_line_ranges, text_line_child_specs, TextLineChildSpec};
fn synthetic_text_child_element_config<'frame, I, C, E>(
parent: &ElementConfig<'frame, Sizing, I, C, E>,
layout_direction: LayoutDirection,
child_gap: f32,
measured: Dimensions,
intrinsic_fit: bool,
) -> ElementConfig<'frame, Sizing, I, C, E> {
#[cfg(any(feature = "complex_color", feature = "complex_colored_text"))]
let mut style = parent.style.clone();
#[cfg(not(any(feature = "complex_color", feature = "complex_colored_text")))]
let mut style = parent.style;
#[cfg(any(feature = "complex_color", feature = "complex_colored_text"))]
{
style.background_color = ColorLayer::Solid(Color::rgba(0.0, 0.0, 0.0, 0.0));
}
#[cfg(not(any(feature = "complex_color", feature = "complex_colored_text")))]
{
style.background_color.a = 0.0;
}
style.layout.layout_direction = layout_direction;
style.layout.child_gap = child_gap;
style.layout.padding = Padding {
top: 0.0,
bottom: 0.0,
left: 0.0,
right: 0.0,
};
let w = measured.width.max(0.0);
let h = measured.height.max(0.0);
style.sizing = Sizing::DoubleAxis(DoubleAxisSizing {
width: if intrinsic_fit {
AxisSizing::Fit(0.0, w)
} else {
AxisSizing::Fixed(w)
},
height: if intrinsic_fit {
AxisSizing::Fit(h, f32::MAX)
} else {
AxisSizing::Fixed(h)
},
});
style.floating = None;
style.border = BorderElementStyle {
color: Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
},
width: BorderWidthStyle {
left: 0.0,
right: 0.0,
top: 0.0,
bottom: 0.0,
between_children: 0.0,
},
};
ElementConfig {
id_config: ElementIdConfig::new_empty(),
style,
image: None,
custom: None,
extra_data: None,
}
}
impl<'layout, 'frame, I, C, E> CotisLayoutRun<'layout, ElementConfig<'frame, I, C, E>> {
pub(crate) fn apply_text_leaf(&mut self, leaf: TextElementConfig<'frame>) {
let container_id = self.open_element_id();
let source: Arc<str> = Arc::from(leaf.text.as_ref());
#[cfg(feature = "complex_colored_text")]
let text_style = leaf.config.clone();
#[cfg(not(feature = "complex_colored_text"))]
let text_style = leaf.config;
{
let mut node = self
.layout_manager
.root
.get_node_mut(&self.open_element_index)
.expect("[Cotis Layout] text leaf open element missing");
node.get_local().self_element.config.layout.layout_direction = LayoutDirection::TopToBottom;
}
let word_row_gap = match text_style.wrap_mode {
TextElementConfigWrapMode::Words => measure_text_fragment(
" ",
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
).width,
_ => 0.0,
};
for line_range in newline_line_ranges(source.as_ref()) {
let line_slice_full = &source[line_range.start..line_range.end];
let (line_dims, words_specs) = match text_style.wrap_mode {
TextElementConfigWrapMode::Words => {
let empty_dims = measure_text_fragment(
"",
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
);
let pieces = line_word_space_pieces(line_slice_full);
let specs = text_line_child_specs(&pieces);
let dims = if specs.is_empty() {
empty_dims
} else {
let mut sum_w = 0.0f32;
let mut max_h = empty_dims.height;
for spec in &specs {
if let TextLineChildSpec::Word(r) = spec {
let fragment = &line_slice_full[r.clone()];
let d = measure_text_fragment(
fragment,
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
);
sum_w += d.width;
max_h = max_h.max(d.height);
}
}
let gap_total = (specs.len().saturating_sub(1)) as f32 * word_row_gap;
Dimensions {
width: sum_w + gap_total,
height: max_h,
}
};
(dims, specs)
}
TextElementConfigWrapMode::Newline | TextElementConfigWrapMode::None => {
let dims = measure_text_fragment(
line_slice_full,
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
);
(dims, Vec::new())
}
};
self.new_child_element();
let mut line_el = {
let parent_cfg = self
.configs
.get(&container_id)
.expect("[Cotis Layout] text leaf needs set_config before set_leaf_config");
synthetic_text_child_element_config(
parent_cfg,
LayoutDirection::LeftToRight,
word_row_gap,
line_dims,
true,
)
};
line_el.id_config.change_id(self.open_element_id());
let mut line_layout: LayoutElementConfig = (&line_el.style).into();
line_layout.layout.wrapping = ChildWrapping::Text;
self.set_open_element_config(line_layout, None, None, line_el);
match text_style.wrap_mode {
TextElementConfigWrapMode::Words => {
let spacer_measured = Dimensions {
width: 0.0,
height: line_dims.height,
};
for spec in words_specs {
match spec {
TextLineChildSpec::Spacer => {
self.new_child_element();
let mut spacer_el = {
let parent_cfg = self
.configs
.get(&container_id)
.expect("[Cotis Layout] text leaf needs set_config before set_leaf_config");
synthetic_text_child_element_config(
parent_cfg,
LayoutDirection::LeftToRight,
0.0,
spacer_measured,
false,
)
};
spacer_el.id_config.change_id(self.open_element_id());
let spacer_layout: LayoutElementConfig = (&spacer_el.style).into();
self.set_open_element_config(spacer_layout, None, None, spacer_el);
self.close_open_element();
}
TextLineChildSpec::Word(w) => {
let global = line_range.start + w.start..line_range.start + w.end;
let word_text = &source[global.clone()];
let word_measured = measure_text_fragment(
word_text,
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
);
self.new_child_element();
let mut word_el = {
let parent_cfg = self
.configs
.get(&container_id)
.expect("[Cotis Layout] text leaf needs set_config before set_leaf_config");
synthetic_text_child_element_config(
parent_cfg,
LayoutDirection::LeftToRight,
0.0,
word_measured,
false,
)
};
word_el.id_config.change_id(self.open_element_id());
let word_layout: LayoutElementConfig = (&word_el.style).into();
self.set_open_element_config(word_layout, None, None, word_el);
let wid = self.open_element_id();
self.text_fragments.insert(
wid,
TextDrawPayload {
source: Arc::clone(&source),
range: global,
#[cfg(feature = "complex_colored_text")]
config: text_style.clone(),
#[cfg(not(feature = "complex_colored_text"))]
config: text_style,
},
);
self.close_open_element();
}
}
}
}
TextElementConfigWrapMode::Newline | TextElementConfigWrapMode::None => {
if !line_range.is_empty() {
let global = line_range.clone();
let word_text = &source[global.clone()];
let word_measured = measure_text_fragment(
word_text,
&text_style,
self.layout_manager.text_dim_fun.as_ref(),
);
self.new_child_element();
let mut word_el = {
let parent_cfg = self
.configs
.get(&container_id)
.expect("[Cotis Layout] text leaf needs set_config before set_leaf_config");
synthetic_text_child_element_config(
parent_cfg,
LayoutDirection::LeftToRight,
0.0,
word_measured,
false,
)
};
word_el.id_config.change_id(self.open_element_id());
let word_layout: LayoutElementConfig = (&word_el.style).into();
self.set_open_element_config(word_layout, None, None, word_el);
let wid = self.open_element_id();
self.text_fragments.insert(
wid,
TextDrawPayload {
source: Arc::clone(&source),
range: global,
#[cfg(feature = "complex_colored_text")]
config: text_style.clone(),
#[cfg(not(feature = "complex_colored_text"))]
config: text_style,
},
);
self.close_open_element();
}
}
}
self.close_open_element();
}
}
}