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 drag_destination;
|
||||||
|
mod layout;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
|
||||||
pub use drag_destination::default_tile_drag_destination;
|
pub use drag_destination::default_tile_drag_destination;
|
||||||
|
|
@ -45,7 +46,7 @@ use {
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
mem,
|
||||||
ops::{Deref, DerefMut, Sub},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -190,23 +191,6 @@ struct CursorState {
|
||||||
op: Option<SeatOp>,
|
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 {
|
impl ContainerNode {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
state: &Rc<State>,
|
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(
|
fn pointer_move(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
_seat: &Rc<WlSeatGlobal>,
|
_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