2022-03-24 20:32:31 +01:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
2022-03-30 23:08:35 +02:00
|
|
|
use crate::shell::layout::{FocusDirection, Layout, Orientation};
|
2022-03-31 13:44:16 +02:00
|
|
|
use atomic_float::AtomicF64;
|
2022-03-24 20:32:31 +01:00
|
|
|
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
|
|
|
|
|
use smithay::{
|
|
|
|
|
desktop::{layer_map_for_output, Kind, Space, Window},
|
2022-03-29 18:03:21 +02:00
|
|
|
reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::{
|
|
|
|
|
ResizeEdge, State as XdgState,
|
|
|
|
|
},
|
2022-03-24 20:32:31 +01:00
|
|
|
utils::Rectangle,
|
2022-03-29 18:03:21 +02:00
|
|
|
wayland::{
|
|
|
|
|
seat::{PointerGrabStartData, Seat},
|
|
|
|
|
Serial,
|
|
|
|
|
},
|
2022-03-24 20:32:31 +01:00
|
|
|
};
|
2022-03-31 13:44:16 +02:00
|
|
|
use std::{
|
|
|
|
|
cell::RefCell,
|
|
|
|
|
sync::{atomic::Ordering, Arc},
|
|
|
|
|
};
|
2022-03-24 20:32:31 +01:00
|
|
|
|
2022-03-29 18:03:21 +02:00
|
|
|
mod grabs;
|
|
|
|
|
pub use self::grabs::*;
|
|
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct TilingLayout {
|
|
|
|
|
gaps: (i32, i32),
|
2022-03-31 13:44:16 +02:00
|
|
|
trees: Vec<Tree<Data>>,
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum Data {
|
|
|
|
|
Fork {
|
|
|
|
|
orientation: Orientation,
|
2022-03-31 13:44:16 +02:00
|
|
|
ratio: Arc<AtomicF64>,
|
2022-03-24 20:32:31 +01:00
|
|
|
},
|
|
|
|
|
Stack {
|
|
|
|
|
active: usize,
|
|
|
|
|
len: usize,
|
|
|
|
|
},
|
|
|
|
|
Window(Window),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct WindowInfo {
|
|
|
|
|
node: NodeId,
|
2022-03-31 13:44:16 +02:00
|
|
|
output: usize,
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Data {
|
|
|
|
|
fn fork() -> Data {
|
|
|
|
|
Data::Fork {
|
|
|
|
|
orientation: Orientation::Vertical,
|
2022-03-31 13:44:16 +02:00
|
|
|
ratio: Arc::new(AtomicF64::new(0.5)),
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TilingLayout {
|
2022-03-31 13:44:16 +02:00
|
|
|
pub fn new() -> TilingLayout {
|
|
|
|
|
TilingLayout {
|
|
|
|
|
gaps: (0, 4),
|
|
|
|
|
trees: Vec::new(),
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Layout for TilingLayout {
|
|
|
|
|
fn map_window<'a>(
|
|
|
|
|
&mut self,
|
|
|
|
|
space: &mut Space,
|
|
|
|
|
window: &Window,
|
|
|
|
|
seat: &Seat,
|
2022-03-31 13:44:16 +02:00
|
|
|
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
2022-03-24 20:32:31 +01:00
|
|
|
) {
|
2022-03-31 13:44:16 +02:00
|
|
|
self.map_window(space, window, Some(seat), Some(focus_stack));
|
2022-03-24 20:32:31 +01:00
|
|
|
self.refresh(space);
|
2022-03-29 14:41:09 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-30 23:08:35 +02:00
|
|
|
fn move_focus<'a>(
|
|
|
|
|
&mut self,
|
|
|
|
|
direction: FocusDirection,
|
|
|
|
|
seat: &Seat,
|
|
|
|
|
space: &mut Space,
|
|
|
|
|
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
|
|
|
|
) -> Option<Window> {
|
2022-03-31 13:44:16 +02:00
|
|
|
let output = super::output_from_seat(Some(seat), space);
|
2022-04-05 16:35:58 +02:00
|
|
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
2022-03-31 13:44:16 +02:00
|
|
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
|
|
|
|
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
2022-03-30 23:08:35 +02:00
|
|
|
let mut node_id = last_active;
|
|
|
|
|
while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) {
|
|
|
|
|
if let &Data::Fork {
|
|
|
|
|
ref orientation, ..
|
|
|
|
|
} = tree.get(&fork).unwrap().data()
|
|
|
|
|
{
|
|
|
|
|
// found a fork
|
|
|
|
|
// which child are we?
|
|
|
|
|
let first = tree.children_ids(&fork).unwrap().next() == Some(&child);
|
|
|
|
|
let focus_subtree = match (first, orientation, direction) {
|
|
|
|
|
(true, Orientation::Horizontal, FocusDirection::Down)
|
|
|
|
|
| (true, Orientation::Vertical, FocusDirection::Right) => {
|
|
|
|
|
tree.children_ids(&fork).unwrap().skip(1).next()
|
|
|
|
|
}
|
|
|
|
|
(false, Orientation::Horizontal, FocusDirection::Up)
|
|
|
|
|
| (false, Orientation::Vertical, FocusDirection::Left) => {
|
|
|
|
|
tree.children_ids(&fork).unwrap().next()
|
|
|
|
|
}
|
|
|
|
|
_ => None, // continue iterating
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if focus_subtree.is_some() {
|
|
|
|
|
let mut node_id = focus_subtree;
|
|
|
|
|
while node_id.is_some()
|
|
|
|
|
&& !matches!(
|
|
|
|
|
tree.get(node_id.as_ref().unwrap()).unwrap().data(),
|
|
|
|
|
Data::Window(_)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
// TODO, if ndoe_id is a stack, we want to assign the active node instead of the first
|
|
|
|
|
node_id = tree.children_ids(node_id.as_ref().unwrap()).unwrap().next();
|
|
|
|
|
}
|
|
|
|
|
if let Some(Data::Window(window)) =
|
|
|
|
|
node_id.and_then(|i| tree.get(&i).ok()).map(|n| n.data())
|
|
|
|
|
{
|
|
|
|
|
return Some(window.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
node_id = fork;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
2022-03-29 14:41:09 +02:00
|
|
|
fn update_orientation<'a>(
|
|
|
|
|
&mut self,
|
|
|
|
|
new_orientation: Orientation,
|
|
|
|
|
seat: &Seat,
|
|
|
|
|
space: &mut Space,
|
2022-03-30 23:08:35 +02:00
|
|
|
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
|
2022-03-29 14:41:09 +02:00
|
|
|
) {
|
2022-03-31 13:44:16 +02:00
|
|
|
let output = super::output_from_seat(Some(seat), space);
|
2022-04-05 16:35:58 +02:00
|
|
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
2022-03-31 13:44:16 +02:00
|
|
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
|
|
|
|
if let Some(last_active) = TilingLayout::last_active_window(tree, focus_stack) {
|
|
|
|
|
if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) {
|
|
|
|
|
if let &mut Data::Fork {
|
|
|
|
|
ref mut orientation,
|
|
|
|
|
..
|
|
|
|
|
} = tree.get_mut(&fork).unwrap().data_mut()
|
|
|
|
|
{
|
|
|
|
|
*orientation = new_orientation;
|
2022-03-29 14:41:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
self.refresh(space);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-31 13:44:16 +02:00
|
|
|
fn refresh<'a>(&mut self, space: &mut Space) {
|
|
|
|
|
let active_outputs = space.outputs().count();
|
|
|
|
|
if self.trees.len() > active_outputs {
|
|
|
|
|
for tree in self
|
|
|
|
|
.trees
|
|
|
|
|
.drain(active_outputs..self.trees.len())
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
|
|
|
|
if let Some(root_id) = tree.root_node_id() {
|
|
|
|
|
for node in tree.traverse_pre_order(root_id).unwrap() {
|
|
|
|
|
if let Data::Window(window) = node.data() {
|
|
|
|
|
self.map_window(space, window, None, None);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-30 23:08:35 +02:00
|
|
|
while let Some(dead_windows) = Some(TilingLayout::update_space_positions(
|
2022-03-31 13:44:16 +02:00
|
|
|
&mut self.trees,
|
|
|
|
|
space,
|
|
|
|
|
self.gaps,
|
2022-03-30 23:08:35 +02:00
|
|
|
))
|
|
|
|
|
.filter(|v| !v.is_empty())
|
2022-03-24 20:32:31 +01:00
|
|
|
{
|
|
|
|
|
for window in dead_windows {
|
|
|
|
|
self.unmap_window(&window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-29 18:03:21 +02:00
|
|
|
|
2022-03-30 22:11:29 +02:00
|
|
|
fn unmap_window(&mut self, space: &mut Space, window: &Window) {
|
|
|
|
|
self.unmap_window(&window);
|
|
|
|
|
space.unmap_window(&window);
|
|
|
|
|
self.refresh(space);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 18:03:21 +02:00
|
|
|
fn resize_request(
|
|
|
|
|
&mut self,
|
2022-03-31 13:44:16 +02:00
|
|
|
space: &mut Space,
|
2022-03-29 18:03:21 +02:00
|
|
|
window: &Window,
|
|
|
|
|
seat: &Seat,
|
|
|
|
|
serial: Serial,
|
|
|
|
|
start_data: PointerGrabStartData,
|
|
|
|
|
edges: ResizeEdge,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(pointer) = seat.get_pointer() {
|
|
|
|
|
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
|
2022-03-31 13:44:16 +02:00
|
|
|
let output = info.borrow().output;
|
|
|
|
|
let tree = TilingLayout::active_tree(&mut self.trees, output);
|
2022-03-29 18:03:21 +02:00
|
|
|
let mut node_id = info.borrow().node.clone();
|
2022-03-30 23:08:35 +02:00
|
|
|
|
|
|
|
|
while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) {
|
2022-03-29 18:03:21 +02:00
|
|
|
if let &Data::Fork {
|
|
|
|
|
ref orientation,
|
|
|
|
|
ref ratio,
|
2022-03-30 23:08:35 +02:00
|
|
|
} = tree.get(&fork).unwrap().data()
|
2022-03-29 18:03:21 +02:00
|
|
|
{
|
|
|
|
|
// found a fork
|
|
|
|
|
// which child are we?
|
2022-03-30 23:08:35 +02:00
|
|
|
let first = tree.children_ids(&fork).unwrap().next() == Some(&child);
|
2022-03-29 18:03:21 +02:00
|
|
|
match (first, orientation, edges) {
|
|
|
|
|
(true, Orientation::Horizontal, ResizeEdge::Bottom)
|
|
|
|
|
| (false, Orientation::Horizontal, ResizeEdge::Top)
|
|
|
|
|
| (true, Orientation::Vertical, ResizeEdge::Right)
|
|
|
|
|
| (false, Orientation::Vertical, ResizeEdge::Left) => {
|
2022-03-31 13:44:16 +02:00
|
|
|
if let Some(output) = space.outputs().nth(output) {
|
|
|
|
|
let grab = ResizeForkGrab {
|
|
|
|
|
start_data,
|
|
|
|
|
orientation: *orientation,
|
|
|
|
|
initial_ratio: ratio.load(Ordering::SeqCst),
|
|
|
|
|
initial_size: layer_map_for_output(output)
|
|
|
|
|
.non_exclusive_zone()
|
|
|
|
|
.size,
|
|
|
|
|
ratio: ratio.clone(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pointer.set_grab(grab, serial, 0);
|
|
|
|
|
}
|
2022-03-29 18:03:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2022-03-30 23:08:35 +02:00
|
|
|
_ => {} // continue iterating
|
2022-03-29 18:03:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-30 23:08:35 +02:00
|
|
|
node_id = fork;
|
2022-03-29 18:03:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TilingLayout {
|
2022-03-31 13:44:16 +02:00
|
|
|
fn active_tree<'a>(trees: &'a mut Vec<Tree<Data>>, output: usize) -> &'a mut Tree<Data> {
|
|
|
|
|
while trees.len() <= output {
|
|
|
|
|
trees.push(Tree::new())
|
|
|
|
|
}
|
|
|
|
|
&mut trees[output]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn last_active_window<'a>(
|
2022-03-30 23:08:35 +02:00
|
|
|
tree: &mut Tree<Data>,
|
2022-03-31 13:44:16 +02:00
|
|
|
mut focus_stack: impl Iterator<Item = &'a Window>,
|
2022-03-30 23:08:35 +02:00
|
|
|
) -> Option<NodeId> {
|
|
|
|
|
let last_active = focus_stack
|
|
|
|
|
.find_map(|window| tree.root_node_id()
|
|
|
|
|
.and_then(|root| tree.traverse_pre_order_ids(root).unwrap()
|
|
|
|
|
.find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window))
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
last_active
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_fork(tree: &mut Tree<Data>, mut node_id: NodeId) -> Option<(NodeId, NodeId)> {
|
|
|
|
|
while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() {
|
|
|
|
|
if let &Data::Fork { .. } = tree.get(&parent_id).unwrap().data() {
|
|
|
|
|
return Some((parent_id, node_id));
|
|
|
|
|
}
|
|
|
|
|
node_id = parent_id;
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 13:44:16 +02:00
|
|
|
fn map_window<'a>(
|
|
|
|
|
&mut self,
|
|
|
|
|
space: &mut Space,
|
|
|
|
|
window: &Window,
|
|
|
|
|
seat: Option<&Seat>,
|
|
|
|
|
focus_stack: Option<Box<dyn Iterator<Item = &'a Window> + 'a>>,
|
|
|
|
|
) {
|
|
|
|
|
let output = super::output_from_seat(seat, space);
|
2022-04-05 16:35:58 +02:00
|
|
|
let idx = space.outputs().position(|o| Some(o) == output.as_ref()).unwrap_or(0);
|
2022-03-31 13:44:16 +02:00
|
|
|
let tree = TilingLayout::active_tree(&mut self.trees, idx);
|
|
|
|
|
let new_window = Node::new(Data::Window(window.clone()));
|
|
|
|
|
|
|
|
|
|
let last_active = focus_stack.and_then(|mut iter|
|
|
|
|
|
iter.find_map(|window| tree.root_node_id()
|
|
|
|
|
.and_then(|root| tree.traverse_pre_order_ids(root).unwrap()
|
|
|
|
|
.find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window))
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
let window_id = if let Some(ref node_id) = last_active {
|
|
|
|
|
let parent_id = tree.get(node_id).unwrap().parent().cloned();
|
|
|
|
|
if let Some(stack_id) =
|
|
|
|
|
parent_id.filter(|id| matches!(tree.get(id).unwrap().data(), Data::Stack { .. }))
|
|
|
|
|
{
|
|
|
|
|
// we add to the stack
|
|
|
|
|
let window_id = tree
|
|
|
|
|
.insert(new_window, InsertBehavior::UnderNode(&stack_id))
|
|
|
|
|
.unwrap();
|
|
|
|
|
if let Data::Stack {
|
|
|
|
|
ref mut len,
|
|
|
|
|
ref mut active,
|
|
|
|
|
} = tree.get_mut(&stack_id).unwrap().data_mut()
|
|
|
|
|
{
|
|
|
|
|
*active = *len;
|
|
|
|
|
*len += 1;
|
|
|
|
|
}
|
|
|
|
|
Ok(window_id)
|
|
|
|
|
} else {
|
|
|
|
|
// we create a new fork
|
|
|
|
|
TilingLayout::new_fork(tree, node_id, new_window)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// nothing? then we add to the root
|
|
|
|
|
if let Some(root_id) = tree.root_node_id().cloned() {
|
|
|
|
|
TilingLayout::new_fork(tree, &root_id, new_window)
|
|
|
|
|
} else {
|
|
|
|
|
tree.insert(new_window, InsertBehavior::AsRoot)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let user_data = window.user_data();
|
|
|
|
|
let window_info = WindowInfo {
|
|
|
|
|
node: window_id.clone(),
|
|
|
|
|
output: idx,
|
|
|
|
|
};
|
|
|
|
|
// insert or update
|
|
|
|
|
if !user_data.insert_if_missing(|| RefCell::new(window_info.clone())) {
|
|
|
|
|
*user_data.get::<RefCell<WindowInfo>>().unwrap().borrow_mut() = window_info;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-24 20:32:31 +01:00
|
|
|
fn unmap_window(&mut self, window: &Window) {
|
|
|
|
|
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
|
2022-03-31 13:44:16 +02:00
|
|
|
let output = info.borrow().output;
|
|
|
|
|
let tree = TilingLayout::active_tree(&mut self.trees, output);
|
2022-03-24 20:32:31 +01:00
|
|
|
|
|
|
|
|
let node_id = info.borrow().node.clone();
|
|
|
|
|
let parent_id = tree
|
|
|
|
|
.get(&node_id)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|node| node.parent())
|
|
|
|
|
.cloned();
|
|
|
|
|
let parent_parent_id = parent_id.as_ref().and_then(|parent_id| {
|
|
|
|
|
tree.get(parent_id)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|node| node.parent())
|
|
|
|
|
.cloned()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// remove self
|
|
|
|
|
slog_scope::debug!("Remove window {:?}", window);
|
|
|
|
|
let _ = tree.remove_node(node_id.clone(), RemoveBehavior::DropChildren);
|
|
|
|
|
|
|
|
|
|
// fixup parent node
|
|
|
|
|
match parent_id {
|
|
|
|
|
Some(id) if matches!(tree.get(&id).unwrap().data(), Data::Fork { .. }) => {
|
|
|
|
|
slog_scope::debug!("Removing Fork");
|
|
|
|
|
let other_child = tree.children_ids(&id).unwrap().cloned().next().unwrap();
|
|
|
|
|
let fork_pos = parent_parent_id.as_ref().and_then(|parent_id| {
|
|
|
|
|
tree.children_ids(parent_id).unwrap().position(|i| i == &id)
|
|
|
|
|
});
|
|
|
|
|
let _ = tree.remove_node(id.clone(), RemoveBehavior::OrphanChildren);
|
|
|
|
|
tree.move_node(
|
|
|
|
|
&other_child,
|
|
|
|
|
parent_parent_id
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|parent_id| MoveBehavior::ToParent(parent_id))
|
|
|
|
|
.unwrap_or(MoveBehavior::ToRoot),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
if let Some(old_pos) = fork_pos {
|
|
|
|
|
tree.make_nth_sibling(&other_child, old_pos).unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Some(id) if matches!(tree.get(&id).unwrap().data(), Data::Stack { .. }) => {
|
|
|
|
|
if tree.children_ids(&id).unwrap().count() == 0 {
|
|
|
|
|
slog_scope::debug!("Removing Stack");
|
|
|
|
|
// remove stack
|
|
|
|
|
let _ = tree.remove_node(id.clone(), RemoveBehavior::DropChildren);
|
|
|
|
|
// TODO now we need to untangle the parent_parent
|
|
|
|
|
// So we should REFACTOR this unmap function to not only work with windows
|
|
|
|
|
} else {
|
|
|
|
|
// fixup stack values
|
|
|
|
|
if let Data::Stack {
|
|
|
|
|
ref mut active,
|
|
|
|
|
ref mut len,
|
|
|
|
|
} = tree.get_mut(&id).unwrap().data_mut()
|
|
|
|
|
{
|
|
|
|
|
*len -= 1;
|
|
|
|
|
*active = std::cmp::max(*active, *len - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => {} // root
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 23:08:35 +02:00
|
|
|
fn new_fork(
|
|
|
|
|
tree: &mut Tree<Data>,
|
|
|
|
|
old_id: &NodeId,
|
|
|
|
|
new: Node<Data>,
|
|
|
|
|
) -> Result<NodeId, NodeIdError> {
|
|
|
|
|
let new_fork = Node::new(Data::fork());
|
|
|
|
|
let old = tree.get(old_id)?;
|
|
|
|
|
let parent_id = old.parent().cloned();
|
|
|
|
|
let pos = parent_id.as_ref().and_then(|parent_id| {
|
|
|
|
|
tree.children_ids(parent_id)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.position(|id| id == old_id)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let fork_id = tree
|
|
|
|
|
.insert(
|
|
|
|
|
new_fork,
|
|
|
|
|
if let Some(parent) = parent_id.as_ref() {
|
|
|
|
|
InsertBehavior::UnderNode(parent)
|
|
|
|
|
} else {
|
|
|
|
|
InsertBehavior::AsRoot
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
tree.move_node(old_id, MoveBehavior::ToParent(&fork_id))
|
|
|
|
|
.unwrap();
|
|
|
|
|
// keep position
|
|
|
|
|
if let Some(old_pos) = pos {
|
|
|
|
|
tree.make_nth_sibling(&fork_id, old_pos).unwrap();
|
|
|
|
|
}
|
|
|
|
|
tree.insert(new, InsertBehavior::UnderNode(&fork_id))
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-31 13:44:16 +02:00
|
|
|
fn update_space_positions(
|
|
|
|
|
trees: &mut Vec<Tree<Data>>,
|
|
|
|
|
space: &mut Space,
|
|
|
|
|
gaps: (i32, i32),
|
|
|
|
|
) -> Vec<Window> {
|
2022-03-30 23:08:35 +02:00
|
|
|
let mut dead_windows = Vec::new();
|
|
|
|
|
let (outer, inner) = gaps;
|
2022-03-31 13:44:16 +02:00
|
|
|
for (idx, output) in space
|
|
|
|
|
.outputs()
|
|
|
|
|
.cloned()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.into_iter()
|
|
|
|
|
{
|
|
|
|
|
let tree = TilingLayout::active_tree(trees, idx);
|
|
|
|
|
if let Some(root) = tree.root_node_id() {
|
|
|
|
|
let mut stack = Vec::new();
|
|
|
|
|
|
|
|
|
|
let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone());
|
|
|
|
|
// TODO saturate? minimum?
|
|
|
|
|
if let Some(mut geo) = geo.as_mut() {
|
|
|
|
|
geo.loc.x += outer;
|
|
|
|
|
geo.loc.y += outer;
|
|
|
|
|
geo.size.w -= outer * 2;
|
|
|
|
|
geo.size.h -= outer * 2;
|
|
|
|
|
}
|
2022-03-30 23:08:35 +02:00
|
|
|
|
2022-03-31 13:44:16 +02:00
|
|
|
for node in tree.traverse_pre_order(root).unwrap() {
|
|
|
|
|
let geo = stack.pop().unwrap_or(geo);
|
|
|
|
|
match node.data() {
|
|
|
|
|
Data::Fork { orientation, ratio } => {
|
|
|
|
|
if let Some(geo) = geo {
|
|
|
|
|
match orientation {
|
|
|
|
|
Orientation::Horizontal => {
|
|
|
|
|
let top_size = (
|
|
|
|
|
geo.size.w,
|
|
|
|
|
((geo.size.h as f64) * ratio.load(Ordering::SeqCst))
|
|
|
|
|
.ceil()
|
|
|
|
|
as i32,
|
|
|
|
|
);
|
|
|
|
|
stack.push(Some(Rectangle::from_loc_and_size(
|
|
|
|
|
(geo.loc.x, geo.loc.y + top_size.1),
|
|
|
|
|
(geo.size.w, geo.size.h - top_size.1),
|
|
|
|
|
)));
|
|
|
|
|
stack.push(Some(Rectangle::from_loc_and_size(
|
|
|
|
|
geo.loc, top_size,
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
Orientation::Vertical => {
|
|
|
|
|
let left_size = (
|
|
|
|
|
((geo.size.w as f64) * ratio.load(Ordering::SeqCst))
|
|
|
|
|
.ceil()
|
|
|
|
|
as i32,
|
|
|
|
|
geo.size.h,
|
|
|
|
|
);
|
|
|
|
|
stack.push(Some(Rectangle::from_loc_and_size(
|
|
|
|
|
(geo.loc.x + left_size.0, geo.loc.y),
|
|
|
|
|
(geo.size.w - left_size.0, geo.size.h),
|
|
|
|
|
)));
|
|
|
|
|
stack.push(Some(Rectangle::from_loc_and_size(
|
|
|
|
|
geo.loc, left_size,
|
|
|
|
|
)));
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
} else {
|
|
|
|
|
stack.push(None);
|
|
|
|
|
stack.push(None);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
}
|
|
|
|
|
Data::Stack { active, len } => {
|
|
|
|
|
for i in 0..*len {
|
|
|
|
|
if i == *active {
|
|
|
|
|
stack.push(geo);
|
|
|
|
|
} else {
|
|
|
|
|
stack.push(None);
|
2022-03-30 23:08:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
}
|
|
|
|
|
Data::Window(window) => {
|
|
|
|
|
if window.toplevel().alive() {
|
|
|
|
|
if let Some(geo) = geo {
|
|
|
|
|
#[allow(irrefutable_let_patterns)]
|
|
|
|
|
if let Kind::Xdg(xdg) = &window.toplevel() {
|
2022-04-26 16:56:39 +02:00
|
|
|
if xdg.current_state().map(|state| state.states.contains(XdgState::Fullscreen)).unwrap_or(false) ||
|
|
|
|
|
xdg.with_pending_state(|pending| pending.states.contains(XdgState::Fullscreen)).unwrap_or(false) {
|
2022-04-22 15:18:28 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
let ret = xdg.with_pending_state(|state| {
|
|
|
|
|
state.size = Some(
|
|
|
|
|
(geo.size.w - inner * 2, geo.size.h - inner * 2)
|
|
|
|
|
.into(),
|
|
|
|
|
);
|
|
|
|
|
state.states.set(XdgState::TiledLeft);
|
|
|
|
|
state.states.set(XdgState::TiledRight);
|
|
|
|
|
state.states.set(XdgState::TiledTop);
|
|
|
|
|
state.states.set(XdgState::TiledBottom);
|
|
|
|
|
});
|
|
|
|
|
if ret.is_ok() {
|
|
|
|
|
xdg.send_configure();
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
let window_geo = window.geometry();
|
|
|
|
|
space.map_window(
|
|
|
|
|
&window,
|
|
|
|
|
(
|
|
|
|
|
geo.loc.x + inner - window_geo.loc.x,
|
|
|
|
|
geo.loc.y + inner - window_geo.loc.y,
|
|
|
|
|
),
|
|
|
|
|
false,
|
|
|
|
|
);
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
2022-03-31 13:44:16 +02:00
|
|
|
} else {
|
|
|
|
|
dead_windows.push(window.clone());
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-30 23:08:35 +02:00
|
|
|
dead_windows
|
2022-03-24 20:32:31 +01:00
|
|
|
}
|
|
|
|
|
}
|