1
0
Fork 0
forked from wry/wry

tree: split container layout

This commit is contained in:
kossLAN 2026-05-29 21:39:35 -04:00
parent 9437a56807
commit 100c657050
No known key found for this signature in database
2 changed files with 238 additions and 230 deletions

View file

@ -1,4 +1,5 @@
mod drag_destination;
mod layout;
mod tasks;
pub use drag_destination::default_tile_drag_destination;
@ -45,7 +46,7 @@ use {
cell::{Cell, RefCell},
fmt::{Debug, Formatter},
mem,
ops::{Deref, DerefMut, Sub},
ops::{Deref, DerefMut},
rc::Rc,
},
};
@ -190,23 +191,6 @@ struct CursorState {
op: Option<SeatOp>,
}
impl ContainerChild {
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 new(
state: &Rc<State>,
@ -391,218 +375,6 @@ 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();
}
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,
));
}
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 }
}
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());
}
}
fn schedule_layout_immediate(self: &Rc<Self>) {
self.schedule_layout();
if self.toplevel_data.visible.get() {
self.damage();
}
}
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
}
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();
}
}
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),
));
}
fn pointer_move(
self: &Rc<Self>,
_seat: &Rc<WlSeatGlobal>,

View file

@ -0,0 +1,236 @@
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),
));
}
}