use crate::layout_algorithm::axis_utils::Axis;
use crate::layout_algorithm::commands::{calculate_render_commands, sort_commands};
use crate::layout_algorithm::positions::calculate_child_positions;
use crate::layout_algorithm::sizing::fit_grow_shrink_children_axis;
use crate::layout_algorithm::wrapping::recursive_wrap_left_to_right;
use crate::layout_struct::layout_states::{
ChildWrapping, LayoutElementConfig, LayoutElementInfo, LayoutElementStyle, element_info_states,
};
use crate::layout_struct::layout_tree::{
LayoutElement, LayoutTree, LayoutTreeCursor, LayoutTreeCursorBorrow, LayoutTreeIndex,
};
use crate::layout_struct::{RenderCommandOutput, TextDrawPayload};
use crate::text::TextMeasuringFun;
use cotis::utils::ElementId;
use cotis::utils::ElementIdConfig;
use cotis::utils::OwnedOrRef;
use cotis_defaults::element_configs::style::sizing::Sizing::DoubleAxis;
use cotis_defaults::element_configs::style::sizing::{AxisSizing, DoubleAxisSizing};
use cotis_defaults::element_configs::style::types::{
Alignment, LayoutAlignmentX, LayoutAlignmentY, LayoutDirection, Padding,
};
use cotis_defaults::element_configs::text_config::TextConfig as DefaultTextConfig;
use cotis_utils::math::Dimensions;
use cotis_utils::text::{LayoutTextMeasuring, TextMeasurer};
use std::collections::{HashMap, VecDeque};
pub struct CotisLayoutManager {
root_id: ElementIdConfig<'static>,
root: LayoutTree,
screen_dimensions: Dimensions,
cache_elements: HashMap<ElementId, LayoutElement>,
element_parent: HashMap<ElementId, ElementId>,
text_dim_fun: Option<TextMeasuringFun>,
}
pub struct CotisLayoutRun<'layout, Config> {
pub(crate) layout_manager: &'layout mut CotisLayoutManager,
open_element_index: LayoutTreeIndex,
configs: HashMap<ElementId, Config>,
pub(crate) text_fragments: HashMap<ElementId, TextDrawPayload>,
}
impl CotisLayoutManager {
pub fn set_text_dim_fun(&mut self, fun: TextMeasuringFun) {
self.text_dim_fun = Some(fun);
}
pub(crate) fn text_dim_fun(&self) -> Option<&TextMeasuringFun> {
self.text_dim_fun.as_ref()
}
}
impl LayoutTextMeasuring<DefaultTextConfig> for CotisLayoutManager {
fn set_text_measuring_function(&mut self, text: Box<TextMeasurer<'_, DefaultTextConfig>>) {
self.text_dim_fun = Some(unsafe {
std::mem::transmute::<Box<TextMeasurer<'_, DefaultTextConfig>>, TextMeasuringFun>(text)
});
}
}
impl CotisLayoutManager {
fn default_node(&self) -> LayoutElement {
LayoutElement {
id: self.root_id.get_handle(),
name: "Root".to_string(),
info: LayoutElementInfo::NotInitialized,
config: LayoutElementConfig {
layout: LayoutElementStyle {
sizing: DoubleAxis(DoubleAxisSizing {
width: AxisSizing::Fixed(self.screen_dimensions.width),
height: AxisSizing::Fixed(self.screen_dimensions.height),
}),
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,
},
..LayoutElementConfig::default()
},
}
}
pub fn new(screen_dimensions: Dimensions) -> Self {
let root = LayoutTree::new();
let root_id = ElementIdConfig::new_empty();
let mut res = Self {
root_id,
root,
screen_dimensions,
cache_elements: Default::default(),
element_parent: Default::default(),
text_dim_fun: None,
};
res.root.add_root(res.default_node());
res.configure_roots();
res
}
pub fn set_dimensions(&mut self, screen_dimensions: Dimensions) {
self.screen_dimensions = screen_dimensions;
}
pub fn get_cache_element(&self, id: ElementId) -> Option<&LayoutElement> {
self.cache_elements.get(&id)
}
pub(crate) fn cached_element_ids(&self) -> Vec<ElementId> {
self.cache_elements.values().map(|e| e.id).collect()
}
pub(crate) fn parent_layout_slot_of(&self, child_slot: ElementId) -> Option<ElementId> {
self.element_parent.get(&child_slot).copied()
}
pub(crate) fn direct_child_slots(&self, parent_slot: ElementId) -> Vec<ElementId> {
self.element_parent
.iter()
.filter_map(|(child, &parent)| (parent == parent_slot).then_some(*child))
.collect()
}
pub(crate) fn clear_tree_no_cache(&mut self) {
self.root.clear_tree_structure();
self.root.add_root(self.default_node());
self.configure_roots();
}
pub(crate) fn clear_tree(&mut self) {
let (cache_elements, parent_map) = self.root.clear_tree_structure_with_parent_map();
self.cache_elements = cache_elements;
self.element_parent = parent_map;
}
pub(crate) fn tree(&mut self) -> &mut LayoutTree {
&mut self.root
}
pub(crate) fn configure_roots(&mut self) {
let mut root_element = self
.root
.get_node_mut(&LayoutTreeIndex::new(&[self.root_id.get_handle()]))
.unwrap();
let root_element = root_element.get_local().self_element;
root_element.info =
LayoutElementInfo::TotalSizedAndPosition(element_info_states::TotalSizedAndPosition {
x: 0.0,
y: 0.0,
width: self.screen_dimensions.width,
height: self.screen_dimensions.height,
});
}
}
impl<'layout, Config> CotisLayoutRun<'layout, Config> {
pub fn new(layout_manager: &'layout mut CotisLayoutManager) -> Self {
layout_manager.clear_tree_no_cache();
let root_id = layout_manager.root_id.get_handle();
Self {
layout_manager,
open_element_index: LayoutTreeIndex::new(&[root_id]),
configs: Default::default(),
text_fragments: Default::default(),
}
}
pub fn get_tree_cursor(&mut self) -> LayoutTreeCursor<'_> {
LayoutTreeCursor::new_from_index(self.layout_manager.tree(), &self.open_element_index)
.unwrap()
}
pub fn new_child_element(&mut self) {
let id_conf = ElementIdConfig::new_empty();
let element = LayoutElement::new(id_conf.get_handle(), String::new());
let child_index = self
.layout_manager
.root
.add_child(&self.open_element_index, element)
.expect("[Cotis Layout] New Element added to missing parent element");
self.open_element_index.push(child_index);
}
pub fn set_open_element_config(
&mut self,
config: LayoutElementConfig,
id: ElementId,
name: Option<OwnedOrRef<str>>,
true_config: Config,
) {
let mut current_id = self.open_element_id();
let mut node = self
.layout_manager
.root
.get_node_mut(&self.open_element_index)
.expect("[Cotis Layout] Tried to set config for open element, but no element is open");
if id != current_id {
current_id = id;
node.change_node_id(id);
self.open_element_index.pop();
self.open_element_index.push(id);
}
{
let node = node.get_local().self_element;
node.config = config;
if let Some(name) = name {
node.name += name.as_ref();
}
}
self.configs.insert(current_id, true_config);
node.get_local().open_configuration()
}
pub fn open_element_id(&self) -> ElementId {
let node = self
.layout_manager
.root
.get_node(&self.open_element_index)
.expect("[Cotis Layout] Tried to set config for open element, but no element is open");
node.get_local().self_element.id
}
pub(crate) fn set_open_element_layout_direction(&mut self, layout_direction: LayoutDirection) {
let mut node = self.layout_manager.root.get_node_mut(&self.open_element_index).expect("[Cotis Layout] Tried to set layout direction for open element, but no element is open");
node.get_local().self_element.config.layout.layout_direction = layout_direction;
}
pub(crate) fn get_config(&self, id: ElementId) -> Option<&Config> {
self.configs.get(&id)
}
pub fn close_open_element(&mut self) {
let mut cursor = self.get_tree_cursor();
cursor.get_local().close_element();
self.open_element_index.pop();
}
fn calculate_final_layout_sizes(&mut self) {
self.layout_manager.configure_roots();
for root in self
.layout_manager
.root
.get_root_nodes()
.map(|c| LayoutTreeIndex::new(&[c.tree_node_id]))
.collect::<Vec<_>>()
{
let tree = &mut self.layout_manager.root;
fit_grow_shrink_children_axis(
&mut LayoutTreeCursor::new_from_index(tree, &root).unwrap(),
Axis::Width,
);
recursive_wrap_left_to_right(
&mut LayoutTreeCursor::new_from_index(tree, &root).unwrap(),
);
fit_grow_shrink_children_axis(
&mut LayoutTreeCursor::new_from_index(tree, &root).unwrap(),
Axis::Height,
);
}
}
fn calculate_positions(&mut self) {
for root in self
.layout_manager
.root
.get_root_nodes()
.map(|c| LayoutTreeIndex::new(&[c.tree_node_id]))
.collect::<Vec<_>>()
{
let tree = self.layout_manager.tree();
calculate_child_positions(&mut LayoutTreeCursor::new_from_index(tree, &root).unwrap());
}
}
fn calculate_render_commands(&mut self) -> Vec<RenderCommandOutput<Config>> {
let mut render_commands = Vec::new();
let mut open_nodes = VecDeque::new();
for root_id in self
.layout_manager
.root
.get_root_nodes_borrow()
.map(|c| c.tree_node_id)
.collect::<Vec<_>>()
{
let mut render_commands_buffer = Vec::new();
let root = LayoutTreeIndex::new(&[root_id]);
open_nodes.push_back(root);
while let Some(node) = open_nodes.pop_front() {
calculate_render_commands(
&self.layout_manager.root,
node,
&mut open_nodes,
&mut render_commands_buffer,
&mut self.configs,
&self.text_fragments,
);
}
let root = LayoutTreeIndex::new(&[root_id]);
let self_node =
LayoutTreeCursorBorrow::new_from_index(&self.layout_manager.root, &root).unwrap();
sort_commands(
&mut render_commands_buffer,
self_node.get_local().self_element.config.floating.z_index,
);
render_commands.append(&mut render_commands_buffer);
}
render_commands
}
pub fn finalize_layouts(&mut self) -> Vec<RenderCommandOutput<Config>> {
if let Some(id) = self.open_element_index.pop()
&& id != self.layout_manager.root_id.get_handle()
{
println!("[Cotis Layout] Not all elements have been closed");
}
self.calculate_final_layout_sizes();
self.calculate_positions();
let res = self.calculate_render_commands();
self.layout_manager.clear_tree();
res
}
pub fn remove_custom(&mut self, element_id: ElementId) -> Config {
self.configs
.remove(&element_id)
.expect("[Cotis Layout] Can't find config from element id")
}
}