tree: split container layout
This commit is contained in:
parent
9437a56807
commit
100c657050
2 changed files with 238 additions and 230 deletions
|
|
@ -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>,
|
||||
|
|
|
|||
236
src/tree/container/layout.rs
Normal file
236
src/tree/container/layout.rs
Normal 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),
|
||||
));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue