use { super::{ContainerChild, ContainerNode, ContainerSplit}, crate::rect::Rect, std::{ops::Sub, rc::Rc}, }; impl ContainerChild { pub(super) fn position_content(&self) { let mut content = self.content.get(); let body = self.body.get(); let width = content.width(); let height = content.height(); // let x1 = body.x1() + (body.width() - width) / 2; // let y1 = body.y1() + (body.height() - height) / 2; let x1 = body.x1(); let y1 = body.y1(); content = Rect::new_sized_saturating(x1, y1, width, height); // log::debug!("body: {:?}", body); // log::debug!("content: {:?}", content); self.content.set(content); } } impl ContainerNode { pub fn predict_child_body_size(&self) -> (i32, i32) { if self.mono_child.is_some() { let mb = self.mono_body.get(); return (mb.width(), mb.height()); } let nc = self.num_children.get() as i32 + 1; match self.split.get() { ContainerSplit::Horizontal => { let spacing = self.child_spacing(); let content_w = self.width.get().sub((nc - 1) * spacing).max(0); (content_w / nc, self.height.get()) } ContainerSplit::Vertical => { let spacing = self.child_spacing(); let content_h = self.height.get().sub((nc - 1) * spacing).max(0); (self.width.get(), content_h / nc) } } } pub fn on_spaces_changed(self: &Rc) { self.update_content_size(); // log::info!("on_spaces_changed"); self.schedule_layout(); self.schedule_compute_render_positions(); } pub fn on_colors_changed(self: &Rc) { self.schedule_compute_render_positions(); } pub(super) fn damage(&self) { let bw = if self.state.theme.sizes.gap.get() != 0 { self.state.theme.sizes.border_width.get() } else { 0 }; self.state.damage(Rect::new_sized_saturating( self.abs_x1.get() - bw, self.abs_y1.get() - bw, self.width.get() + 2 * bw, self.height.get() + 2 * bw, )); } pub(super) fn child_spacing(&self) -> i32 { let gap = self.state.theme.sizes.gap.get(); let bw = self.state.theme.sizes.border_width.get(); if gap == 0 { bw } else { gap + 2 * bw } } pub(super) fn schedule_layout(self: &Rc) { if self.state.layout_animations_requested.get() || self.state.layout_animations_active.get() { self.animate_next_layout.set(true); } if !self.layout_scheduled.replace(true) { self.state.pending_container_layout.push(self.clone()); } } pub(super) fn schedule_layout_immediate(self: &Rc) { self.schedule_layout(); if self.toplevel_data.visible.get() { self.damage(); } } pub(super) fn all_children_match_body(&self) -> bool { if let Some(mono) = self.mono_child.get() { let body = self.mono_body.get(); let content = mono.content.get(); return content.width() == body.width() && content.height() == body.height(); } for child in self.children.iter() { let body = child.body.get(); let content = child.content.get(); if content.width() != body.width() || content.height() != body.height() { return false; } } true } pub(super) fn perform_layout(self: &Rc) { self.layout_scheduled.set(false); if self.num_children.get() == 0 { self.mono_transition_animation_pending.set(false); return; } if let Some(child) = self.mono_child.get() { self.perform_mono_layout(&child); } else { self.perform_split_layout(); } self.state.tree_changed(); // log::info!("perform_layout"); self.schedule_compute_render_positions(); self.layout_complete.trigger(); if self.all_children_match_body() { self.all_children_resized.trigger(); if self.toplevel_data.visible.get() { self.damage(); } } self.mono_transition_animation_pending.set(false); } fn perform_mono_layout(self: &Rc, child: &ContainerChild) { let mb = self.mono_body.get(); child .node .clone() .tl_change_extents(&mb.move_(self.abs_x1.get(), self.abs_y1.get())); self.mono_content .set(child.content.get().at_point(mb.x1(), mb.y1())); } fn perform_split_layout(self: &Rc) { let sum_factors = self.sum_factors.get(); let split = self.split.get(); let spacing = self.child_spacing(); let (content_size, other_content_size) = match split { ContainerSplit::Horizontal => (self.content_width.get(), self.content_height.get()), ContainerSplit::Vertical => (self.content_height.get(), self.content_width.get()), }; let num_children = self.num_children.get(); if num_children == 0 { return; } let mut pos = 0; let mut remaining_content_size = content_size; for child in self.children.iter() { let factor = child.factor.get() / sum_factors; child.factor.set(factor); let mut body_size = (content_size as f64 * factor).round() as i32; body_size = body_size.min(remaining_content_size); remaining_content_size -= body_size; let (x1, y1, width, height) = match split { ContainerSplit::Horizontal => (pos, 0, body_size, other_content_size), _ => (0, pos, other_content_size, body_size), }; let body = Rect::new_sized_saturating(x1, y1, width, height); child.body.set(body); pos += body_size + spacing; } if remaining_content_size > 0 { let size_per = remaining_content_size / num_children as i32; let mut rem = remaining_content_size % num_children as i32; pos = 0; for child in self.children.iter() { let mut body = child.body.get(); let mut add = size_per; if rem > 0 { rem -= 1; add += 1; } let (x1, y1, width, height, size) = match split { ContainerSplit::Horizontal => { let width = body.width() + add; (pos, 0, width, other_content_size, width) } _ => { let height = body.height() + add; (0, pos, other_content_size, height, height) } }; body = Rect::new_sized_saturating(x1, y1, width, height); child.body.set(body); pos += size + spacing; } } self.sum_factors.set(1.0); for child in self.children.iter() { let body = child.body.get(); let body = body.move_(self.abs_x1.get(), self.abs_y1.get()); child.node.clone().tl_change_extents(&body); child.position_content(); } } pub(super) fn update_content_size(&self) { let nc = self.num_children.get(); let spacing = self.child_spacing(); match self.split.get() { ContainerSplit::Horizontal => { let new_content_size = self.width.get().sub((nc - 1) as i32 * spacing).max(0); self.content_width.set(new_content_size); self.content_height.set(self.height.get()); } ContainerSplit::Vertical => { let new_content_size = self.height.get().sub((nc - 1) as i32 * spacing).max(0); self.content_height.set(new_content_size); self.content_width.set(self.width.get()); } } let tab_bar_height = if self.mono_child.is_some() { // Tab bar sits above the window with a configurable gap. let tbh = self.state.theme.sizes.tab_bar_height.get(); let gap = self.state.theme.sizes.tab_bar_gap.get(); tbh + gap } else { 0 }; self.mono_body.set(Rect::new_sized_saturating( 0, tab_bar_height, self.width.get(), (self.height.get() - tab_bar_height).max(0), )); } }