236 lines
8.6 KiB
Rust
236 lines
8.6 KiB
Rust
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>) {
|
|
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>) {
|
|
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<Self>) {
|
|
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>) {
|
|
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>) {
|
|
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<Self>, 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<Self>) {
|
|
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),
|
|
));
|
|
}
|
|
}
|