cosmic-comp/src/shell/layout/tiling/mod.rs

5342 lines
218 KiB
Rust
Raw Normal View History

2022-03-24 20:32:31 +01:00
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage},
2022-09-28 12:01:29 +02:00
shell::{
2023-06-05 17:52:47 +02:00
element::{
2023-07-06 00:03:26 +02:00
resize_indicator::ResizeIndicator,
stack::{
CosmicStackRenderElement, MoveResult as StackMoveResult,
TAB_HEIGHT as STACK_TAB_HEIGHT,
},
swap_indicator::SwapIndicator,
window::CosmicWindowRenderElement,
CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow,
2023-06-05 17:52:47 +02:00
},
2022-09-28 12:01:29 +02:00
focus::{
target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
2023-06-05 17:52:47 +02:00
FocusDirection, FocusStackMut,
2022-09-28 12:01:29 +02:00
},
grabs::ResizeEdge,
2022-09-28 12:01:29 +02:00
layout::Orientation,
CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode,
ResizeDirection, ResizeMode, Trigger,
2022-09-28 12:01:29 +02:00
},
theme::group_color,
utils::{prelude::*, tween::EaseRectangle},
wayland::{
handlers::xdg_shell::popup::get_popup_toplevel,
protocols::{toplevel_info::ToplevelInfoState, workspace::WorkspaceHandle},
},
};
2022-03-24 20:32:31 +01:00
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
use keyframe::{
ease,
functions::{EaseInOutCubic, Linear},
};
2022-03-24 20:32:31 +01:00
use smithay::{
backend::renderer::{
2023-05-17 19:46:21 +02:00
element::{
2024-02-23 20:08:57 +01:00
utils::{
constrain_render_elements, ConstrainAlign, ConstrainScaleBehavior,
RescaleRenderElement,
},
AsRenderElements, Id, RenderElement,
2023-05-17 19:46:21 +02:00
},
2023-07-21 16:08:55 +02:00
glow::GlowRenderer,
ImportAll, ImportMem, Renderer,
},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind},
input::Seat,
2023-10-25 19:40:26 +02:00
output::Output,
2023-07-05 23:48:10 +02:00
reexports::wayland_server::Client,
utils::{IsAlive, Logical, Point, Rectangle, Scale, Size},
2023-05-12 20:01:37 +02:00
wayland::{compositor::add_blocker, seat::WaylandFocus},
};
use std::{
collections::{HashMap, VecDeque},
2023-05-30 21:02:19 +02:00
sync::{Arc, Weak},
2023-05-12 20:01:37 +02:00
time::{Duration, Instant},
2022-03-24 20:32:31 +01:00
};
use tracing::trace;
2023-07-05 23:48:10 +02:00
use wayland_backend::server::ClientId;
2022-03-24 20:32:31 +01:00
2023-05-12 20:01:37 +02:00
mod blocker;
2022-03-29 18:03:21 +02:00
mod grabs;
2023-05-12 20:01:37 +02:00
pub use self::blocker::*;
2022-03-29 18:03:21 +02:00
pub use self::grabs::*;
2022-09-28 12:01:29 +02:00
pub const ANIMATION_DURATION: Duration = Duration::from_millis(200);
2024-02-28 19:56:15 +01:00
pub const MINIMIZE_ANIMATION_DURATION: Duration = Duration::from_millis(300);
2023-07-26 16:13:12 +02:00
pub const MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(150);
2023-07-27 20:24:02 +02:00
pub const INITIAL_MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(500);
2023-05-12 20:01:37 +02:00
#[derive(Debug, Clone, PartialEq)]
pub struct NodeDesc {
pub handle: WorkspaceHandle,
pub node: NodeId,
pub stack_window: Option<CosmicSurface>,
}
2023-07-21 16:08:55 +02:00
#[derive(Debug, Clone, PartialEq)]
enum TargetZone {
2023-07-31 17:29:42 +02:00
Initial,
2023-07-21 16:08:55 +02:00
InitialPlaceholder(NodeId),
WindowStack(NodeId, Rectangle<i32, Local>),
2023-07-21 16:08:55 +02:00
WindowSplit(NodeId, Direction),
GroupEdge(NodeId, Direction),
GroupInterior(NodeId, usize),
}
impl TargetZone {
fn is_window_zone(&self) -> bool {
matches!(
self,
2023-07-24 19:31:31 +02:00
TargetZone::WindowStack(..) | TargetZone::WindowSplit(_, _)
2023-07-21 16:08:55 +02:00
)
}
}
2023-05-12 20:01:37 +02:00
#[derive(Debug, Clone, Default)]
struct TreeQueue {
trees: VecDeque<(Tree<Data>, Duration, Option<TilingBlocker>)>,
2023-05-12 20:01:37 +02:00
animation_start: Option<Instant>,
}
impl TreeQueue {
pub fn push_tree(
&mut self,
tree: Tree<Data>,
duration: impl Into<Option<Duration>>,
blocker: Option<TilingBlocker>,
) {
self.trees
.push_back((tree, duration.into().unwrap_or(Duration::ZERO), blocker))
2023-05-12 20:01:37 +02:00
}
}
2022-09-28 12:01:29 +02:00
#[derive(Debug, Clone)]
2022-03-24 20:32:31 +01:00
pub struct TilingLayout {
2023-10-25 19:40:26 +02:00
output: Output,
queue: TreeQueue,
2023-05-12 20:01:37 +02:00
pending_blockers: Vec<TilingBlocker>,
placeholder_id: Id,
swapping_stack_surface_id: Id,
2023-07-21 16:08:55 +02:00
last_overview_hover: Option<(Option<Instant>, TargetZone)>,
pub theme: cosmic::Theme,
2023-07-21 16:08:55 +02:00
}
#[derive(Debug, Clone, PartialEq)]
pub enum PillIndicator {
Outer(Direction),
Inner(usize),
2022-03-24 20:32:31 +01:00
}
2022-09-28 12:01:29 +02:00
#[derive(Debug, Clone)]
2022-03-24 20:32:31 +01:00
pub enum Data {
2022-09-28 12:01:29 +02:00
Group {
2022-03-24 20:32:31 +01:00
orientation: Orientation,
2022-09-28 12:01:29 +02:00
sizes: Vec<i32>,
last_geometry: Rectangle<i32, Local>,
2022-09-28 12:01:29 +02:00
alive: Arc<()>,
2023-07-21 16:08:55 +02:00
pill_indicator: Option<PillIndicator>,
2022-03-24 20:32:31 +01:00
},
2022-09-28 12:01:29 +02:00
Mapped {
mapped: CosmicMapped,
last_geometry: Rectangle<i32, Local>,
2024-02-23 20:08:57 +01:00
minimize_rect: Option<Rectangle<i32, Local>>,
2022-03-24 20:32:31 +01:00
},
Placeholder {
last_geometry: Rectangle<i32, Local>,
2023-07-21 16:08:55 +02:00
initial_placeholder: bool,
},
2022-03-24 20:32:31 +01:00
}
impl Data {
fn new_group(orientation: Orientation, geo: Rectangle<i32, Local>) -> Data {
2022-09-28 12:01:29 +02:00
Data::Group {
orientation,
sizes: vec![
match orientation {
Orientation::Vertical => geo.size.w / 2,
Orientation::Horizontal => geo.size.h / 2,
};
2
],
last_geometry: geo,
alive: Arc::new(()),
2023-07-21 16:08:55 +02:00
pill_indicator: None,
2022-09-28 12:01:29 +02:00
}
}
fn is_group(&self) -> bool {
matches!(self, Data::Group { .. })
}
fn is_mapped(&self, mapped: Option<&CosmicMapped>) -> bool {
match mapped {
Some(m) => matches!(self, Data::Mapped { mapped, .. } if m == mapped),
None => matches!(self, Data::Mapped { .. }),
}
}
fn is_stack(&self) -> bool {
match self {
Data::Mapped { mapped, .. } => mapped.is_stack(),
_ => false,
}
}
fn is_placeholder(&self) -> bool {
match self {
Data::Placeholder { .. } => true,
_ => false,
}
}
2022-09-28 12:01:29 +02:00
fn orientation(&self) -> Orientation {
match self {
Data::Group { orientation, .. } => *orientation,
_ => panic!("Not a group"),
}
}
fn add_window(&mut self, idx: usize) {
match self {
Data::Group {
sizes,
last_geometry,
orientation,
..
} => {
let last_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let equal_sizing = last_length / (sizes.len() as i32 + 1); // new window size
let remainder = last_length - equal_sizing; // size for the rest of the windowns
for size in sizes.iter_mut() {
*size = ((*size as f64 / last_length as f64) * remainder as f64).round() as i32;
}
let used_size: i32 = sizes.iter().sum();
let new_size = last_length - used_size;
sizes.insert(idx, new_size);
}
_ => panic!("Adding window to leaf?"),
2022-09-28 12:01:29 +02:00
}
}
2022-10-27 16:11:54 +02:00
fn swap_windows(&mut self, i: usize, j: usize) {
match self {
Data::Group { sizes, .. } => {
sizes.swap(i, j);
}
_ => panic!("Swapping windows to a leaf?"),
2022-10-27 16:11:54 +02:00
}
}
2022-09-28 12:01:29 +02:00
fn remove_window(&mut self, idx: usize) {
match self {
Data::Group {
sizes,
last_geometry,
orientation,
..
} => {
let last_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let old_size = sizes.remove(idx);
let remaining_size: i32 = sizes.iter().sum();
2022-09-28 12:01:29 +02:00
for size in sizes.iter_mut() {
*size +=
((*size as f64 / remaining_size as f64) * old_size as f64).round() as i32;
2022-09-28 12:01:29 +02:00
}
let used_size: i32 = sizes.iter().sum();
let overflow = last_length - used_size;
if overflow != 0 {
*sizes.last_mut().unwrap() += overflow;
}
}
_ => panic!("Added window to leaf?"),
2022-09-28 12:01:29 +02:00
}
}
fn geometry(&self) -> &Rectangle<i32, Local> {
2022-09-28 12:01:29 +02:00
match self {
Data::Group { last_geometry, .. } => last_geometry,
Data::Mapped { last_geometry, .. } => last_geometry,
2023-07-21 16:08:55 +02:00
Data::Placeholder { last_geometry, .. } => last_geometry,
2022-09-28 12:01:29 +02:00
}
}
fn update_geometry(&mut self, geo: Rectangle<i32, Local>) {
2022-09-28 12:01:29 +02:00
match self {
Data::Group {
orientation,
sizes,
last_geometry,
..
} => {
let previous_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let new_length = match orientation {
Orientation::Horizontal => geo.size.h,
Orientation::Vertical => geo.size.w,
};
sizes.iter_mut().for_each(|len| {
*len = (((*len as f64) / (previous_length as f64)) * (new_length as f64))
.round() as i32;
});
let sum: i32 = sizes.iter().sum();
// fix rounding issues
if sum != new_length {
let diff = new_length - sum;
*sizes.last_mut().unwrap() += diff;
2022-09-28 12:01:29 +02:00
}
*last_geometry = geo;
}
2023-07-21 16:08:55 +02:00
Data::Mapped { last_geometry, .. } | Data::Placeholder { last_geometry, .. } => {
2022-09-28 12:01:29 +02:00
*last_geometry = geo;
}
}
}
fn len(&self) -> usize {
match self {
Data::Group { sizes, .. } => sizes.len(),
_ => 1,
2022-03-24 20:32:31 +01:00
}
}
}
2023-05-30 21:02:19 +02:00
#[derive(Debug, Clone)]
enum FocusedNodeData {
Group(Vec<NodeId>, Weak<()>),
Window(CosmicMapped),
}
2024-02-23 17:25:40 +01:00
#[derive(Debug)]
pub struct MinimizedTilingState {
pub parent: Option<id_tree::NodeId>,
pub sibling: Option<id_tree::NodeId>,
2024-02-23 17:25:40 +01:00
pub orientation: Orientation,
pub idx: usize,
pub sizes: Vec<i32>,
}
2022-03-24 20:32:31 +01:00
impl TilingLayout {
pub fn new(theme: cosmic::Theme, output: &Output) -> TilingLayout {
TilingLayout {
2023-10-25 19:40:26 +02:00
queue: TreeQueue {
trees: {
let mut queue = VecDeque::new();
queue.push_back((Tree::new(), Duration::ZERO, None));
queue
},
animation_start: None,
},
output: output.clone(),
2023-05-12 20:01:37 +02:00
pending_blockers: Vec::new(),
placeholder_id: Id::new(),
swapping_stack_surface_id: Id::new(),
2023-07-21 16:08:55 +02:00
last_overview_hover: None,
theme,
}
2022-03-24 20:32:31 +01:00
}
2023-10-25 19:40:26 +02:00
pub fn set_output(&mut self, output: &Output) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2022-09-28 12:01:29 +02:00
2023-10-25 19:40:26 +02:00
for node in tree
.root_node_id()
.and_then(|root_id| tree.traverse_pre_order(root_id).ok())
.into_iter()
.flatten()
{
2024-02-23 17:25:40 +01:00
if let Data::Mapped { mapped, .. } = node.data() {
2023-10-25 19:40:26 +02:00
mapped.output_leave(&self.output);
mapped.output_enter(output, mapped.bbox());
}
2022-09-28 12:01:29 +02:00
}
2023-10-25 19:40:26 +02:00
let blocker = TilingLayout::update_positions(output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, None, blocker);
self.output = output.clone();
2022-09-28 12:01:29 +02:00
}
pub fn map<'a>(
2022-03-24 20:32:31 +01:00
&mut self,
2022-09-28 12:01:29 +02:00
window: CosmicMapped,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
direction: Option<Direction>,
2022-03-24 20:32:31 +01:00
) {
2023-10-25 19:40:26 +02:00
window.output_enter(&self.output, window.bbox());
window.set_bounds(self.output.geometry().size.as_logical());
self.map_internal(window, focus_stack, direction, None);
}
pub fn map_internal<'a>(
2022-09-28 12:01:29 +02:00
&mut self,
window: impl Into<CosmicMapped>,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
direction: Option<Direction>,
minimize_rect: Option<Rectangle<i32, Local>>,
2022-09-28 12:01:29 +02:00
) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
let last_active = focus_stack
2024-01-15 09:35:38 +00:00
.and_then(|focus_stack| TilingLayout::last_active_window(&mut tree, focus_stack))
.map(|(node_id, _)| node_id);
2024-02-28 19:56:15 +01:00
let duration = if minimize_rect.is_some() {
MINIMIZE_ANIMATION_DURATION
} else {
ANIMATION_DURATION
};
TilingLayout::map_to_tree(
&mut tree,
window,
&self.output,
last_active,
direction,
minimize_rect,
);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2024-02-28 19:56:15 +01:00
self.queue.push_tree(tree, duration, blocker);
2023-06-05 17:52:47 +02:00
}
2024-02-23 17:25:40 +01:00
pub fn remap_minimized<'a>(
&mut self,
window: CosmicMapped,
from: Rectangle<i32, Local>,
tiling_state: Option<MinimizedTilingState>,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
) {
2024-02-23 20:08:57 +01:00
window.set_minimized(false);
2024-02-23 17:25:40 +01:00
let gaps = self.gaps();
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
if let Some(MinimizedTilingState {
parent,
sibling,
2024-02-23 17:25:40 +01:00
orientation,
idx,
mut sizes,
}) = tiling_state
{
if let Some(node) = parent.as_ref().and_then(|parent| tree.get_mut(parent).ok()) {
2024-02-23 17:25:40 +01:00
if let Data::Group {
orientation: current_orientation,
sizes: current_sizes,
..
} = node.data_mut()
{
let parent_id = parent.unwrap();
2024-02-23 17:25:40 +01:00
if *current_orientation == orientation && sizes.len() == current_sizes.len() + 1
{
let previous_length: i32 = sizes.iter().copied().sum();
let new_length: i32 = current_sizes.iter().copied().sum();
if previous_length != new_length {
// rescale sizes
sizes.iter_mut().for_each(|len| {
*len = (((*len as f64) / (previous_length as f64))
* (new_length as f64))
.round() as i32;
});
let sum: i32 = sizes.iter().sum();
// fix rounding issues
if sum != new_length {
let diff = new_length - sum;
*sizes.last_mut().unwrap() += diff;
}
}
}
*current_sizes = sizes;
let new_node = Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
2024-02-23 20:08:57 +01:00
minimize_rect: Some(from),
2024-02-23 17:25:40 +01:00
});
let new_id = tree
.insert(new_node, InsertBehavior::UnderNode(&parent_id))
2024-02-23 17:25:40 +01:00
.unwrap();
tree.make_nth_sibling(&new_id, idx).unwrap();
*window.tiling_node_id.lock().unwrap() = Some(new_id);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2024-02-28 19:56:15 +01:00
self.queue
.push_tree(tree, MINIMIZE_ANIMATION_DURATION, blocker);
2024-02-23 17:25:40 +01:00
return;
}
}
if sibling
.as_ref()
.is_some_and(|sibling| tree.get(&sibling).is_ok())
{
let sibling_id = sibling.unwrap();
let new_node = Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
minimize_rect: Some(from),
});
let new_id = tree.insert(new_node, InsertBehavior::AsRoot).unwrap();
let group_id =
TilingLayout::new_group(&mut tree, &sibling_id, &new_id, orientation).unwrap();
tree.make_nth_sibling(&new_id, idx).unwrap();
if let Data::Group {
sizes: default_sizes,
last_geometry,
..
} = tree.get_mut(&group_id).unwrap().data_mut()
{
match orientation {
Orientation::Horizontal => {
last_geometry.size.h = sizes.iter().copied().sum()
}
Orientation::Vertical => last_geometry.size.w = sizes.iter().copied().sum(),
};
*default_sizes = sizes;
}
*window.tiling_node_id.lock().unwrap() = Some(new_id);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2024-02-28 19:56:15 +01:00
self.queue
.push_tree(tree, MINIMIZE_ANIMATION_DURATION, blocker);
return;
}
2024-02-23 17:25:40 +01:00
}
// else add as new_window
self.map_internal(window, focus_stack, None, Some(from));
2024-02-23 17:25:40 +01:00
}
2023-06-05 17:52:47 +02:00
fn map_to_tree<'a>(
mut tree: &mut Tree<Data>,
window: impl Into<CosmicMapped>,
output: &Output,
2024-01-15 09:35:38 +00:00
node: Option<NodeId>,
2023-06-05 17:52:47 +02:00
direction: Option<Direction>,
minimize_rect: Option<Rectangle<i32, Local>>,
2023-06-05 17:52:47 +02:00
) {
2022-09-28 12:01:29 +02:00
let window = window.into();
let new_window = Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
minimize_rect,
2022-09-28 12:01:29 +02:00
});
let window_id = if let Some(direction) = direction {
if let Some(root_id) = tree.root_node_id().cloned() {
let orientation = match direction {
Direction::Left | Direction::Right => Orientation::Vertical,
Direction::Up | Direction::Down => Orientation::Horizontal,
};
2022-09-28 12:01:29 +02:00
let new_id = tree.insert(new_window, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(&mut tree, &root_id, &new_id, orientation).unwrap();
tree.make_nth_sibling(
&new_id,
match direction {
Direction::Left | Direction::Up => 1,
Direction::Right | Direction::Down => 0,
},
)
.unwrap();
new_id
} else {
tree.insert(new_window, InsertBehavior::AsRoot).unwrap()
}
2022-09-28 12:01:29 +02:00
} else {
2024-01-15 09:35:38 +00:00
if let Some(ref node_id) = node {
2022-09-28 12:01:29 +02:00
let orientation = {
let window_size = tree.get(node_id).unwrap().data().geometry().size;
if window_size.w > window_size.h {
2022-09-28 12:01:29 +02:00
Orientation::Vertical
} else {
Orientation::Horizontal
}
};
2022-10-27 16:11:54 +02:00
let new_id = tree.insert(new_window, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(&mut tree, &node_id, &new_id, orientation).unwrap();
2022-10-27 16:11:54 +02:00
new_id
2022-09-28 12:01:29 +02:00
} else {
// nothing? then we add to the root
if let Some(root_id) = tree.root_node_id().cloned() {
let orientation = {
let output_size = output.geometry().size;
if output_size.w > output_size.h {
Orientation::Vertical
} else {
Orientation::Horizontal
}
};
let new_id = tree.insert(new_window, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(&mut tree, &root_id, &new_id, orientation).unwrap();
new_id
} else {
tree.insert(new_window, InsertBehavior::AsRoot).unwrap()
}
2022-09-28 12:01:29 +02:00
}
2022-10-27 16:11:54 +02:00
};
2022-09-28 12:01:29 +02:00
*window.tiling_node_id.lock().unwrap() = Some(window_id);
}
pub fn replace_window(&mut self, old: &CosmicMapped, new: &CosmicMapped) {
let gaps = self.gaps();
let Some(old_id) = old.tiling_node_id.lock().unwrap().clone() else {
return;
};
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2023-10-25 19:40:26 +02:00
if let Ok(node) = tree.get_mut(&old_id) {
let data = node.data_mut();
match data {
Data::Mapped { mapped, .. } => {
assert_eq!(mapped, old);
*mapped = new.clone();
*new.tiling_node_id.lock().unwrap() = Some(old_id);
*old.tiling_node_id.lock().unwrap() = None;
}
_ => unreachable!("Tiling id points to group"),
}
2023-10-25 19:40:26 +02:00
old.output_leave(&self.output);
new.output_enter(&self.output, new.bbox());
2023-09-11 21:12:20 +02:00
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
}
}
pub fn move_tree<'a>(
this: &mut Self,
other: &mut Self,
other_handle: &WorkspaceHandle,
seat: &Seat<State>,
focus_stack: impl Iterator<Item = &'a CosmicMapped> + 'a,
desc: NodeDesc,
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
) -> Option<KeyboardFocusTarget> {
let this_handle = &desc.handle;
2023-10-25 19:40:26 +02:00
let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone();
let mut other_tree = other.queue.trees.back().unwrap().0.copy_clone();
match desc.stack_window {
Some(stack_surface) => {
let node = this_tree.get(&desc.node).ok()?;
let Data::Mapped {
mapped: this_mapped,
..
} = node.data()
else {
return None;
};
let this_stack = this_mapped.stack_ref()?;
this_stack.remove_window(&stack_surface);
if !this_stack.alive() {
this.unmap(&this_mapped);
}
let mapped: CosmicMapped =
CosmicWindow::new(stack_surface, this_stack.loop_handle(), this.theme.clone())
.into();
2023-10-25 19:40:26 +02:00
if this.output != other.output {
mapped.output_leave(&this.output);
mapped.output_enter(&other.output, mapped.bbox());
for (ref surface, _) in mapped.windows() {
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_leave_output(surface, &this.output);
toplevel_info_state.toplevel_enter_output(surface, &other.output);
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_workspace(surface, this_handle);
toplevel_info_state.toplevel_enter_workspace(surface, other_handle);
}
mapped.set_tiled(true);
other.map(mapped.clone(), Some(focus_stack), None);
return Some(KeyboardFocusTarget::Element(mapped));
}
None => {
let node = this_tree.get(&desc.node).ok()?;
let mut children = node
.children()
.into_iter()
.map(|child_id| (desc.node.clone(), child_id.clone()))
.collect::<Vec<_>>();
let node = Node::new(node.data().clone());
TilingLayout::unmap_internal(&mut this_tree, &desc.node);
let id = match other_tree.root_node_id() {
None => other_tree.insert(node, InsertBehavior::AsRoot).unwrap(),
Some(_) => {
let focused_node = seat
.get_keyboard()
.unwrap()
.current_focus()
.and_then(|target| {
2023-10-25 19:40:26 +02:00
TilingLayout::currently_focused_node(&other_tree, target)
})
.map(|(id, _)| id)
.unwrap_or(other_tree.root_node_id().unwrap().clone());
let orientation = {
let window_size = other_tree
.get(&focused_node)
.unwrap()
.data()
.geometry()
.size;
if window_size.w > window_size.h {
Orientation::Vertical
} else {
Orientation::Horizontal
}
};
let id = other_tree.insert(node, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(&mut other_tree, &focused_node, &id, orientation)
.unwrap();
id
}
};
for (ref mut parent_id, _) in children.iter_mut() {
*parent_id = id.clone();
}
if let Data::Mapped { mapped, .. } = other_tree.get_mut(&id).unwrap().data_mut() {
2023-10-25 19:40:26 +02:00
if this.output != other.output {
mapped.output_leave(&this.output);
mapped.output_enter(&other.output, mapped.bbox());
for (ref surface, _) in mapped.windows() {
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_leave_output(surface, &this.output);
toplevel_info_state.toplevel_enter_output(surface, &other.output);
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_workspace(surface, this_handle);
toplevel_info_state.toplevel_enter_workspace(surface, other_handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(id.clone());
}
while !children.is_empty() {
for (parent_id, child_id) in std::mem::take(&mut children) {
let new_children = this_tree
.children_ids(&child_id)
.unwrap()
.cloned()
.collect::<Vec<_>>();
let child_node = this_tree
.remove_node(child_id, RemoveBehavior::OrphanChildren)
.unwrap();
let maybe_mapped = match child_node.data() {
Data::Mapped { mapped, .. } => Some(mapped.clone()),
_ => None,
};
let new_id = other_tree
.insert(child_node, InsertBehavior::UnderNode(&parent_id))
.unwrap();
if let Some(mapped) = maybe_mapped {
2023-10-25 19:40:26 +02:00
if this.output != other.output {
mapped.output_leave(&this.output);
mapped.output_enter(&other.output, mapped.bbox());
for (ref surface, _) in mapped.windows() {
toplevel_info_state
2023-10-25 19:40:26 +02:00
.toplevel_leave_output(surface, &this.output);
toplevel_info_state
2023-10-25 19:40:26 +02:00
.toplevel_enter_output(surface, &other.output);
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_workspace(surface, this_handle);
toplevel_info_state.toplevel_enter_workspace(surface, other_handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone());
}
children.extend(
new_children
.into_iter()
.map(|new_child| (new_id.clone(), new_child)),
);
}
}
let this_gaps = this.gaps();
let other_gaps = other.gaps();
let blocker =
TilingLayout::update_positions(&this.output, &mut this_tree, this_gaps);
2023-10-25 19:40:26 +02:00
this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker);
let blocker =
TilingLayout::update_positions(&other.output, &mut other_tree, other_gaps);
2023-10-25 19:40:26 +02:00
other
.queue
.push_tree(other_tree, ANIMATION_DURATION, blocker);
other.node_desc_to_focus(&NodeDesc {
handle: other_handle.clone(),
node: id,
stack_window: None,
})
}
}
}
pub fn swap_trees(
this: &mut Self,
mut other: Option<&mut Self>,
this_desc: &NodeDesc,
other_desc: &NodeDesc,
2023-09-11 21:12:20 +02:00
toplevel_info_state: &mut ToplevelInfoState<State, CosmicSurface>,
) -> Option<KeyboardFocusTarget> {
2023-10-25 19:40:26 +02:00
let other_output = other
.as_ref()
.map(|other| other.output.clone())
.unwrap_or(this.output.clone());
if this.output == other_output
&& this_desc.handle == other_desc.handle
&& this_desc.node == other_desc.node
&& this_desc.stack_window.is_some() != other_desc.stack_window.is_some()
{
return None;
}
2023-10-25 19:40:26 +02:00
let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone();
let mut other_tree = match other.as_mut() {
2023-10-25 19:40:26 +02:00
Some(other) => Some(other.queue.trees.back().unwrap().0.copy_clone()),
None => {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
Some(this.queue.trees.back().unwrap().0.copy_clone())
} else {
None
}
}
};
match (&this_desc.stack_window, &other_desc.stack_window) {
(None, None) if other_tree.is_none() => {
if this_desc.node != other_desc.node {
2023-10-25 19:40:26 +02:00
this_tree
.swap_nodes(
&this_desc.node,
&other_desc.node,
id_tree::SwapBehavior::TakeChildren,
)
.unwrap();
}
}
(None, None) => {
let Some(other_tree) = other_tree.as_mut() else {
unreachable!()
};
let this_node = this_tree.get_mut(&this_desc.node).ok()?;
let other_node = other_tree.get_mut(&other_desc.node).ok()?;
// swap data
let other_data = other_node.data().clone();
other_node.replace_data(this_node.replace_data(other_data));
if let Data::Mapped { mapped, .. } = this_node.data_mut() {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
2023-09-11 21:12:20 +02:00
mapped.output_leave(&other_output);
2023-10-25 19:40:26 +02:00
mapped.output_enter(&this.output, mapped.bbox());
2023-09-11 21:12:20 +02:00
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_output(surface, &other_output);
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_enter_output(surface, &this.output);
2023-09-11 21:12:20 +02:00
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_workspace(surface, &other_desc.handle);
toplevel_info_state.toplevel_enter_workspace(surface, &this_desc.handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone());
}
if let Data::Mapped { mapped, .. } = other_node.data_mut() {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
mapped.output_leave(&this.output);
2023-09-11 21:12:20 +02:00
mapped.output_enter(&other_output, mapped.bbox());
for (ref surface, _) in mapped.windows() {
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_leave_output(surface, &this.output);
2023-09-11 21:12:20 +02:00
toplevel_info_state.toplevel_enter_output(surface, &other_output);
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state.toplevel_leave_workspace(surface, &this_desc.handle);
toplevel_info_state.toplevel_enter_workspace(surface, &other_desc.handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(other_desc.node.clone());
}
// swap children
let mut this_children = this_node
.children()
.into_iter()
.map(|child_id| (other_desc.node.clone(), child_id.clone()))
.collect::<Vec<_>>();
while !this_children.is_empty() {
for (parent_id, child_id) in std::mem::take(&mut this_children) {
let new_children = this_tree
.children_ids(&child_id)
.unwrap()
.cloned()
.collect::<Vec<_>>();
let child_node = this_tree
.remove_node(child_id, RemoveBehavior::OrphanChildren)
.unwrap();
let maybe_mapped = match child_node.data() {
Data::Mapped { mapped, .. } => Some(mapped.clone()),
_ => None,
};
let new_id = other_tree
.insert(child_node, InsertBehavior::UnderNode(&parent_id))
.unwrap();
if let Some(mapped) = maybe_mapped {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
mapped.output_leave(&this.output);
2023-09-11 21:12:20 +02:00
mapped.output_enter(&other_output, mapped.bbox());
for (ref surface, _) in mapped.windows() {
toplevel_info_state
2023-10-25 19:40:26 +02:00
.toplevel_leave_output(surface, &this.output);
2023-09-11 21:12:20 +02:00
toplevel_info_state
.toplevel_enter_output(surface, &other_output);
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state
.toplevel_leave_workspace(surface, &this_desc.handle);
toplevel_info_state
.toplevel_enter_workspace(surface, &other_desc.handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone());
}
this_children.extend(
new_children
.into_iter()
.map(|new_child| (new_id.clone(), new_child)),
);
}
}
let other_node = other_tree.get_mut(&other_desc.node).ok()?;
let mut other_children = other_node
.children()
.into_iter()
.map(|child_id| (this_desc.node.clone(), child_id.clone()))
.collect::<Vec<_>>();
while !other_children.is_empty() {
for (parent_id, child_id) in std::mem::take(&mut other_children) {
let new_children = other_tree
.children_ids(&child_id)
.unwrap()
.cloned()
.collect::<Vec<_>>();
let child_node = other_tree
.remove_node(child_id, RemoveBehavior::OrphanChildren)
.unwrap();
let maybe_mapped = match child_node.data() {
Data::Mapped { mapped, .. } => Some(mapped.clone()),
_ => None,
};
let new_id = this_tree
.insert(child_node, InsertBehavior::UnderNode(&parent_id))
.unwrap();
if let Some(mapped) = maybe_mapped {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
2023-09-11 21:12:20 +02:00
mapped.output_leave(&other_output);
2023-10-25 19:40:26 +02:00
mapped.output_enter(&this.output, mapped.bbox());
2023-09-11 21:12:20 +02:00
for (ref surface, _) in mapped.windows() {
toplevel_info_state
.toplevel_leave_output(surface, &other_output);
toplevel_info_state
2023-10-25 19:40:26 +02:00
.toplevel_enter_output(surface, &this.output);
2023-09-11 21:12:20 +02:00
}
}
for (ref surface, _) in mapped.windows() {
toplevel_info_state
.toplevel_leave_workspace(surface, &other_desc.handle);
toplevel_info_state
.toplevel_enter_workspace(surface, &this_desc.handle);
}
*mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone());
}
other_children.extend(
new_children
.into_iter()
.map(|new_child| (new_id.clone(), new_child)),
);
}
}
}
(Some(this_surface), None) => {
if !this_surface.alive() {
return None;
}
let this_node = this_tree.get_mut(&this_desc.node).ok()?;
let Data::Mapped {
mapped: this_mapped,
..
} = this_node.data()
else {
return None;
};
let this_mapped = this_mapped.clone();
let geometry = *this_node.data().geometry();
assert!(this_mapped.is_stack());
let other_tree = other_tree.as_mut().unwrap_or(&mut this_tree);
if other_tree.get(&other_desc.node).is_err() {
return None;
}
let surfaces = other_tree
.traverse_pre_order(&other_desc.node)
.unwrap()
.flat_map(|node| match node.data() {
Data::Mapped { mapped, .. } => Some(mapped.windows().map(|(s, _)| s)),
_ => None,
})
.flatten()
.collect::<Vec<_>>();
let cleanup = other_tree
.children_ids(&other_desc.node)
.unwrap()
.cloned()
.collect::<Vec<_>>();
for node in cleanup {
let _ = other_tree.remove_node(node, RemoveBehavior::DropChildren);
}
let this_stack = this_mapped.stack_ref()?;
let this_idx = this_stack
.surfaces()
.position(|s| &s == this_surface)
.unwrap();
for (i, surface) in surfaces.into_iter().enumerate() {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
2023-09-11 21:12:20 +02:00
surface.output_leave(&other_output);
2023-10-25 19:40:26 +02:00
surface.output_enter(&this.output, surface.bbox());
2023-09-11 21:12:20 +02:00
toplevel_info_state.toplevel_leave_output(&surface, &other_output);
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_enter_output(&surface, &this.output);
2023-09-11 21:12:20 +02:00
}
if this_desc.handle != other_desc.handle {
toplevel_info_state.toplevel_leave_workspace(&surface, &other_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&surface, &this_desc.handle);
}
this_stack.add_window(surface, Some(this_idx + i));
}
2023-10-25 19:40:26 +02:00
if this.output != other_output {
this_surface.output_leave(&this.output);
toplevel_info_state.toplevel_leave_output(&this_surface, &this.output);
2023-09-11 21:12:20 +02:00
toplevel_info_state.toplevel_enter_output(&this_surface, &other_output);
}
if this_desc.handle != other_desc.handle {
toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&this_surface, &other_desc.handle);
}
this_stack.remove_window(&this_surface);
let mapped: CosmicMapped = CosmicWindow::new(
this_surface.clone(),
this_stack.loop_handle(),
this.theme.clone(),
)
.into();
2023-09-11 21:12:20 +02:00
mapped.set_tiled(true);
mapped.refresh();
2023-10-25 19:40:26 +02:00
if this.output != other_output {
2023-09-11 21:12:20 +02:00
mapped.output_enter(&other_output, mapped.bbox());
}
*mapped.tiling_node_id.lock().unwrap() = Some(other_desc.node.clone());
other_tree
.get_mut(&other_desc.node)
.unwrap()
.replace_data(Data::Mapped {
mapped,
last_geometry: geometry,
2024-02-23 20:08:57 +01:00
minimize_rect: None,
});
}
(None, Some(other_surface)) => {
if !other_surface.alive() {
return None;
}
let other_tree = other_tree.as_mut().unwrap_or(&mut this_tree);
let other_node = other_tree.get_mut(&other_desc.node).ok()?;
let Data::Mapped {
mapped: other_mapped,
..
} = other_node.data()
else {
return None;
};
let other_mapped = other_mapped.clone();
let geometry = *other_node.data().geometry();
assert!(other_mapped.is_stack());
let surfaces = this_tree
.traverse_pre_order(&this_desc.node)
.unwrap()
.flat_map(|node| match node.data() {
Data::Mapped { mapped, .. } => Some(mapped.windows().map(|(s, _)| s)),
_ => None,
})
.flatten()
.collect::<Vec<_>>();
let cleanup = this_tree
.children_ids(&this_desc.node)
.unwrap()
.cloned()
.collect::<Vec<_>>();
for node in cleanup {
let _ = this_tree.remove_node(node, RemoveBehavior::DropChildren);
}
let other_stack = other_mapped.stack_ref()?;
let other_idx = other_stack
.surfaces()
.position(|s| &s == other_surface)
.unwrap();
for (i, surface) in surfaces.into_iter().enumerate() {
2023-10-25 19:40:26 +02:00
if this.output != other_output {
surface.output_leave(&this.output);
2023-09-11 21:12:20 +02:00
surface.output_enter(&other_output, surface.bbox());
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_leave_output(&surface, &this.output);
2023-09-11 21:12:20 +02:00
toplevel_info_state.toplevel_enter_output(&surface, &other_output);
}
if this_desc.handle != other_desc.handle {
toplevel_info_state.toplevel_leave_workspace(&surface, &this_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&surface, &other_desc.handle);
}
other_stack.add_window(surface, Some(other_idx + i));
}
2023-10-25 19:40:26 +02:00
if this.output != other_output {
2023-09-11 21:12:20 +02:00
other_surface.output_leave(&other_output);
toplevel_info_state.toplevel_leave_output(&other_surface, &other_output);
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_enter_output(&other_surface, &this.output);
2023-09-11 21:12:20 +02:00
}
if this_desc.handle != other_desc.handle {
toplevel_info_state
.toplevel_leave_workspace(&other_surface, &other_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&other_surface, &this_desc.handle);
}
other_stack.remove_window(&other_surface);
let mapped: CosmicMapped = CosmicWindow::new(
other_surface.clone(),
other_stack.loop_handle(),
this.theme.clone(),
)
.into();
2023-09-11 21:12:20 +02:00
mapped.set_tiled(true);
mapped.refresh();
2023-10-25 19:40:26 +02:00
if this.output != other_output {
mapped.output_enter(&this.output, mapped.bbox());
2023-09-11 21:12:20 +02:00
}
*mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone());
this_tree
.get_mut(&this_desc.node)
.unwrap()
.replace_data(Data::Mapped {
mapped,
last_geometry: geometry,
2024-02-23 20:08:57 +01:00
minimize_rect: None,
});
}
(Some(this_surface), Some(other_surface)) => {
if !this_surface.alive() || !other_surface.alive() {
return None;
}
let this_mapped =
this_tree
.get(&this_desc.node)
.ok()
.and_then(|node| match node.data() {
Data::Mapped { mapped, .. } => Some(mapped.clone()),
_ => None,
})?;
let other_tree = other_tree.as_mut().unwrap_or(&mut this_tree);
let other_mapped =
other_tree
.get(&other_desc.node)
.ok()
.and_then(|node| match node.data() {
Data::Mapped { mapped, .. } => Some(mapped.clone()),
_ => None,
})?;
let this_stack = this_mapped.stack_ref()?;
let other_stack = other_mapped.stack_ref()?;
let this_idx = this_stack
.surfaces()
.position(|s| &s == this_surface)
.unwrap();
let other_idx = other_stack
.surfaces()
.position(|s| &s == other_surface)
.unwrap();
let this_was_active = &this_stack.active() == this_surface;
let other_was_active = &other_stack.active() == other_surface;
this_stack.add_window(other_surface.clone(), Some(this_idx));
this_stack.remove_window(&this_surface);
other_stack.add_window(this_surface.clone(), Some(other_idx));
2023-09-11 21:12:20 +02:00
2023-10-25 19:40:26 +02:00
if this.output != other_output {
toplevel_info_state.toplevel_leave_output(&this_surface, &this.output);
2023-09-11 21:12:20 +02:00
toplevel_info_state.toplevel_leave_output(&other_surface, &other_output);
toplevel_info_state.toplevel_enter_output(&this_surface, &other_output);
2023-10-25 19:40:26 +02:00
toplevel_info_state.toplevel_enter_output(&other_surface, &this.output);
2023-09-11 21:12:20 +02:00
}
if this_desc.handle != other_desc.handle {
toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle);
toplevel_info_state
.toplevel_leave_workspace(&other_surface, &other_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&this_surface, &other_desc.handle);
toplevel_info_state.toplevel_enter_workspace(&other_surface, &this_desc.handle);
}
other_stack.remove_window(&other_surface);
if this_was_active {
this_stack.set_active(&other_surface);
}
if other_was_active {
other_stack.set_active(&this_surface);
}
return other
.as_ref()
.unwrap_or(&this)
.node_desc_to_focus(other_desc);
}
}
let this_gaps = this.gaps();
let blocker = TilingLayout::update_positions(&this.output, &mut this_tree, this_gaps);
2023-10-25 19:40:26 +02:00
this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker);
let has_other_tree = other_tree.is_some();
if let Some(mut other_tree) = other_tree {
let (other_queue, gaps) = if let Some(other) = other.as_mut() {
let other_gaps = other.gaps();
(&mut other.queue, other_gaps)
} else {
(&mut this.queue, this_gaps)
};
let blocker = TilingLayout::update_positions(&other_output, &mut other_tree, gaps);
other_queue.push_tree(other_tree, ANIMATION_DURATION, blocker);
}
match (&this_desc.stack_window, &other_desc.stack_window) {
(None, None) if !has_other_tree => this.node_desc_to_focus(&this_desc),
//(None, Some(_)) => None,
_ => other
.as_ref()
.unwrap_or(&this)
.node_desc_to_focus(&other_desc),
}
}
pub fn node_desc_to_focus(&self, desc: &NodeDesc) -> Option<KeyboardFocusTarget> {
2023-10-25 19:40:26 +02:00
let tree = &self.queue.trees.back().unwrap().0;
let data = tree.get(&desc.node).ok()?.data();
match data {
Data::Mapped { mapped, .. } => Some(KeyboardFocusTarget::Element(mapped.clone())),
Data::Group { alive, .. } => Some(KeyboardFocusTarget::Group(WindowGroup {
node: desc.node.clone(),
alive: Arc::downgrade(alive),
focus_stack: tree
.children_ids(&desc.node)
.unwrap()
.cloned()
.collect::<Vec<_>>(),
})),
_ => None,
}
}
2023-10-25 19:40:26 +02:00
pub fn tree(&self) -> &Tree<Data> {
&self.queue.trees.back().unwrap().0
}
2023-10-25 19:40:26 +02:00
pub fn unmap(&mut self, window: &CosmicMapped) -> bool {
if self.unmap_window_internal(window) {
window.output_leave(&self.output);
window.set_tiled(false);
*window.tiling_node_id.lock().unwrap() = None;
true
} else {
false
}
2022-09-28 12:01:29 +02:00
}
2023-10-25 19:40:26 +02:00
pub fn unmap_as_placeholder(&mut self, window: &CosmicMapped) -> Option<NodeId> {
let node_id = window.tiling_node_id.lock().unwrap().clone()?;
let data = self
2023-10-25 19:40:26 +02:00
.queue
.trees
.back_mut()
.unwrap()
.0
.get_mut(&node_id)
.unwrap()
.data_mut();
*data = Data::Placeholder {
last_geometry: data.geometry().clone(),
2023-07-21 16:08:55 +02:00
initial_placeholder: true,
};
2023-10-25 19:40:26 +02:00
window.output_leave(&self.output);
window.set_tiled(false);
2023-10-25 19:40:26 +02:00
Some(node_id)
}
2024-02-23 17:25:40 +01:00
pub fn unmap_minimize(
&mut self,
window: &CosmicMapped,
to: Rectangle<i32, Local>,
) -> Option<MinimizedTilingState> {
let node_id = window.tiling_node_id.lock().unwrap().clone()?;
let state = {
let tree = &self.queue.trees.back().unwrap().0;
tree.get(&node_id).unwrap().parent().and_then(|parent_id| {
let parent = tree.get(&parent_id).unwrap();
let idx = parent
.children()
.iter()
.position(|id| id == &node_id)
.unwrap();
if let Data::Group {
orientation, sizes, ..
} = parent.data()
{
if sizes.len() == 2 {
// this group will be flattened
Some(MinimizedTilingState {
parent: None,
sibling: parent.children().iter().cloned().find(|id| id != &node_id),
orientation: *orientation,
idx,
sizes: sizes.clone(),
})
} else {
Some(MinimizedTilingState {
parent: Some(parent_id.clone()),
sibling: None,
orientation: *orientation,
idx,
sizes: sizes.clone(),
})
}
2024-02-23 17:25:40 +01:00
} else {
None
}
})
};
if self.unmap_window_internal(window) {
let tree = &mut self
.queue
.trees
.get_mut(self.queue.trees.len() - 2)
.unwrap()
.0;
2024-02-23 20:08:57 +01:00
if let Data::Mapped {
minimize_rect: minimize_to,
..
} = tree.get_mut(&node_id).unwrap().data_mut()
{
2024-02-23 17:25:40 +01:00
*minimize_to = Some(to);
}
window.set_minimized(true);
}
state
}
2023-10-25 19:40:26 +02:00
fn unmap_window_internal(&mut self, mapped: &CosmicMapped) -> bool {
2023-05-12 20:01:37 +02:00
let tiling_node_id = mapped.tiling_node_id.lock().unwrap().as_ref().cloned();
let gaps = self.gaps();
2023-05-12 20:01:37 +02:00
if let Some(node_id) = tiling_node_id {
2023-10-25 19:40:26 +02:00
if self
.queue
.trees
.back()
.unwrap()
.0
.get(&node_id)
.map(|node| node.data().is_mapped(Some(mapped)))
.unwrap_or(false)
{
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2023-05-12 20:01:37 +02:00
2023-07-21 16:08:55 +02:00
TilingLayout::unmap_internal(&mut tree, &node_id);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return true;
2023-07-21 16:08:55 +02:00
}
}
2023-10-25 19:40:26 +02:00
false
2023-07-21 16:08:55 +02:00
}
2022-09-28 12:01:29 +02:00
2023-07-21 16:08:55 +02:00
fn unmap_internal(tree: &mut Tree<Data>, node: &NodeId) {
let parent_id = tree.get(node).ok().and_then(|node| node.parent()).cloned();
let position = parent_id.as_ref().and_then(|parent_id| {
tree.children_ids(&parent_id)
.unwrap()
.position(|id| id == node)
});
let parent_parent_id = parent_id.as_ref().and_then(|parent_id| {
tree.get(parent_id)
.ok()
.and_then(|node| node.parent())
.cloned()
});
2022-09-28 12:01:29 +02:00
2023-07-21 16:08:55 +02:00
// remove self
trace!(?node, "Removing node.");
let _ = tree.remove_node(node.clone(), RemoveBehavior::DropChildren);
2022-09-28 12:01:29 +02:00
2023-07-21 16:08:55 +02:00
// fixup parent node
match parent_id {
Some(id) => {
let position = position.unwrap();
let group = tree.get_mut(&id).unwrap().data_mut();
assert!(group.is_group());
if group.len() > 2 {
group.remove_window(position);
} else {
trace!("Removing Group");
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();
2022-09-28 12:01:29 +02:00
}
}
}
2023-07-21 16:08:55 +02:00
None => {} // root
2022-09-28 12:01:29 +02:00
}
}
2023-05-12 20:01:37 +02:00
// TODO: Move would needs this to be accurate during animations
2023-10-25 19:40:26 +02:00
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Local>> {
2022-09-28 12:01:29 +02:00
if let Some(id) = elem.tiling_node_id.lock().unwrap().as_ref() {
2023-10-25 19:40:26 +02:00
let node = self.queue.trees.back().unwrap().0.get(id).ok()?;
let data = node.data();
assert!(data.is_mapped(Some(elem)));
let geo = *data.geometry();
Some(geo)
} else {
None
2022-09-28 12:01:29 +02:00
}
}
2023-05-30 21:02:19 +02:00
pub fn move_current_node<'a>(
2022-10-27 16:11:54 +02:00
&mut self,
direction: Direction,
seat: &Seat<State>,
) -> MoveResult {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2022-10-27 16:11:54 +02:00
2023-07-31 17:36:32 +02:00
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return MoveResult::None;
2023-07-31 17:36:32 +02:00
};
let Some((node_id, data)) = TilingLayout::currently_focused_node(&mut tree, target) else {
return MoveResult::None;
};
// stacks may handle movement internally
if let FocusedNodeData::Window(window) = data.clone() {
match window.handle_move(direction) {
StackMoveResult::Handled => return MoveResult::Done,
StackMoveResult::MoveOut(surface, loop_handle) => {
let mapped: CosmicMapped =
CosmicWindow::new(surface, loop_handle, self.theme.clone()).into();
2023-10-25 19:40:26 +02:00
mapped.output_enter(&self.output, mapped.bbox());
let orientation = match direction {
Direction::Left | Direction::Right => Orientation::Vertical,
Direction::Up | Direction::Down => Orientation::Horizontal,
};
let new_node = Node::new(Data::Mapped {
mapped: mapped.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
2024-02-23 20:08:57 +01:00
minimize_rect: None,
});
let new_id = tree.insert(new_node, InsertBehavior::AsRoot).unwrap();
TilingLayout::new_group(&mut tree, &node_id, &new_id, orientation).unwrap();
tree.make_nth_sibling(
&new_id,
match direction {
Direction::Left | Direction::Up => 0,
Direction::Right | Direction::Down => 1,
},
)
.unwrap();
*mapped.tiling_node_id.lock().unwrap() = Some(new_id);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return MoveResult::ShiftFocus(mapped.into());
}
StackMoveResult::Default => {} // continue normally
}
}
2022-10-27 16:11:54 +02:00
let mut child_id = node_id.clone();
// Without a parent to start with, just return
let Some(og_parent) = tree.get(&node_id).unwrap().parent().cloned() else {
return match data {
FocusedNodeData::Window(window) => MoveResult::MoveFurther(window.into()),
2023-07-31 17:36:32 +02:00
FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther(
WindowGroup {
node: node_id,
alive,
focus_stack,
}
.into(),
),
};
};
2022-10-27 16:11:54 +02:00
let og_idx = tree
.children_ids(&og_parent)
.unwrap()
.position(|id| id == &child_id)
.unwrap();
let mut maybe_parent = Some(og_parent.clone());
while let Some(parent) = maybe_parent {
let parent_data = tree.get(&parent).unwrap().data();
let orientation = parent_data.orientation();
let len = parent_data.len();
// which child are we?
let idx = tree
.children_ids(&parent)
.unwrap()
.position(|id| id == &child_id)
.unwrap();
// if the orientation does not match..
2022-10-27 16:11:54 +02:00
if matches!(
(orientation, direction),
(Orientation::Horizontal, Direction::Right)
| (Orientation::Horizontal, Direction::Left)
| (Orientation::Vertical, Direction::Up)
| (Orientation::Vertical, Direction::Down)
) {
// ...create a new group with our parent (cleanup will remove any one-child-groups afterwards)
2022-10-27 16:11:54 +02:00
TilingLayout::new_group(
2023-05-12 20:01:37 +02:00
&mut tree,
2022-10-27 16:11:54 +02:00
&parent,
&node_id,
match direction {
Direction::Left | Direction::Right => Orientation::Vertical,
Direction::Up | Direction::Down => Orientation::Horizontal,
},
)
.unwrap();
tree.make_nth_sibling(
&node_id,
if direction == Direction::Left || direction == Direction::Up {
0
} else {
1
},
)
.unwrap();
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.remove_window(og_idx);
2023-05-12 20:01:37 +02:00
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return MoveResult::Done;
2022-10-27 16:11:54 +02:00
}
// now if the orientation matches
// if we are not already in this group, we just move into it (up)
if child_id != node_id {
tree.move_node(&node_id, MoveBehavior::ToParent(&parent))
.unwrap();
tree.make_nth_sibling(
&node_id,
if direction == Direction::Left || direction == Direction::Up {
idx
} else {
idx + 1
},
)
.unwrap();
tree.get_mut(&parent).unwrap().data_mut().add_window(idx);
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.remove_window(og_idx);
2023-05-12 20:01:37 +02:00
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return MoveResult::Done;
2022-10-27 16:11:54 +02:00
}
// we can maybe move inside the group, if we don't run out of elements
if let Some(next_idx) = match (orientation, direction) {
(Orientation::Horizontal, Direction::Down)
| (Orientation::Vertical, Direction::Right)
if idx < (len - 1) =>
{
Some(idx + 1)
}
(Orientation::Horizontal, Direction::Up)
| (Orientation::Vertical, Direction::Left)
if idx > 0 =>
{
Some(idx - 1)
}
_ => None,
} {
// if we can, we need to check the next element and move "into" it
2022-10-27 16:11:54 +02:00
let next_child_id = tree
.children_ids(&parent)
.unwrap()
.nth(next_idx)
.unwrap()
.clone();
let result = if tree.get(&next_child_id).unwrap().data().is_stack()
&& tree.get(&node_id).unwrap().data().is_mapped(None)
&& !tree.get(&node_id).unwrap().data().is_stack()
&& len == 2
{
let node = tree
.remove_node(node_id, RemoveBehavior::DropChildren)
.unwrap();
let stack_data = tree.get_mut(&next_child_id).unwrap().data_mut();
let mut mapped = match stack_data {
Data::Mapped { mapped, .. } => mapped.clone(),
_ => unreachable!(),
};
let stack = mapped.stack_ref_mut().unwrap();
let surface = match node.data() {
Data::Mapped { mapped, .. } => mapped.active_window(),
_ => unreachable!(),
};
stack.add_window(
surface,
match direction {
Direction::Right => Some(0),
_ => None,
},
);
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.remove_window(og_idx);
MoveResult::ShiftFocus(mapped.into())
} else if tree.get(&next_child_id).unwrap().data().is_group() && len == 2 {
2022-10-27 16:11:54 +02:00
// if it is a group, we want to move into the group
tree.move_node(&node_id, MoveBehavior::ToParent(&next_child_id))
.unwrap();
let group_orientation = tree.get(&next_child_id).unwrap().data().orientation();
match (group_orientation, direction) {
(Orientation::Horizontal, Direction::Down)
| (Orientation::Vertical, Direction::Right) => {
tree.make_first_sibling(&node_id).unwrap();
tree.get_mut(&next_child_id)
.unwrap()
.data_mut()
.add_window(0);
}
(Orientation::Horizontal, Direction::Up)
| (Orientation::Vertical, Direction::Left) => {
tree.make_last_sibling(&node_id).unwrap();
let group = tree.get_mut(&next_child_id).unwrap().data_mut();
group.add_window(group.len());
}
_ => {
// we want the middle
let group_len = tree.get(&next_child_id).unwrap().data().len();
if group_len % 2 == 0 {
tree.make_nth_sibling(&node_id, group_len / 2).unwrap();
tree.get_mut(&next_child_id)
.unwrap()
.data_mut()
.add_window(group_len / 2);
} else {
// we move again by making a new fork
let old_id = tree
.children_ids(&next_child_id)
.unwrap()
.skip(group_len / 2)
.next()
.unwrap()
.clone();
TilingLayout::new_group(
2023-05-12 20:01:37 +02:00
&mut tree,
2022-10-27 16:11:54 +02:00
&old_id,
&node_id,
!group_orientation,
)
.unwrap();
tree.make_nth_sibling(
&node_id,
if direction == Direction::Left || direction == Direction::Up {
1
} else {
0
2022-10-27 16:11:54 +02:00
},
)
.unwrap();
}
}
};
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.remove_window(og_idx);
MoveResult::Done
2022-10-27 16:11:54 +02:00
} else if len == 2 && child_id == node_id {
// if we are just us two in the group, lets swap
tree.make_nth_sibling(&node_id, next_idx).unwrap();
// also swap sizes
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.swap_windows(idx, next_idx);
MoveResult::Done
2022-10-27 16:11:54 +02:00
} else {
// else we make a new fork
2023-05-12 20:01:37 +02:00
TilingLayout::new_group(&mut tree, &next_child_id, &node_id, orientation)
.unwrap();
2022-10-27 16:11:54 +02:00
tree.make_nth_sibling(
&node_id,
if direction == Direction::Left || direction == Direction::Up {
1
} else {
0
},
)
.unwrap();
tree.get_mut(&og_parent)
.unwrap()
.data_mut()
.remove_window(og_idx);
MoveResult::Done
};
2023-05-12 20:01:37 +02:00
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return result;
2022-10-27 16:11:54 +02:00
}
// We have reached the end of our parent group, try to move out even higher.
maybe_parent = tree.get(&parent).unwrap().parent().cloned();
child_id = parent.clone();
}
2023-05-30 21:02:19 +02:00
match data {
FocusedNodeData::Window(window) => MoveResult::MoveFurther(window.into()),
FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther(
2023-05-30 21:02:19 +02:00
WindowGroup {
node: node_id,
alive,
focus_stack,
}
.into(),
),
2022-10-27 16:11:54 +02:00
}
}
2022-09-28 12:01:29 +02:00
pub fn next_focus<'a>(
&mut self,
direction: FocusDirection,
seat: &Seat<State>,
2022-09-28 12:01:29 +02:00
focus_stack: impl Iterator<Item = &'a CosmicMapped> + 'a,
swap_desc: Option<NodeDesc>,
2022-11-10 18:42:11 +01:00
) -> FocusResult {
2023-10-25 19:40:26 +02:00
let tree = &self.queue.trees.back().unwrap().0;
2022-09-28 12:01:29 +02:00
2023-07-31 17:36:32 +02:00
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return FocusResult::None;
};
let Some(focused) = TilingLayout::currently_focused_node(tree, target).or_else(|| {
TilingLayout::last_active_window(tree, focus_stack)
.map(|(id, mapped)| (id, FocusedNodeData::Window(mapped)))
}) else {
2023-07-31 17:36:32 +02:00
return FocusResult::None;
};
let (last_node_id, data) = focused;
// stacks may handle focus internally
if let FocusedNodeData::Window(window) = data.clone() {
if window.handle_focus(
direction,
swap_desc.clone().filter(|desc| desc.node == last_node_id),
) {
return FocusResult::Handled;
2023-05-30 21:02:19 +02:00
}
}
2023-05-30 21:02:19 +02:00
if direction == FocusDirection::In {
if swap_desc
.as_ref()
.map(|desc| desc.node == last_node_id)
.unwrap_or(false)
{
return FocusResult::Handled; // abort
}
if let FocusedNodeData::Group(mut stack, _) = data.clone() {
let maybe_id = stack.pop().unwrap();
let id = if tree
.children_ids(&last_node_id)
.unwrap()
.any(|id| id == &maybe_id)
{
Some(maybe_id)
} else {
tree.children_ids(&last_node_id).unwrap().next().cloned()
};
2023-05-30 21:02:19 +02:00
if let Some(id) = id {
return match tree.get(&id).unwrap().data() {
Data::Mapped { mapped, .. } => {
if mapped.is_stack() {
mapped.stack_ref().unwrap().focus_stack();
2023-06-14 17:29:08 +02:00
}
FocusResult::Some(mapped.clone().into())
}
Data::Group { alive, .. } => FocusResult::Some(
WindowGroup {
node: id,
alive: Arc::downgrade(alive),
focus_stack: stack,
}
.into(),
),
Data::Placeholder { .. } => FocusResult::None,
};
2023-05-30 21:02:19 +02:00
}
2022-09-28 12:01:29 +02:00
}
}
2022-09-28 12:01:29 +02:00
let mut node_id = last_node_id.clone();
while let Some(group) = tree.get(&node_id).unwrap().parent() {
let child = node_id.clone();
let group_data = tree.get(&group).unwrap().data();
let main_orientation = group_data.orientation();
assert!(group_data.is_group());
if direction == FocusDirection::Out {
if swap_desc
.as_ref()
.map(|desc| {
tree.traverse_pre_order_ids(group)
.unwrap()
.any(|node| node == desc.node)
})
.unwrap_or(false)
{
return FocusResult::Handled; // abort
}
return FocusResult::Some(
WindowGroup {
node: group.clone(),
alive: match group_data {
&Data::Group { ref alive, .. } => Arc::downgrade(alive),
_ => unreachable!(),
},
focus_stack: match data {
FocusedNodeData::Group(mut stack, _) => {
stack.push(child);
stack
}
_ => vec![child],
},
}
.into(),
);
}
2022-09-28 12:01:29 +02:00
// which child are we?
let idx = tree
.children_ids(&group)
.unwrap()
.position(|id| id == &child)
.unwrap();
let len = group_data.len();
2022-09-28 12:01:29 +02:00
let focus_subtree = match (main_orientation, direction) {
(Orientation::Horizontal, FocusDirection::Down)
| (Orientation::Vertical, FocusDirection::Right)
if idx < (len - 1) =>
{
tree.children_ids(&group).unwrap().skip(idx + 1).next()
}
(Orientation::Horizontal, FocusDirection::Up)
| (Orientation::Vertical, FocusDirection::Left)
if idx > 0 =>
{
tree.children_ids(&group).unwrap().skip(idx - 1).next()
}
_ => None, // continue iterating
};
2022-09-28 12:01:29 +02:00
if focus_subtree.is_some() {
let mut node_id = focus_subtree;
while node_id.is_some() {
if let Some(desc) = swap_desc.as_ref() {
if let Some(replacement_id) = tree
.ancestor_ids(node_id.unwrap())
.unwrap()
.find(|anchestor| *anchestor == &desc.node)
.or_else(|| {
tree.children_ids(node_id.unwrap())
.unwrap()
.find(|child| *child == &desc.node)
})
{
return match tree.get(replacement_id).unwrap().data() {
Data::Group { alive, .. } => {
FocusResult::Some(KeyboardFocusTarget::Group(WindowGroup {
node: replacement_id.clone(),
alive: Arc::downgrade(&alive),
focus_stack: tree
.children_ids(replacement_id)
.unwrap()
.cloned()
.collect(),
}))
}
Data::Mapped { mapped, .. } => {
if mapped.is_stack()
&& desc.stack_window.is_none()
&& replacement_id == &desc.node
{
mapped.stack_ref().unwrap().focus_stack();
}
FocusResult::Some(KeyboardFocusTarget::Element(mapped.clone()))
}
_ => unreachable!(),
};
}
}
match tree.get(node_id.unwrap()).unwrap().data() {
Data::Group { orientation, .. } if orientation == &main_orientation => {
// if the group is layed out in the direction we care about,
// we can just use the first or last element (depending on the direction)
match direction {
FocusDirection::Down | FocusDirection::Right => {
node_id = tree
.children_ids(node_id.as_ref().unwrap())
.unwrap()
.next();
}
FocusDirection::Up | FocusDirection::Left => {
node_id = tree
.children_ids(node_id.as_ref().unwrap())
.unwrap()
.last();
}
_ => unreachable!(),
}
}
Data::Group { .. } => {
let center = {
let geo = tree.get(&last_node_id).unwrap().data().geometry();
let mut point = geo.loc;
2022-09-28 12:01:29 +02:00
match direction {
FocusDirection::Down => {
point += Point::from((geo.size.w / 2 - 1, geo.size.h))
2022-09-28 12:01:29 +02:00
}
FocusDirection::Up => point.x += geo.size.w / 2 - 1,
FocusDirection::Left => point.y += geo.size.h / 2 - 1,
FocusDirection::Right => {
point += Point::from((geo.size.w, geo.size.h / 2 - 1))
2022-09-28 12:01:29 +02:00
}
_ => unreachable!(),
};
point.to_f64()
};
2022-09-28 12:01:29 +02:00
let distance = |candidate: &&NodeId| -> f64 {
let geo = tree.get(candidate).unwrap().data().geometry();
let mut point = geo.loc;
match direction {
FocusDirection::Up => {
point += Point::from((geo.size.w / 2, geo.size.h))
}
FocusDirection::Down => point.x += geo.size.w,
FocusDirection::Right => point.y += geo.size.h / 2,
FocusDirection::Left => {
point += Point::from((geo.size.w, geo.size.h / 2))
}
_ => unreachable!(),
};
let point = point.to_f64();
((point.x - center.x).powi(2) + (point.y - center.y).powi(2)).sqrt()
};
node_id = tree
.children_ids(node_id.as_ref().unwrap())
.unwrap()
.min_by(|node1, node2| {
distance(node1).abs().total_cmp(&distance(node2).abs())
});
}
Data::Mapped { mapped, .. } => {
if mapped.is_stack()
&& swap_desc
.as_ref()
.map(|desc| {
desc.stack_window.is_none()
&& &desc.node == node_id.unwrap()
})
.unwrap_or(false)
{
mapped.stack_ref().unwrap().focus_stack();
}
return FocusResult::Some(mapped.clone().into());
}
Data::Placeholder { .. } => return FocusResult::None,
}
}
} else {
node_id = group.clone();
}
}
2022-11-10 18:42:11 +01:00
FocusResult::None
}
pub fn update_orientation<'a>(
&mut self,
new_orientation: Option<Orientation>,
seat: &Seat<State>,
) {
let gaps = self.gaps();
2023-07-31 17:36:32 +02:00
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return;
};
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
if let Some((last_active, _)) = TilingLayout::currently_focused_node(&tree, target) {
2022-09-28 12:01:29 +02:00
if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() {
if let &mut Data::Group {
ref mut orientation,
2022-09-28 12:01:29 +02:00
ref mut sizes,
ref last_geometry,
..
2022-09-28 12:01:29 +02:00
} = tree.get_mut(&group).unwrap().data_mut()
{
2022-09-28 12:01:29 +02:00
let previous_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let new_orientation = new_orientation.unwrap_or(!*orientation);
2022-09-28 12:01:29 +02:00
let new_length = match new_orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
sizes.iter_mut().for_each(|len| {
*len = (((*len as f64) / (previous_length as f64)) * (new_length as f64))
.round() as i32;
});
let sum: i32 = sizes.iter().sum();
if sum < new_length {
*sizes.last_mut().unwrap() += new_length - sum;
}
2022-07-05 18:46:38 +02:00
2022-09-28 12:01:29 +02:00
*orientation = new_orientation;
2023-05-12 20:01:37 +02:00
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
2022-09-28 12:01:29 +02:00
}
2022-07-05 18:46:38 +02:00
}
2022-03-24 20:32:31 +01:00
}
}
2022-03-29 18:03:21 +02:00
pub fn toggle_stacking(&mut self, mapped: &CosmicMapped) -> Option<KeyboardFocusTarget> {
let gaps = self.gaps();
let Some(node_id) = mapped.tiling_node_id.lock().unwrap().clone() else {
return None;
};
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
if tree.get(&node_id).is_err() {
return None;
}
let result = if mapped.is_window() {
// if it is just a window
match tree.get_mut(&node_id).unwrap().data_mut() {
Data::Mapped { mapped, .. } => {
mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone());
2024-01-15 09:35:38 +00:00
KeyboardFocusTarget::Element(mapped.clone())
}
_ => unreachable!(),
2024-01-15 09:35:38 +00:00
}
} else {
// if we have a stack
let mut surfaces = mapped.windows().map(|(s, _)| s);
let first = surfaces.next().expect("Stack without a window?");
let handle = match tree.get_mut(&node_id).unwrap().data_mut() {
Data::Mapped { mapped, .. } => {
let handle = mapped.loop_handle();
mapped.convert_to_surface(
first,
(&self.output, mapped.bbox()),
self.theme.clone(),
);
handle
}
_ => unreachable!(),
};
// map the rest
2024-01-15 09:35:38 +00:00
let mut current_node = node_id.clone();
for other in surfaces {
other.try_force_undecorated(false);
other.set_tiled(false);
let window = CosmicMapped::from(CosmicWindow::new(
other,
handle.clone(),
self.theme.clone(),
));
window.output_enter(&self.output, window.bbox());
{
let layer_map = layer_map_for_output(&self.output);
window.set_bounds(layer_map.non_exclusive_zone().size);
}
TilingLayout::map_to_tree(
&mut tree,
window.clone(),
&self.output,
Some(current_node),
None,
None,
);
let node = window.tiling_node_id.lock().unwrap().clone().unwrap();
2024-01-15 09:35:38 +00:00
current_node = node;
}
2024-01-15 09:35:38 +00:00
let node = tree.get(&node_id).unwrap();
let node_id = if current_node != node_id {
node.parent().cloned().unwrap_or(node_id)
} else {
node_id
};
match tree.get(&node_id).unwrap().data() {
Data::Group { alive, .. } => KeyboardFocusTarget::Group(WindowGroup {
node: node_id.clone(),
alive: Arc::downgrade(alive),
focus_stack: tree
2024-01-15 09:35:38 +00:00
.children_ids(&node_id)
.unwrap()
.cloned()
.collect::<Vec<_>>(),
2024-01-15 09:35:38 +00:00
}),
Data::Mapped { mapped, .. } => KeyboardFocusTarget::Element(mapped.clone()),
_ => unreachable!(),
}
};
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
Some(result)
}
pub fn toggle_stacking_focused<'a>(
&mut self,
seat: &Seat<State>,
mut focus_stack: FocusStackMut,
) -> Option<KeyboardFocusTarget> {
let gaps = self.gaps();
2023-07-31 17:36:32 +02:00
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return None;
2023-07-31 17:36:32 +02:00
};
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2023-06-05 17:52:47 +02:00
if let Some((last_active, last_active_data)) =
2023-10-25 19:40:26 +02:00
TilingLayout::currently_focused_node(&tree, target)
2023-06-05 17:52:47 +02:00
{
match last_active_data {
FocusedNodeData::Window(mapped) => {
let res = self.toggle_stacking(&mapped);
focus_stack.append(&mapped);
return res;
2023-06-05 17:52:47 +02:00
}
FocusedNodeData::Group(_, _) => {
let mut handle = None;
let surfaces = tree
.traverse_pre_order(&last_active)
.unwrap()
.flat_map(|node| match node.data() {
Data::Mapped { mapped, .. } => {
if handle.is_none() {
handle = Some(mapped.loop_handle());
}
Some(mapped.windows().map(|(s, _)| s))
}
_ => None,
2023-06-05 17:52:47 +02:00
})
.flatten()
.collect::<Vec<_>>();
if surfaces.is_empty() {
return None;
2023-06-05 17:52:47 +02:00
}
let handle = handle.unwrap();
let stack = CosmicStack::new(surfaces.into_iter(), handle, self.theme.clone());
2023-06-05 17:52:47 +02:00
for child in tree
.children_ids(&last_active)
.unwrap()
.cloned()
.collect::<Vec<_>>()
.into_iter()
{
tree.remove_node(child, RemoveBehavior::DropChildren)
.unwrap();
}
let data = tree.get_mut(&last_active).unwrap().data_mut();
let geo = *data.geometry();
2023-10-25 19:40:26 +02:00
stack.output_enter(&self.output, stack.bbox());
2023-06-05 17:52:47 +02:00
stack.set_activate(true);
stack.active().send_configure();
stack.refresh();
let mapped = CosmicMapped::from(stack);
*mapped.last_geometry.lock().unwrap() = Some(geo);
*mapped.tiling_node_id.lock().unwrap() = Some(last_active);
focus_stack.append(&mapped);
*data = Data::Mapped {
mapped: mapped.clone(),
2023-06-05 17:52:47 +02:00
last_geometry: geo,
2024-02-23 20:08:57 +01:00
minimize_rect: None,
2023-06-05 17:52:47 +02:00
};
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
return Some(KeyboardFocusTarget::Element(mapped));
2023-06-05 17:52:47 +02:00
}
}
}
None
2023-06-05 17:52:47 +02:00
}
2023-10-25 19:40:26 +02:00
pub fn recalculate(&mut self) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
2023-05-30 16:32:58 +02:00
}
2023-05-12 20:01:37 +02:00
pub fn refresh(&mut self) {
#[cfg(feature = "debug")]
puffin::profile_function!();
2022-09-28 12:01:29 +02:00
let dead_windows = self
.mapped()
.map(|(w, _)| w.clone())
2022-09-28 12:01:29 +02:00
.filter(|w| !w.alive())
.collect::<Vec<_>>();
for dead_window in dead_windows.iter() {
2023-07-21 16:08:55 +02:00
self.unmap_window_internal(dead_window);
}
2023-05-12 20:01:37 +02:00
for (mapped, _) in self.mapped() {
2023-05-12 20:01:37 +02:00
mapped.refresh();
}
}
pub fn animations_going(&self) -> bool {
2023-10-25 19:40:26 +02:00
self.queue.animation_start.is_some()
2023-05-12 20:01:37 +02:00
}
2023-07-05 23:48:10 +02:00
pub fn update_animation_state(&mut self) -> HashMap<ClientId, Client> {
let mut clients = HashMap::new();
2023-05-12 20:01:37 +02:00
for blocker in self.pending_blockers.drain(..) {
2023-07-05 23:48:10 +02:00
clients.extend(blocker.signal_ready());
2023-05-12 20:01:37 +02:00
}
2023-07-05 23:48:10 +02:00
2023-10-25 19:40:26 +02:00
if let Some(start) = self.queue.animation_start {
let duration_since_start = Instant::now().duration_since(start);
if duration_since_start
>= self
.queue
.trees
.get(1)
.expect("Animation going without second tree?")
.1
{
let _ = self.queue.animation_start.take();
let _ = self.queue.trees.pop_front();
2024-02-23 20:08:57 +01:00
let front = self.queue.trees.front_mut().unwrap();
if let Some(root_id) = front.0.root_node_id() {
for node in front
.0
.traverse_pre_order_ids(root_id)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
if let Data::Mapped { minimize_rect, .. } =
front.0.get_mut(&node).unwrap().data_mut()
{
minimize_rect.take();
}
}
}
let _ = front.2.take();
2023-10-25 19:40:26 +02:00
} else {
return clients;
2023-05-12 20:01:37 +02:00
}
2023-10-25 19:40:26 +02:00
}
let ready_trees = self
.queue
.trees
.iter()
.skip(1)
.take_while(|(_, _, blocker)| {
blocker
.as_ref()
.map(|blocker| blocker.is_ready() && blocker.is_signaled())
.unwrap_or(true)
})
.count();
2023-10-25 19:40:26 +02:00
// merge
let other_duration = if ready_trees > 1 {
self.queue
.trees
2023-10-25 19:40:26 +02:00
.drain(1..ready_trees)
.fold(None, |res, (_, duration, blocker)| {
if let Some(blocker) = blocker {
clients.extend(blocker.signal_ready());
}
Some(
res.map(|old_duration: Duration| old_duration.max(duration))
.unwrap_or(duration),
)
})
2023-10-25 19:40:26 +02:00
} else {
None
};
2023-10-25 19:40:26 +02:00
// start
if ready_trees > 0 {
let (_, duration, blocker) = self.queue.trees.get_mut(1).unwrap();
*duration = other_duration
.map(|other| other.max(*duration))
.unwrap_or(*duration);
if let Some(blocker) = blocker {
clients.extend(blocker.signal_ready());
2022-10-27 16:11:54 +02:00
}
2023-10-25 19:40:26 +02:00
self.queue.animation_start = Some(Instant::now());
2022-10-27 16:11:54 +02:00
}
2023-07-05 23:48:10 +02:00
clients
2022-03-30 22:11:29 +02:00
}
pub fn possible_resizes(tree: &Tree<Data>, mut node_id: NodeId) -> ResizeEdge {
let mut edges = ResizeEdge::empty();
while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() {
let orientation = tree.get(&group_id).unwrap().data().orientation();
let node_idx = tree
.children_ids(&group_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
let total = tree.children_ids(&group_id).unwrap().count();
if orientation == Orientation::Vertical {
if node_idx > 0 {
edges.insert(ResizeEdge::LEFT);
}
if node_idx < total - 1 {
edges.insert(ResizeEdge::RIGHT);
}
} else {
if node_idx > 0 {
edges.insert(ResizeEdge::TOP);
}
if node_idx < total - 1 {
edges.insert(ResizeEdge::BOTTOM);
}
}
node_id = group_id;
}
edges
}
2023-12-07 19:53:41 +00:00
pub fn menu_resize(
&self,
mut node_id: NodeId,
edge: ResizeEdge,
) -> Option<(NodeId, usize, Orientation)> {
let tree = self.tree();
while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() {
let orientation = tree.get(&group_id).unwrap().data().orientation();
let node_idx = tree
.children_ids(&group_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
let total = tree.children_ids(&group_id).unwrap().count();
if orientation == Orientation::Vertical {
if node_idx > 0 && edge.contains(ResizeEdge::LEFT) {
return Some((group_id, node_idx - 1, orientation));
}
if node_idx < total - 1 && edge.contains(ResizeEdge::RIGHT) {
return Some((group_id, node_idx, orientation));
}
} else {
if node_idx > 0 && edge.contains(ResizeEdge::TOP) {
return Some((group_id, node_idx - 1, orientation));
}
if node_idx < total - 1 && edge.contains(ResizeEdge::BOTTOM) {
return Some((group_id, node_idx, orientation));
}
}
node_id = group_id;
}
None
}
pub fn resize(
&mut self,
focused: &KeyboardFocusTarget,
direction: ResizeDirection,
edges: ResizeEdge,
amount: i32,
) -> bool {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
let Some(root_id) = tree.root_node_id() else {
return false;
};
let Some(mut node_id) = (match TilingLayout::currently_focused_node(&tree, focused.clone())
{
Some((_id, FocusedNodeData::Window(mapped))) =>
// we need to make sure the id belongs to this tree..
{
tree.traverse_pre_order_ids(root_id)
.unwrap()
.find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped)))
}
Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the workspace handle was already matched, so the id is to be trusted
_ => None,
}) else {
return false;
};
while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() {
let orientation = tree.get(&group_id).unwrap().data().orientation();
if !((orientation == Orientation::Vertical
&& (edges.contains(ResizeEdge::LEFT) || edges.contains(ResizeEdge::RIGHT)))
|| (orientation == Orientation::Horizontal
&& (edges.contains(ResizeEdge::TOP) || edges.contains(ResizeEdge::BOTTOM))))
{
node_id = group_id.clone();
continue;
}
let node_idx = tree
.children_ids(&group_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
let Some(other_idx) = (match edges {
x if x.intersects(ResizeEdge::TOP_LEFT) => node_idx.checked_sub(1),
2023-07-31 17:36:32 +02:00
_ => {
if tree.children_ids(&group_id).unwrap().count() - 1 > node_idx {
Some(node_idx + 1)
} else {
None
}
}
}) else {
node_id = group_id.clone();
continue;
};
let data = tree.get_mut(&group_id).unwrap().data_mut();
match data {
Data::Group { sizes, .. } => {
let (shrink_idx, grow_idx) = if direction == ResizeDirection::Inwards {
(node_idx, other_idx)
} else {
(other_idx, node_idx)
};
if sizes[shrink_idx] + sizes[grow_idx]
< match orientation {
Orientation::Vertical => 720,
Orientation::Horizontal => 480,
}
{
return true;
};
let old_size = sizes[shrink_idx];
sizes[shrink_idx] =
(old_size - amount).max(if orientation == Orientation::Vertical {
360
} else {
240
});
let diff = old_size - sizes[shrink_idx];
sizes[grow_idx] += diff;
}
_ => unreachable!(),
}
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, None, blocker);
return true;
}
true
}
2023-10-25 19:40:26 +02:00
pub fn stacking_indicator(&self) -> Option<Rectangle<i32, Local>> {
2023-07-24 19:31:31 +02:00
if let Some(TargetZone::WindowStack(_, geo)) =
self.last_overview_hover.as_ref().map(|(_, zone)| zone)
{
Some(*geo)
} else {
None
}
}
2023-10-25 19:40:26 +02:00
pub fn cleanup_drag(&mut self) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
if let Some(root) = tree.root_node_id() {
for id in tree
.traverse_pre_order_ids(root)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
match tree.get_mut(&id).map(|node| node.data_mut()) {
Ok(Data::Placeholder { .. }) => TilingLayout::unmap_internal(&mut tree, &id),
Ok(Data::Group { pill_indicator, .. }) if pill_indicator.is_some() => {
pill_indicator.take();
}
_ => {}
}
}
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
}
}
2023-10-25 19:40:26 +02:00
pub fn drop_window(&mut self, window: CosmicMapped) -> (CosmicMapped, Point<i32, Local>) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
2023-10-25 19:40:26 +02:00
window.output_enter(&self.output, window.bbox());
{
let layer_map = layer_map_for_output(&self.output);
window.set_bounds(layer_map.non_exclusive_zone().size);
}
2023-07-24 21:14:34 +02:00
let mapped = match self.last_overview_hover.as_ref().map(|x| &x.1) {
Some(TargetZone::GroupEdge(group_id, direction)) if tree.get(&group_id).is_ok() => {
let new_id = tree
.insert(
Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
2024-02-23 20:08:57 +01:00
minimize_rect: None,
2023-07-24 21:14:34 +02:00
}),
InsertBehavior::UnderNode(group_id),
)
.unwrap();
let orientation = if matches!(direction, Direction::Left | Direction::Right) {
Orientation::Vertical
} else {
Orientation::Horizontal
};
if tree.get(group_id).unwrap().data().orientation() != orientation {
TilingLayout::new_group(&mut tree, &group_id, &new_id, orientation).unwrap();
} else {
let data = tree.get_mut(group_id).unwrap().data_mut();
let len = data.len();
data.add_window(if matches!(direction, Direction::Left | Direction::Up) {
0
} else {
len
});
}
if matches!(direction, Direction::Left | Direction::Up) {
tree.make_first_sibling(&new_id).unwrap();
} else {
tree.make_last_sibling(&new_id).unwrap();
}
*window.tiling_node_id.lock().unwrap() = Some(new_id);
window
}
Some(TargetZone::GroupInterior(group_id, idx)) if tree.get(&group_id).is_ok() => {
let new_id = tree
.insert(
Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
2024-02-23 20:08:57 +01:00
minimize_rect: None,
2023-07-24 21:14:34 +02:00
}),
InsertBehavior::UnderNode(group_id),
)
.unwrap();
let idx = {
let data = tree.get_mut(group_id).unwrap().data_mut();
let bound_idx = data.len().min(*idx + 1);
data.add_window(bound_idx);
bound_idx
};
tree.make_nth_sibling(&new_id, dbg!(idx)).unwrap();
*window.tiling_node_id.lock().unwrap() = Some(new_id);
window
}
Some(TargetZone::InitialPlaceholder(node_id)) if tree.get(&node_id).is_ok() => {
let data = tree.get_mut(&node_id).unwrap().data_mut();
let geo = data.geometry().clone();
*data = Data::Mapped {
mapped: window.clone(),
last_geometry: geo,
2024-02-23 20:08:57 +01:00
minimize_rect: None,
2023-07-24 21:14:34 +02:00
};
*window.tiling_node_id.lock().unwrap() = Some(node_id.clone());
window
}
Some(TargetZone::WindowSplit(window_id, direction)) if tree.get(&window_id).is_ok() => {
let new_id = tree
.insert(
Node::new(Data::Mapped {
mapped: window.clone(),
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
2024-02-23 20:08:57 +01:00
minimize_rect: None,
2023-07-24 21:14:34 +02:00
}),
InsertBehavior::UnderNode(&window_id),
)
.unwrap();
let orientation = if matches!(direction, Direction::Left | Direction::Right) {
Orientation::Vertical
} else {
Orientation::Horizontal
};
TilingLayout::new_group(&mut tree, &window_id, &new_id, orientation).unwrap();
if matches!(direction, Direction::Left | Direction::Up) {
tree.make_first_sibling(&new_id).unwrap();
}
*window.tiling_node_id.lock().unwrap() = Some(new_id.clone());
window
}
Some(TargetZone::WindowStack(window_id, _)) if tree.get(&window_id).is_ok() => {
match tree.get_mut(window_id).unwrap().data_mut() {
Data::Mapped { mapped, .. } => {
mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone());
2023-07-31 17:36:32 +02:00
let Some(stack) = mapped.stack_ref_mut() else {
unreachable!()
};
2023-07-24 21:14:34 +02:00
for surface in window.windows().map(|s| s.0) {
stack.add_window(surface, None);
}
mapped.clone()
}
_ => unreachable!(),
}
}
_ => {
TilingLayout::map_to_tree(
&mut tree,
window.clone(),
&self.output,
None,
None,
None,
);
2023-07-24 21:14:34 +02:00
window
}
};
2023-07-25 16:47:01 +02:00
if let Some(root) = tree.root_node_id() {
for id in tree
.traverse_pre_order_ids(root)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
match tree.get_mut(&id).map(|node| node.data_mut()) {
Ok(Data::Placeholder { .. }) => TilingLayout::unmap_internal(&mut tree, &id),
Ok(Data::Group { pill_indicator, .. }) if pill_indicator.is_some() => {
pill_indicator.take();
}
_ => {}
2023-07-25 16:47:01 +02:00
}
}
}
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
2023-07-24 21:14:34 +02:00
2023-10-25 19:40:26 +02:00
let location = self.element_geometry(&mapped).unwrap().loc;
2023-07-24 21:14:34 +02:00
(mapped, location)
}
fn last_active_window<'a>(
2023-05-12 20:01:37 +02:00
tree: &Tree<Data>,
2022-09-28 12:01:29 +02:00
mut focus_stack: impl Iterator<Item = &'a CosmicMapped>,
2023-05-30 21:02:19 +02:00
) -> Option<(NodeId, CosmicMapped)> {
2022-09-28 12:01:29 +02:00
focus_stack
.find_map(|mapped| tree.root_node_id()
.and_then(|root| tree.traverse_pre_order_ids(root).unwrap()
2022-09-28 12:01:29 +02:00
.find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Mapped { mapped: m, .. }) if m == mapped))
2023-05-30 21:02:19 +02:00
).map(|id| (id, mapped.clone()))
)
}
2023-05-30 21:02:19 +02:00
fn currently_focused_node(
tree: &Tree<Data>,
mut target: KeyboardFocusTarget,
2023-05-30 21:02:19 +02:00
) -> Option<(NodeId, FocusedNodeData)> {
2022-10-27 16:11:54 +02:00
// if the focus is currently on a popup, treat it's toplevel as the target
if let KeyboardFocusTarget::Popup(popup) = target {
let toplevel_surface = match popup {
PopupKind::Xdg(xdg) => get_popup_toplevel(&xdg),
2023-09-29 21:33:16 +02:00
PopupKind::InputMethod(_) => unreachable!(),
2022-10-27 16:11:54 +02:00
}?;
let root_id = tree.root_node_id()?;
let node =
tree.traverse_pre_order(root_id)
.unwrap()
.find(|node| match node.data() {
Data::Mapped { mapped, .. } => mapped
.windows()
.any(|(w, _)| w.wl_surface().as_ref() == Some(&toplevel_surface)),
2022-10-27 16:11:54 +02:00
_ => false,
})?;
target = KeyboardFocusTarget::Element(match node.data() {
Data::Mapped { mapped, .. } => mapped.clone(),
_ => unreachable!(),
});
}
match target {
KeyboardFocusTarget::Element(mapped) => {
let node_id = mapped.tiling_node_id.lock().unwrap().clone()?;
let node = tree.get(&node_id).ok()?;
let data = node.data();
if data.is_mapped(Some(&mapped)) {
2023-05-30 21:02:19 +02:00
return Some((node_id, FocusedNodeData::Window(mapped)));
2022-10-27 16:11:54 +02:00
}
}
KeyboardFocusTarget::Group(window_group) => {
2023-10-25 19:40:26 +02:00
let node = tree.get(&window_group.node).ok()?;
if node.data().is_group() {
return Some((
window_group.node,
FocusedNodeData::Group(window_group.focus_stack, window_group.alive),
));
2022-10-27 16:11:54 +02:00
}
}
_ => {}
};
None
}
2022-09-28 12:01:29 +02:00
fn new_group(
tree: &mut Tree<Data>,
old_id: &NodeId,
2022-10-27 16:11:54 +02:00
new_id: &NodeId,
2022-09-28 12:01:29 +02:00
orientation: Orientation,
) -> Result<NodeId, NodeIdError> {
2022-09-28 12:01:29 +02:00
let new_group = Node::new(Data::new_group(
orientation,
Rectangle::from_loc_and_size((0, 0), (100, 100)),
));
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)
});
2022-09-28 12:01:29 +02:00
let group_id = tree
.insert(
2022-09-28 12:01:29 +02:00
new_group,
if let Some(parent) = parent_id.as_ref() {
InsertBehavior::UnderNode(parent)
} else {
InsertBehavior::AsRoot
},
)
.unwrap();
2022-09-28 12:01:29 +02:00
tree.move_node(old_id, MoveBehavior::ToParent(&group_id))
.unwrap();
// keep position
if let Some(old_pos) = pos {
2022-09-28 12:01:29 +02:00
tree.make_nth_sibling(&group_id, old_pos).unwrap();
}
2022-10-27 16:11:54 +02:00
tree.move_node(new_id, MoveBehavior::ToParent(&group_id))
.unwrap();
Ok(group_id)
2022-03-24 20:32:31 +01:00
}
2023-07-21 16:08:55 +02:00
fn has_adjacent_node(tree: &Tree<Data>, node: &NodeId, direction: Direction) -> bool {
let mut search_node = node;
match tree.ancestor_ids(node) {
Ok(mut iter) => iter.any(|parent_id| {
let parent = tree.get(parent_id).unwrap();
let children = parent.children();
let idx = children.iter().position(|id| id == search_node).unwrap();
search_node = parent_id;
match direction {
Direction::Up => {
parent.data().orientation() == Orientation::Horizontal && idx > 0
}
Direction::Down => {
parent.data().orientation() == Orientation::Horizontal
&& idx < (children.len() - 1)
}
Direction::Left => {
parent.data().orientation() == Orientation::Vertical && idx > 0
}
Direction::Right => {
parent.data().orientation() == Orientation::Vertical
&& idx < (children.len() - 1)
}
}
}),
Err(_) => false,
}
}
2023-07-27 20:24:02 +02:00
fn has_sibling_node(tree: &Tree<Data>, node: &NodeId, direction: Direction) -> bool {
match tree.get(node).ok().and_then(|node| node.parent()) {
Some(parent_id) => {
let parent = tree.get(parent_id).unwrap();
let children = parent.children();
let idx = children.iter().position(|id| id == node).unwrap();
match direction {
Direction::Up => {
parent.data().orientation() == Orientation::Horizontal && idx > 0
}
Direction::Down => {
parent.data().orientation() == Orientation::Horizontal
&& idx < (children.len() - 1)
}
Direction::Left => {
parent.data().orientation() == Orientation::Vertical && idx > 0
}
Direction::Right => {
parent.data().orientation() == Orientation::Vertical
&& idx < (children.len() - 1)
}
}
}
None => false,
}
}
2023-05-12 20:01:37 +02:00
fn update_positions(
output: &Output,
tree: &mut Tree<Data>,
gaps: (i32, i32),
) -> Option<TilingBlocker> {
#[cfg(feature = "debug")]
puffin::profile_function!();
2023-05-12 20:01:37 +02:00
if let Some(root_id) = tree.root_node_id() {
let mut configures = Vec::new();
let (outer, inner) = gaps;
let mut geo = layer_map_for_output(&output)
.non_exclusive_zone()
.as_local();
2023-05-12 20:01:37 +02:00
geo.loc.x += outer;
geo.loc.y += outer;
geo.size.w -= outer * 2;
geo.size.h -= outer * 2;
let mut stack = vec![geo];
for node_id in tree
.traverse_pre_order_ids(root_id)
.unwrap()
.collect::<Vec<_>>()
.into_iter()
{
let node = tree.get_mut(&node_id).unwrap();
let data = node.data_mut();
// flatten tree
if data.is_group() && data.len() == 1 {
// RemoveBehavior::LiftChildren sadly does not what we want: lifting them into the same place.
// So we need to fix that manually..
let idx = node.parent().cloned().map(|parent_id| {
tree.children_ids(&parent_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap()
});
let child_id = tree
.children_ids(&node_id)
.unwrap()
.cloned()
.next()
.unwrap();
tree.remove_node(node_id, RemoveBehavior::LiftChildren)
.unwrap();
if let Some(idx) = idx {
tree.make_nth_sibling(&child_id, idx).unwrap();
} else {
// additionally `RemoveBehavior::LiftChildren` doesn't work, when removing the root-node,
// even with just one child. *sigh*
tree.move_node(&child_id, MoveBehavior::ToRoot).unwrap();
}
continue;
}
if let Some(mut geo) = stack.pop() {
2023-07-21 16:08:55 +02:00
let node = tree.get(&node_id).unwrap();
let data = node.data();
2023-05-12 20:01:37 +02:00
if data.is_mapped(None) {
2023-07-21 16:08:55 +02:00
let gap = (
(
if TilingLayout::has_adjacent_node(tree, &node_id, Direction::Left)
{
inner / 2
} else {
inner
},
if TilingLayout::has_adjacent_node(tree, &node_id, Direction::Up) {
inner / 2
} else {
inner
},
),
(
if TilingLayout::has_adjacent_node(tree, &node_id, Direction::Right)
{
inner / 2
} else {
inner
},
if TilingLayout::has_adjacent_node(tree, &node_id, Direction::Down)
{
inner / 2
} else {
inner
},
),
);
geo.loc += gap.0.into();
geo.size -= gap.0.into();
geo.size -= gap.1.into();
2023-05-12 20:01:37 +02:00
}
2023-07-21 16:08:55 +02:00
let node = tree.get_mut(&node_id).unwrap();
let data = node.data_mut();
2023-05-12 20:01:37 +02:00
data.update_geometry(geo);
match data {
Data::Group {
orientation, sizes, ..
} => match orientation {
Orientation::Horizontal => {
let mut previous: i32 = sizes.iter().sum();
for size in sizes.iter().rev() {
previous -= *size;
stack.push(Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + previous),
(geo.size.w, *size),
));
2022-03-24 20:32:31 +01:00
}
}
2023-05-12 20:01:37 +02:00
Orientation::Vertical => {
let mut previous: i32 = sizes.iter().sum();
for size in sizes.iter().rev() {
previous -= *size;
stack.push(Rectangle::from_loc_and_size(
(geo.loc.x + previous, geo.loc.y),
(*size, geo.size.h),
));
}
}
},
Data::Mapped { mapped, .. } => {
if !(mapped.is_fullscreen(true) || mapped.is_maximized(true)) {
2023-05-12 20:01:37 +02:00
mapped.set_tiled(true);
2023-10-25 19:40:26 +02:00
let internal_geometry = geo.to_global(&output);
mapped.set_geometry(internal_geometry);
if let Some(serial) = mapped.configure() {
configures.push((mapped.active_window(), serial));
2022-03-24 20:32:31 +01:00
}
}
}
Data::Placeholder { .. } => {}
2022-03-24 20:32:31 +01:00
}
}
}
2023-05-12 20:01:37 +02:00
if !configures.is_empty() {
let blocker = TilingBlocker::new(configures);
for (surface, _) in &blocker.necessary_acks {
if let Some(surface) = surface.wl_surface() {
add_blocker(&surface, blocker.clone());
}
}
return Some(blocker);
}
2022-03-24 20:32:31 +01:00
}
2023-05-12 20:01:37 +02:00
None
2022-03-24 20:32:31 +01:00
}
2022-09-28 12:01:29 +02:00
pub fn element_under(
2023-07-21 16:08:55 +02:00
&mut self,
location_f64: Point<f64, Local>,
2023-07-21 16:08:55 +02:00
overview: OverviewMode,
2023-10-25 19:40:26 +02:00
) -> Option<(PointerFocusTarget, Point<i32, Local>)> {
let gaps = self.gaps();
2023-07-21 16:08:55 +02:00
let last_overview_hover = &mut self.last_overview_hover;
let placeholder_id = &self.placeholder_id;
2023-10-25 19:40:26 +02:00
let tree = &self.queue.trees.back().unwrap().0;
let root = tree.root_node_id()?;
let location = location_f64.to_i32_round();
2023-10-25 19:40:26 +02:00
{
let output_geo =
Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_logical())
.as_local();
if !output_geo.contains(location) {
return None;
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
}
2023-10-25 19:40:26 +02:00
if !matches!(overview, OverviewMode::Started(_, _)) {
last_overview_hover.take();
}
2023-07-25 16:47:01 +02:00
2023-10-25 19:40:26 +02:00
if matches!(overview, OverviewMode::None) {
for (mapped, geo) in self.mapped() {
if !mapped.bbox().contains((location - geo.loc).as_logical()) {
continue;
}
if mapped.is_in_input_region(&(location_f64 - geo.loc.to_f64()).as_logical()) {
return Some((mapped.clone().into(), geo.loc));
}
}
2023-10-25 19:40:26 +02:00
let mut result = None;
let mut lookup = Some(root.clone());
while let Some(node) = lookup {
let data = tree.get(&node).unwrap().data();
if data.geometry().contains(location) {
result = Some(node.clone());
}
2023-07-21 16:08:55 +02:00
2023-10-25 19:40:26 +02:00
lookup = None;
if result.is_some() && data.is_group() {
for child_id in tree.children_ids(&node).unwrap() {
if tree
.get(child_id)
.unwrap()
.data()
.geometry()
.contains(location)
{
lookup = Some(child_id.clone());
break;
}
}
}
2023-10-25 19:40:26 +02:00
}
2023-10-25 19:40:26 +02:00
match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) {
Some((
_,
Data::Mapped {
mapped,
last_geometry,
2024-02-23 17:25:40 +01:00
..
2023-10-25 19:40:26 +02:00
},
)) => {
let test_point = (location.to_f64() - last_geometry.loc.to_f64()
+ mapped.geometry().loc.to_f64().as_local())
.as_logical();
mapped.is_in_input_region(&test_point).then(|| {
(
mapped.clone().into(),
last_geometry.loc - mapped.geometry().loc.as_local(),
)
})
}
Some((
id,
Data::Group {
orientation,
last_geometry,
..
},
)) => {
let idx = tree
.children(&id)
.unwrap()
.position(|node| {
let data = node.data();
match orientation {
Orientation::Vertical => location.x < data.geometry().loc.x,
Orientation::Horizontal => location.y < data.geometry().loc.y,
}
})
2023-10-25 19:40:26 +02:00
.and_then(|x| x.checked_sub(1))?;
Some((
2023-10-25 19:40:26 +02:00
ResizeForkTarget {
node: id.clone(),
output: self.output.downgrade(),
left_up_idx: idx,
orientation,
2023-10-25 19:40:26 +02:00
}
.into(),
last_geometry.loc
+ tree
.children(&id)
.unwrap()
.skip(idx)
.next()
.map(|node| {
let geo = node.data().geometry();
geo.loc + geo.size
})
.unwrap(),
))
}
_ => None,
}
} else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) {
let non_exclusive_zone = layer_map_for_output(&self.output)
.non_exclusive_zone()
.as_local();
let geometries = geometries_for_groupview(
tree,
Option::<&mut GlowRenderer>::None,
non_exclusive_zone,
None,
1.0,
overview.alpha().unwrap(),
1.0,
placeholder_id,
Some(None),
None,
None,
self.theme.cosmic(),
2023-10-25 19:40:26 +02:00
)
.0;
let mut result = None;
let mut lookup = Some(root.clone());
while let Some(node) = lookup {
let data = tree.get(&node).unwrap().data();
if geometries
.get(&node)
.map(|geo| geo.contains(location))
.unwrap_or(false)
{
result = Some(node.clone());
}
lookup = None;
if result.is_some() && data.is_group() {
if tree.children(&node).unwrap().any(|child| {
matches!(
child.data(),
Data::Placeholder {
initial_placeholder: false,
..
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
)
}) {
break;
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
for child_id in tree.children_ids(&node).unwrap() {
if geometries
.get(child_id)
.map(|geo| geo.contains(location))
.unwrap_or(false)
{
lookup = Some(child_id.clone());
2023-07-27 20:24:02 +02:00
break;
}
2023-10-25 19:40:26 +02:00
}
}
}
if let Some(res_id) = result {
let mut last_geometry = *geometries.get(&res_id)?;
let node = tree.get(&res_id).unwrap();
let data = node.data().clone();
let group_zone = if let Data::Group { orientation, .. } = &data {
if node.children().iter().any(|child_id| {
tree.get(child_id)
.ok()
.map(|child| {
matches!(
child.data(),
Data::Placeholder {
initial_placeholder: false,
..
}
)
})
.unwrap_or(false)
}) {
None
} else {
let left_edge = match &*last_overview_hover {
Some((_, TargetZone::GroupEdge(id, Direction::Left)))
if *id == res_id =>
2023-07-21 16:08:55 +02:00
{
2023-10-25 19:40:26 +02:00
let zone = Rectangle::from_loc_and_size(
last_geometry.loc,
(80, last_geometry.size.h),
);
last_geometry.loc.x += 80;
last_geometry.size.w -= 80;
zone
}
_ => {
let zone = Rectangle::from_loc_and_size(
last_geometry.loc,
(32, last_geometry.size.h),
);
last_geometry.loc.x += 32;
last_geometry.size.w -= 32;
zone
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
};
let top_edge = match &*last_overview_hover {
Some((_, TargetZone::GroupEdge(id, Direction::Up)))
if *id == res_id =>
{
let zone = Rectangle::from_loc_and_size(
last_geometry.loc,
(last_geometry.size.w, 80),
);
last_geometry.loc.y += 80;
last_geometry.size.h -= 80;
zone
}
_ => {
let zone = Rectangle::from_loc_and_size(
last_geometry.loc,
(last_geometry.size.w, 32),
);
last_geometry.loc.y += 32;
last_geometry.size.h -= 32;
zone
}
};
let right_edge = match &*last_overview_hover {
Some((_, TargetZone::GroupEdge(id, Direction::Right)))
if *id == res_id =>
{
let zone = Rectangle::from_loc_and_size(
(
last_geometry.loc.x + last_geometry.size.w - 80,
last_geometry.loc.y,
),
(80, last_geometry.size.h),
);
last_geometry.size.w -= 80;
zone
}
_ => {
let zone = Rectangle::from_loc_and_size(
(
last_geometry.loc.x + last_geometry.size.w - 32,
last_geometry.loc.y,
),
(32, last_geometry.size.h),
);
last_geometry.size.w -= 32;
zone
}
};
let bottom_edge = match &*last_overview_hover {
Some((_, TargetZone::GroupEdge(id, Direction::Down)))
if *id == res_id =>
{
let zone = Rectangle::from_loc_and_size(
(
last_geometry.loc.x,
last_geometry.loc.y + last_geometry.size.h - 80,
),
(last_geometry.size.w, 80),
);
last_geometry.size.h -= 80;
zone
}
_ => {
let zone = Rectangle::from_loc_and_size(
(
last_geometry.loc.x,
last_geometry.loc.y + last_geometry.size.h - 32,
),
(last_geometry.size.w, 32),
);
last_geometry.size.h -= 32;
zone
}
};
if left_edge.contains(location) {
Some(TargetZone::GroupEdge(res_id.clone(), Direction::Left))
} else if right_edge.contains(location) {
Some(TargetZone::GroupEdge(res_id.clone(), Direction::Right))
} else if top_edge.contains(location) {
Some(TargetZone::GroupEdge(res_id.clone(), Direction::Up))
} else if bottom_edge.contains(location) {
Some(TargetZone::GroupEdge(res_id.clone(), Direction::Down))
} else {
let idx = tree
.children_ids(&res_id)
.unwrap()
.position(|node| {
let Some(geo) = geometries.get(node) else {
return false;
};
2023-10-25 19:40:26 +02:00
match orientation {
Orientation::Vertical => location.x < geo.loc.x,
Orientation::Horizontal => location.y < geo.loc.y,
}
})
.and_then(|x| x.checked_sub(1))
.unwrap_or(0);
Some(TargetZone::GroupInterior(res_id.clone(), idx))
2023-07-21 16:08:55 +02:00
}
}
2023-10-25 19:40:26 +02:00
} else {
None
};
2023-07-21 16:08:55 +02:00
2023-10-25 19:40:26 +02:00
let target_zone = group_zone.unwrap_or_else(|| match &data {
Data::Placeholder { .. } => TargetZone::InitialPlaceholder(res_id),
Data::Group { .. } | Data::Mapped { .. } => {
let id = if data.is_group() {
tree.get(&res_id)
.unwrap()
.children()
.iter()
.find(|child_id| tree.get(child_id).unwrap().data().is_mapped(None))
.expect("Placeholder group without real window?")
.clone()
2023-07-27 20:24:02 +02:00
} else {
2023-10-25 19:40:26 +02:00
res_id
};
let third_width = (last_geometry.size.w as f64 / 3.0).round() as i32;
let third_height = (last_geometry.size.h as f64 / 3.0).round() as i32;
let stack_region = Rectangle::from_extemities(
(
last_geometry.loc.x + third_width,
last_geometry.loc.y + third_height,
),
(
last_geometry.loc.x + 2 * third_width,
last_geometry.loc.y + 2 * third_height,
),
);
if stack_region.contains(location) {
TargetZone::WindowStack(id, last_geometry)
} else {
let left_right = {
let relative_loc = (location.x - last_geometry.loc.x) as f64;
if relative_loc < last_geometry.size.w as f64 / 2.0 {
(Direction::Left, relative_loc / last_geometry.size.w as f64)
} else {
(
Direction::Right,
1.0 - (relative_loc / last_geometry.size.w as f64),
)
2023-07-21 16:08:55 +02:00
}
};
2023-10-25 19:40:26 +02:00
let up_down = {
let relative_loc = (location.y - last_geometry.loc.y) as f64;
if relative_loc < last_geometry.size.h as f64 / 2.0 {
(Direction::Up, relative_loc / last_geometry.size.h as f64)
} else {
(
Direction::Down,
1.0 - (relative_loc / last_geometry.size.h as f64),
)
2023-07-21 16:08:55 +02:00
}
};
2023-10-25 19:40:26 +02:00
let direction = if left_right.1 < up_down.1 {
left_right.0
2023-07-21 16:08:55 +02:00
} else {
2023-10-25 19:40:26 +02:00
up_down.0
2023-07-27 20:24:02 +02:00
};
2023-10-25 19:40:26 +02:00
TargetZone::WindowSplit(id, direction)
}
}
});
match &mut *last_overview_hover {
last_overview_hover @ None => {
*last_overview_hover = Some((
None,
tree.traverse_pre_order_ids(root)
.unwrap()
.find(|id| match tree.get(id).unwrap().data() {
Data::Placeholder {
initial_placeholder: true,
..
} => true,
_ => false,
})
.map(|node_id| TargetZone::InitialPlaceholder(node_id))
.unwrap_or(TargetZone::Initial),
));
}
Some((instant, old_target_zone)) => {
if *old_target_zone != target_zone {
let overdue = if let Some(instant) = instant {
match old_target_zone {
TargetZone::InitialPlaceholder(_) => {
Instant::now().duration_since(*instant)
> INITIAL_MOUSE_ANIMATION_DELAY
2023-07-27 20:24:02 +02:00
}
2023-10-25 19:40:26 +02:00
_ => {
Instant::now().duration_since(*instant)
> MOUSE_ANIMATION_DELAY
2023-07-27 20:24:02 +02:00
}
2023-10-25 19:40:26 +02:00
}
} else {
*instant = Some(Instant::now());
false
};
2023-07-27 20:24:02 +02:00
2023-10-25 19:40:26 +02:00
if overdue {
let duration = if target_zone.is_window_zone()
&& !old_target_zone.is_window_zone()
{
ANIMATION_DURATION * 2
2023-07-27 20:24:02 +02:00
} else {
2023-10-25 19:40:26 +02:00
ANIMATION_DURATION
2023-07-27 20:24:02 +02:00
};
2023-10-25 19:40:26 +02:00
let mut tree = tree.copy_clone();
2023-07-27 20:24:02 +02:00
2023-10-25 19:40:26 +02:00
// remove old placeholders
let removed = if let TargetZone::InitialPlaceholder(node_id) =
old_target_zone
{
if tree.get(&node_id).is_ok() {
TilingLayout::unmap_internal(&mut tree, &node_id);
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
true
} else if let TargetZone::WindowSplit(node_id, _) = old_target_zone
{
if let Some(children) = tree
.get(&node_id)
.ok()
.and_then(|node| node.parent())
.and_then(|parent_id| tree.get(parent_id).ok())
.map(|node| node.children().clone())
2023-07-21 16:08:55 +02:00
{
2023-10-25 19:40:26 +02:00
for id in children {
let matches = matches!(
tree.get(&id).unwrap().data(),
Data::Placeholder {
initial_placeholder: false,
..
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
);
if matches {
TilingLayout::unmap_internal(&mut tree, &id);
break;
2023-07-21 16:08:55 +02:00
}
}
2023-10-25 19:40:26 +02:00
}
true
} else if let TargetZone::GroupEdge(node_id, _) = old_target_zone {
if let Ok(node) = tree.get_mut(&node_id) {
match node.data_mut() {
Data::Group { pill_indicator, .. } => {
*pill_indicator = None;
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
_ => unreachable!(),
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
}
true
} else if let TargetZone::GroupInterior(node_id, _) =
old_target_zone
{
if let Ok(node) = tree.get_mut(&node_id) {
match node.data_mut() {
Data::Group { pill_indicator, .. } => {
*pill_indicator = None;
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
_ => unreachable!(),
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
}
true
} else {
false
};
2023-07-21 16:08:55 +02:00
2023-10-25 19:40:26 +02:00
// add placeholders
let added = if let TargetZone::WindowSplit(node_id, dir) =
&target_zone
{
let id = tree
.insert(
Node::new(Data::Placeholder {
last_geometry: Rectangle::from_loc_and_size(
(0, 0),
(100, 100),
),
initial_placeholder: false,
}),
InsertBehavior::UnderNode(node_id),
2023-07-21 16:08:55 +02:00
)
.unwrap();
2023-10-25 19:40:26 +02:00
let orientation =
if matches!(dir, Direction::Left | Direction::Right) {
Orientation::Vertical
} else {
Orientation::Horizontal
};
TilingLayout::new_group(&mut tree, &node_id, &id, orientation)
.unwrap();
if matches!(dir, Direction::Left | Direction::Up) {
tree.make_first_sibling(&id).unwrap();
}
2023-07-21 16:08:55 +02:00
2023-10-25 19:40:26 +02:00
true
} else if let TargetZone::GroupEdge(node_id, direction) =
&target_zone
{
if let Ok(node) = tree.get_mut(&node_id) {
match node.data_mut() {
Data::Group { pill_indicator, .. } => {
*pill_indicator =
Some(PillIndicator::Outer(*direction));
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
_ => unreachable!(),
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
true
} else {
false
}
} else if let TargetZone::GroupInterior(node_id, idx) = &target_zone
{
if let Ok(node) = tree.get_mut(&node_id) {
match node.data_mut() {
Data::Group { pill_indicator, .. } => {
*pill_indicator = Some(PillIndicator::Inner(*idx));
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
_ => unreachable!(),
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
true
2023-07-21 16:08:55 +02:00
} else {
false
}
2023-10-25 19:40:26 +02:00
} else {
false
};
2023-07-21 16:08:55 +02:00
2023-10-25 19:40:26 +02:00
if removed || added {
let blocker = TilingLayout::update_positions(
&self.output,
&mut tree,
gaps,
2023-10-25 19:40:26 +02:00
);
self.queue.push_tree(tree, duration, blocker);
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
2023-07-21 16:08:55 +02:00
*instant = None;
2023-10-25 19:40:26 +02:00
*old_target_zone = target_zone;
2023-07-21 16:08:55 +02:00
}
2023-10-25 19:40:26 +02:00
} else {
*instant = None;
2023-07-21 16:08:55 +02:00
}
}
}
}
2023-10-25 19:40:26 +02:00
None
} else {
None
}
}
pub fn mapped(&self) -> impl Iterator<Item = (&CosmicMapped, Rectangle<i32, Local>)> {
2023-10-25 19:40:26 +02:00
let tree = &self.queue.trees.back().unwrap().0;
let iter = if let Some(root) = tree.root_node_id() {
Some(
tree.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.filter(|node| match node.data() {
Data::Mapped { mapped, .. } => mapped.is_activated(false),
_ => unreachable!(),
})
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry.clone()),
2023-10-25 19:40:26 +02:00
_ => unreachable!(),
})
.chain(
2022-09-28 12:01:29 +02:00
tree.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
2022-12-15 13:07:25 +01:00
.filter(|node| match node.data() {
2023-10-25 19:40:26 +02:00
Data::Mapped { mapped, .. } => !mapped.is_activated(false),
2022-12-15 13:07:25 +01:00
_ => unreachable!(),
})
2022-09-28 12:01:29 +02:00
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry.clone()),
2022-09-28 12:01:29 +02:00
_ => unreachable!(),
2023-10-25 19:40:26 +02:00
}),
),
)
} else {
None
};
iter.into_iter().flatten()
2022-09-28 12:01:29 +02:00
}
pub fn windows(&self) -> impl Iterator<Item = (CosmicSurface, Rectangle<i32, Local>)> + '_ {
self.mapped().flat_map(|(mapped, geo)| {
2023-05-30 16:22:29 +02:00
mapped.windows().map(move |(w, p)| {
(w, {
2023-05-30 16:22:29 +02:00
let mut geo = geo.clone();
geo.loc += p.as_local();
geo.size -= p.to_size().as_local();
2023-05-30 16:22:29 +02:00
geo
})
})
2022-09-28 12:01:29 +02:00
})
}
2023-10-25 19:40:26 +02:00
pub fn has_node(&self, node: &NodeId) -> bool {
let tree = &self.queue.trees.back().unwrap().0;
tree.root_node_id()
.map(|root| {
tree.traverse_pre_order_ids(root)
.unwrap()
.any(|id| &id == node)
})
.unwrap_or(false)
}
2023-05-12 20:01:37 +02:00
2023-10-25 19:40:26 +02:00
pub fn merge(&mut self, mut other: TilingLayout) {
let gaps = self.gaps();
2023-10-25 19:40:26 +02:00
let src = other.queue.trees.pop_back().unwrap().0;
let mut dst = self.queue.trees.back().unwrap().0.copy_clone();
2023-05-12 20:01:37 +02:00
2023-10-25 19:40:26 +02:00
let orientation = match self.output.geometry().size {
x if x.w >= x.h => Orientation::Vertical,
_ => Orientation::Horizontal,
};
TilingLayout::merge_trees(src, &mut dst, orientation);
let blocker = TilingLayout::update_positions(&self.output, &mut dst, gaps);
2023-10-25 19:40:26 +02:00
self.queue.push_tree(dst, ANIMATION_DURATION, blocker);
2022-09-28 12:01:29 +02:00
}
fn merge_trees(src: Tree<Data>, dst: &mut Tree<Data>, orientation: Orientation) {
2022-11-22 15:47:27 +01:00
if let Some(dst_root_id) = dst.root_node_id().cloned() {
2022-09-28 12:01:29 +02:00
let mut stack = Vec::new();
2022-11-22 15:47:27 +01:00
if let Some(src_root_id) = src.root_node_id() {
let root_node = src.get(src_root_id).unwrap();
let new_node = Node::new(root_node.data().clone());
let new_id = dst
.insert(new_node, InsertBehavior::UnderNode(&dst_root_id))
.unwrap();
if let &mut Data::Mapped { ref mut mapped, .. } =
dst.get_mut(&new_id).unwrap().data_mut()
{
*mapped.tiling_node_id.lock().unwrap() = Some(new_id.clone());
}
TilingLayout::new_group(dst, &dst_root_id, &new_id, orientation).unwrap();
stack.push((src_root_id.clone(), new_id));
2022-09-28 12:01:29 +02:00
}
while let Some((src_id, dst_id)) = stack.pop() {
for child_id in src.children_ids(&src_id).unwrap() {
let src_node = src.get(&child_id).unwrap();
let new_node = Node::new(src_node.data().clone());
let new_child_id = dst
.insert(new_node, InsertBehavior::UnderNode(&dst_id))
.unwrap();
2022-11-22 15:47:27 +01:00
if let &mut Data::Mapped { ref mut mapped, .. } =
dst.get_mut(&new_child_id).unwrap().data_mut()
{
*mapped.tiling_node_id.lock().unwrap() = Some(new_child_id.clone());
}
2022-09-28 12:01:29 +02:00
stack.push((child_id.clone(), new_child_id));
}
}
} else {
*dst = src;
}
}
2023-10-25 19:40:26 +02:00
pub fn render<R>(
2022-09-28 12:01:29 +02:00
&self,
renderer: &mut R,
2023-05-30 21:02:19 +02:00
seat: Option<&Seat<State>>,
2023-10-25 19:40:26 +02:00
non_exclusive_zone: Rectangle<i32, Local>,
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
2023-07-06 00:03:26 +02:00
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
indicator_thickness: u8,
theme: &cosmic::theme::CosmicTheme,
) -> Result<
(
Vec<CosmicMappedRenderElement<R>>,
Vec<CosmicMappedRenderElement<R>>,
),
OutputNotMapped,
>
2022-09-28 12:01:29 +02:00
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
2022-09-28 12:01:29 +02:00
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
2023-03-07 22:20:44 +01:00
CosmicWindowRenderElement<R>: RenderElement<R>,
2023-06-08 13:19:30 +02:00
CosmicStackRenderElement<R>: RenderElement<R>,
2022-09-28 12:01:29 +02:00
{
#[cfg(feature = "debug")]
puffin::profile_function!();
2023-10-25 19:40:26 +02:00
let output_scale = self.output.current_scale().fractional_scale();
2022-09-28 12:01:29 +02:00
2023-10-25 19:40:26 +02:00
let (target_tree, duration, _) = if self.queue.animation_start.is_some() {
self.queue
2023-05-12 20:01:37 +02:00
.trees
.get(1)
.expect("Animation ongoing, should have two trees")
} else {
2023-10-25 19:40:26 +02:00
self.queue.trees.front().unwrap()
2023-05-12 20:01:37 +02:00
};
2023-10-25 19:40:26 +02:00
let reference_tree = self
.queue
2023-05-12 20:01:37 +02:00
.animation_start
.is_some()
2023-10-25 19:40:26 +02:00
.then(|| &self.queue.trees.front().unwrap().0);
2023-05-12 20:01:37 +02:00
2023-10-25 19:40:26 +02:00
let percentage = if let Some(animation_start) = self.queue.animation_start {
let percentage = Instant::now().duration_since(animation_start).as_millis() as f32
/ duration.as_millis() as f32;
ease(EaseInOutCubic, 0.0, 1.0, percentage)
2023-05-12 20:01:37 +02:00
} else {
1.0
};
let draw_groups = overview.0.alpha();
2022-12-15 13:07:25 +01:00
let mut window_elements = Vec::new();
let mut popup_elements = Vec::new();
2023-05-12 20:01:37 +02:00
let is_overview = !matches!(overview.0, OverviewMode::None);
let is_mouse_tiling = (matches!(overview.0, OverviewMode::Started(Trigger::Pointer(_), _)))
2023-07-24 19:31:31 +02:00
.then(|| self.last_overview_hover.as_ref().map(|x| &x.1));
let swap_desc = match &overview.0 {
OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _)
| OverviewMode::Ended(Some(Trigger::KeyboardSwap(_, desc)), _) => Some(desc.clone()),
_ => None,
};
2023-05-17 19:46:21 +02:00
// all gone windows and fade them out
let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() {
let (geometries, _) = if let Some(transition) = draw_groups {
Some(geometries_for_groupview(
2023-05-17 19:46:21 +02:00
reference_tree,
2023-07-21 16:08:55 +02:00
&mut *renderer,
2023-05-17 19:46:21 +02:00
non_exclusive_zone,
2023-05-30 21:02:19 +02:00
seat, // TODO: Would be better to be an old focus,
2023-05-17 19:46:21 +02:00
// but for that we have to associate focus with a tree (and animate focus changes properly)
2023-05-30 13:20:46 +02:00
1.0 - transition,
transition,
output_scale,
&self.placeholder_id,
is_mouse_tiling,
swap_desc.clone(),
overview.1.as_ref().and_then(|(_, tree)| tree.clone()),
theme,
))
2023-05-17 19:46:21 +02:00
} else {
None
2023-05-12 20:01:37 +02:00
}
2023-05-17 19:46:21 +02:00
.unzip();
// all old windows we want to fade out
let (w_elements, p_elements) = render_old_tree(
2023-05-17 19:46:21 +02:00
reference_tree,
target_tree,
renderer,
geometries.clone(),
output_scale,
percentage,
2024-02-23 20:08:57 +01:00
indicator_thickness,
swap_desc.is_some(),
2024-02-23 20:08:57 +01:00
theme,
);
window_elements.extend(w_elements);
popup_elements.extend(p_elements);
2023-05-17 19:46:21 +02:00
geometries
} else {
None
};
let (geometries, group_elements) = if let Some(transition) = draw_groups {
Some(geometries_for_groupview(
2023-05-17 19:46:21 +02:00
target_tree,
2023-07-21 16:08:55 +02:00
&mut *renderer,
2023-05-17 19:46:21 +02:00
non_exclusive_zone,
2023-05-30 21:02:19 +02:00
seat,
2023-05-30 13:20:46 +02:00
transition,
transition,
output_scale,
&self.placeholder_id,
is_mouse_tiling,
swap_desc.clone(),
overview.1.as_ref().and_then(|(_, tree)| tree.clone()),
theme,
))
2023-05-17 19:46:21 +02:00
} else {
None
2023-05-12 20:01:37 +02:00
}
2023-05-17 19:46:21 +02:00
.unzip();
2023-05-12 20:01:37 +02:00
2023-05-17 19:46:21 +02:00
// all alive windows
let (w_elements, p_elements) = render_new_tree(
2023-05-17 19:46:21 +02:00
target_tree,
reference_tree,
renderer,
non_exclusive_zone,
2023-05-17 19:46:21 +02:00
geometries,
old_geometries,
2023-08-31 18:29:20 +02:00
is_overview,
2023-05-30 21:02:19 +02:00
seat,
2023-10-25 19:40:26 +02:00
&self.output,
2023-05-17 19:46:21 +02:00
percentage,
draw_groups,
if let Some(transition) = draw_groups {
2023-05-30 13:20:46 +02:00
let diff = (4u8.abs_diff(indicator_thickness) as f32 * transition).round() as u8;
if 3 > indicator_thickness {
indicator_thickness + diff
} else {
indicator_thickness - diff
}
} else {
indicator_thickness
},
overview,
2023-07-06 00:03:26 +02:00
resize_indicator,
swap_desc.clone(),
&self.swapping_stack_surface_id,
&self.placeholder_id,
theme,
);
window_elements.extend(w_elements);
popup_elements.extend(p_elements);
2023-05-17 19:46:21 +02:00
// tiling hints
if let Some(group_elements) = group_elements {
window_elements.extend(group_elements);
}
Ok((window_elements, popup_elements))
2023-05-17 19:46:21 +02:00
}
fn gaps(&self) -> (i32, i32) {
let g = self.theme.cosmic().gaps;
(g.0 as i32, g.1 as i32)
}
2023-05-17 19:46:21 +02:00
}
2023-07-27 20:24:02 +02:00
const GAP_KEYBOARD: i32 = 8;
const GAP_MOUSE: i32 = 32;
const PLACEHOLDER_GAP_MOUSE: i32 = 8;
const WINDOW_BACKDROP_BORDER: i32 = 4;
const WINDOW_BACKDROP_GAP: i32 = 12;
2023-06-09 19:37:26 +02:00
const MAX_SWAP_WINDOW_SIZE: (i32, i32) = (360, 240);
fn swap_factor(size: Size<i32, Logical>) -> f64 {
let target_w = std::cmp::min(size.w, MAX_SWAP_WINDOW_SIZE.0);
let target_h = std::cmp::min(size.h, MAX_SWAP_WINDOW_SIZE.1);
(target_w as f64 / size.w as f64).min(target_h as f64 / size.h as f64)
}
fn swap_geometry(
size: Size<i32, Logical>,
relative_to: Rectangle<i32, Local>,
) -> Rectangle<i32, Local> {
let factor = swap_factor(size);
let new_size = Size::from((
(size.w as f64 * factor).round() as i32,
(size.h as f64 * factor).round() as i32,
));
let loc = Point::from((
relative_to.loc.x + relative_to.size.w - new_size.w,
relative_to.loc.y,
));
Rectangle::from_loc_and_size(loc, new_size)
}
2023-07-21 16:08:55 +02:00
fn geometries_for_groupview<'a, R>(
2023-05-17 19:46:21 +02:00
tree: &Tree<Data>,
2023-07-21 16:08:55 +02:00
renderer: impl Into<Option<&'a mut R>>,
non_exclusive_zone: Rectangle<i32, Local>,
2023-05-30 21:02:19 +02:00
seat: Option<&Seat<State>>,
2023-05-17 19:46:21 +02:00
alpha: f32,
transition: f32,
output_scale: f64,
placeholder_id: &Id,
2023-07-24 19:31:31 +02:00
mouse_tiling: Option<Option<&TargetZone>>,
swap_desc: Option<NodeDesc>,
swap_tree: Option<&Tree<Data>>,
theme: &cosmic::theme::CosmicTheme,
) -> (
HashMap<NodeId, Rectangle<i32, Local>>,
2023-05-17 19:46:21 +02:00
Vec<CosmicMappedRenderElement<R>>,
)
2023-05-17 19:46:21 +02:00
where
2023-07-21 16:08:55 +02:00
R: Renderer + ImportAll + ImportMem + AsGlowRenderer + 'a,
2023-05-17 19:46:21 +02:00
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
{
// we need to recalculate geometry for all elements, if we are drawing groups
let gap: i32 = (if mouse_tiling.is_some() {
GAP_MOUSE
} else {
GAP_KEYBOARD
} as f32
* transition)
.round() as i32;
let mut renderer = renderer.into();
let root = tree.root_node_id();
let mut stack = Vec::new();
if swap_tree.is_some() {
// push bogos value, that will get ignored anyway
stack.push((Rectangle::from_loc_and_size((0, 0), (320, 240)), 0));
}
if root.is_some() {
stack.push((non_exclusive_zone, 0));
}
2023-05-30 21:02:19 +02:00
let mut elements = Vec::new();
let mut geometries: HashMap<NodeId, Rectangle<i32, Local>> = HashMap::new();
let alpha = alpha * transition;
2023-05-30 13:20:46 +02:00
let focused = seat
.and_then(|seat| {
seat.get_keyboard()
.unwrap()
.current_focus()
2023-10-25 19:40:26 +02:00
.and_then(|target| TilingLayout::currently_focused_node(&tree, target))
})
.map(|(id, _)| id);
let focused_geo = if let Some(focused_id) = focused.as_ref() {
Some(*tree.get(focused_id).unwrap().data().geometry())
} else {
None
};
2023-05-17 19:46:21 +02:00
let has_potential_groups = if let Some(focused_id) = focused.as_ref() {
let focused_node = tree.get(focused_id).unwrap();
if let Some(parent) = focused_node.parent() {
let parent_node = tree.get(parent).unwrap();
parent_node.children().len() > 2
} else {
false
}
} else {
false
};
for (tree, node_id) in root
.into_iter()
.flat_map(|root| tree.traverse_pre_order_ids(root).unwrap())
.map(|id| (tree, id))
.chain(
swap_tree
.as_ref()
.filter(|_| swap_desc.as_ref().unwrap().stack_window.is_none())
.into_iter()
.flat_map(|tree| {
tree.traverse_pre_order_ids(&swap_desc.as_ref().unwrap().node)
.unwrap()
})
.map(|id| (*swap_tree.as_ref().unwrap(), id)),
)
{
if let Some((mut geo, depth)) = stack.pop() {
let node: &Node<Data> = tree.get(&node_id).unwrap();
let data = node.data();
let is_placeholder_sibling = node
.parent()
.and_then(|parent_id| tree.children_ids(parent_id).ok())
.map(|mut siblings| {
siblings.any(|child_id| tree.get(child_id).unwrap().data().is_placeholder())
})
.unwrap_or(false);
2023-07-21 16:08:55 +02:00
let render_potential_group = swap_desc.is_none()
&& has_potential_groups
&& (if let Some(focused_id) = focused.as_ref() {
// `focused` can move into us directly
if let Some(parent) = node.parent() {
let parent_data = tree.get(parent).unwrap().data();
2023-05-17 19:46:21 +02:00
let idx = tree
.children_ids(parent)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
if let Some(focused_idx) = tree
.children_ids(parent)
.unwrap()
.position(|id| id == focused_id)
{
// only direct neighbors
focused_idx.abs_diff(idx) == 1
// skip neighbors, if this is a group of two
&& parent_data.len() > 2
2023-05-17 19:46:21 +02:00
} else {
false
}
2023-05-30 13:20:46 +02:00
} else {
false
}
} else {
false
});
let (element_gap_left, element_gap_up, element_gap_right, element_gap_down) = {
let gap = if is_placeholder_sibling {
PLACEHOLDER_GAP_MOUSE
} else {
gap
};
(
if TilingLayout::has_sibling_node(tree, &node_id, Direction::Left)
&& (mouse_tiling.is_some() || depth > 0)
{
gap / 2
} else {
0
},
if TilingLayout::has_sibling_node(tree, &node_id, Direction::Up)
&& (mouse_tiling.is_some() || depth > 0)
{
gap / 2
} else {
0
},
if TilingLayout::has_sibling_node(tree, &node_id, Direction::Right)
&& (mouse_tiling.is_some() || depth > 0)
{
gap / 2
} else {
0
},
if TilingLayout::has_sibling_node(tree, &node_id, Direction::Down)
&& (mouse_tiling.is_some() || depth > 0)
{
gap / 2
} else {
0
},
)
};
2023-05-17 19:46:21 +02:00
let group_color = group_color(theme);
match data {
Data::Group {
orientation,
last_geometry,
sizes,
alive,
pill_indicator,
} => {
let render_active_child = if let Some(focused_id) = focused.as_ref() {
!has_potential_groups
&& swap_desc.is_none()
&& node
.children()
.iter()
.any(|child_id| child_id == focused_id)
2023-07-27 20:24:02 +02:00
} else {
false
2023-07-27 20:24:02 +02:00
};
geo.loc += (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_right, element_gap_down).into();
2023-05-12 20:01:37 +02:00
geometries.insert(node_id.clone(), geo);
2023-07-27 20:24:02 +02:00
if let Some(renderer) = renderer.as_mut() {
if (render_potential_group || render_active_child) && Some(&node_id) != root
{
elements.push(
IndicatorShader::element(
*renderer,
Key::Group(Arc::downgrade(alive)),
geo,
4,
if render_active_child { 16 } else { 8 },
alpha * if render_potential_group { 0.40 } else { 1.0 },
output_scale,
group_color,
)
.into(),
);
}
if mouse_tiling.is_some()
&& pill_indicator.is_some()
&& Some(&node_id) != root
{
elements.push(
IndicatorShader::element(
*renderer,
Key::Group(Arc::downgrade(alive)),
geo,
4,
8,
alpha * 0.40,
output_scale,
group_color,
)
.into(),
);
}
2023-07-27 20:24:02 +02:00
if mouse_tiling.is_some()
&& node
.parent()
.map(|parent_id| {
matches!(
tree.get(&parent_id).unwrap().data(),
Data::Group {
pill_indicator: Some(_),
..
}
2023-07-21 16:08:55 +02:00
)
})
.unwrap_or(false)
{
// test if parent pill-indicator is adjacent
let parent_id = node.parent().unwrap();
let parent = tree.get(parent_id).unwrap();
let own_idx = tree
.children_ids(parent_id)
.unwrap()
.position(|child_id| child_id == &node_id)
.unwrap();
let draw_outline = match parent.data() {
Data::Group {
pill_indicator,
orientation,
..
} => match pill_indicator {
Some(PillIndicator::Inner(pill_idx)) => {
*pill_idx == own_idx || pill_idx + 1 == own_idx
}
Some(PillIndicator::Outer(dir)) => match (dir, orientation) {
(Direction::Left, Orientation::Horizontal)
| (Direction::Right, Orientation::Horizontal)
| (Direction::Up, Orientation::Vertical)
| (Direction::Down, Orientation::Vertical) => true,
(Direction::Left, Orientation::Vertical)
| (Direction::Up, Orientation::Horizontal) => own_idx == 0,
(Direction::Right, Orientation::Vertical)
| (Direction::Down, Orientation::Horizontal) => {
own_idx + 1 == parent.data().len()
}
},
None => unreachable!(),
},
_ => unreachable!(),
};
if draw_outline {
2023-07-21 16:08:55 +02:00
elements.push(
IndicatorShader::element(
*renderer,
Key::Group(Arc::downgrade(alive)),
geo,
4,
8,
alpha * 0.15,
output_scale,
group_color,
2023-07-21 16:08:55 +02:00
)
.into(),
);
}
2023-05-17 19:46:21 +02:00
}
}
2023-05-17 19:46:21 +02:00
geo.loc += (gap, gap).into();
geo.size -= (gap * 2, gap * 2).into();
2023-05-17 19:46:21 +02:00
if mouse_tiling.is_some() {
if let Some(PillIndicator::Outer(direction)) = pill_indicator {
let (pill_geo, remaining_geo) = match direction {
Direction::Left => (
Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y),
(16, geo.size.h),
2023-07-21 16:08:55 +02:00
),
Rectangle::from_loc_and_size(
(geo.loc.x + 48, geo.loc.y),
(geo.size.w - 48, geo.size.h),
2023-07-21 16:08:55 +02:00
),
),
Direction::Up => (
Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y),
(geo.size.w, 16),
2023-07-21 16:08:55 +02:00
),
Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + 48),
(geo.size.w, geo.size.h - 48),
2023-07-21 16:08:55 +02:00
),
),
Direction::Right => (
Rectangle::from_loc_and_size(
(geo.loc.x + geo.size.w - 16, geo.loc.y),
(16, geo.size.h),
),
Rectangle::from_loc_and_size(
geo.loc,
(geo.size.w - 48, geo.size.h),
),
),
Direction::Down => (
Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + geo.size.h - 16),
(geo.size.w, 16),
),
Rectangle::from_loc_and_size(
geo.loc,
(geo.size.w, geo.size.h - 48),
),
),
};
2023-07-21 16:08:55 +02:00
if let Some(renderer) = renderer.as_mut() {
elements.push(
BackdropShader::element(
*renderer,
placeholder_id.clone(),
pill_geo,
8.,
alpha * 0.4,
group_color,
)
.into(),
);
}
geo = remaining_geo;
2023-05-12 20:01:37 +02:00
};
}
2023-05-12 20:01:37 +02:00
if matches!(swap_desc, Some(ref desc) if &desc.node == &node_id) {
if let Some(renderer) = renderer.as_mut() {
elements.push(
BackdropShader::element(
*renderer,
Key::Group(Arc::downgrade(alive)),
geo,
8.,
2023-09-14 15:27:05 +02:00
alpha
* if focused
.as_ref()
.map(|focused| {
focused == &swap_desc.as_ref().unwrap().node
})
.unwrap_or(false)
{
0.4
} else {
0.15
},
group_color,
)
.into(),
);
2023-05-12 20:01:37 +02:00
}
let swap_geo = swap_geometry(
geo.size.as_logical(),
focused_geo.unwrap_or({
let mut geo = non_exclusive_zone;
geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into();
geo.size -=
(WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into();
geo
}),
);
geo = ease(
Linear,
EaseRectangle(geo),
EaseRectangle(swap_geo),
transition,
)
.unwrap();
geometries.insert(node_id.clone(), geo);
};
2023-05-12 20:01:37 +02:00
let previous_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let new_length = match orientation {
Orientation::Horizontal => geo.size.h,
Orientation::Vertical => geo.size.w,
};
let mut sizes = sizes
.iter()
.map(|len| {
(((*len as f64) / (previous_length as f64)) * (new_length as f64))
.round() as i32
})
.collect::<Vec<_>>();
let sum: i32 = sizes.iter().sum();
if sum < new_length {
*sizes.last_mut().unwrap() += new_length - sum;
}
match orientation {
Orientation::Horizontal => {
let mut previous: i32 = sizes.iter().sum();
for (idx, size) in sizes.iter().enumerate().rev() {
previous -= *size;
let mut geo = Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + previous),
(geo.size.w, *size),
);
if mouse_tiling.is_some() {
if let Some(PillIndicator::Inner(pill_idx)) = pill_indicator {
if *pill_idx == idx {
geo.size.h -= 32;
}
if idx
.checked_sub(1)
.map(|idx| idx == *pill_idx)
.unwrap_or(false)
{
if let Some(renderer) = renderer.as_mut() {
elements.push(
BackdropShader::element(
*renderer,
placeholder_id.clone(),
Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y - 8),
(geo.size.w, 16),
),
8.,
alpha * 0.4,
group_color,
)
.into(),
);
2023-07-21 16:08:55 +02:00
}
geo.loc.y += 32;
geo.size.h -= 32;
2023-07-21 16:08:55 +02:00
}
}
2023-05-17 19:46:21 +02:00
}
stack.push((geo, depth + 1));
2023-05-17 19:46:21 +02:00
}
}
Orientation::Vertical => {
let mut previous: i32 = sizes.iter().sum();
for (idx, size) in sizes.iter().enumerate().rev() {
previous -= *size;
let mut geo = Rectangle::from_loc_and_size(
(geo.loc.x + previous, geo.loc.y),
(*size, geo.size.h),
);
if mouse_tiling.is_some() {
if let Some(PillIndicator::Inner(pill_idx)) = pill_indicator {
if *pill_idx == idx {
geo.size.w -= 32;
}
if idx
.checked_sub(1)
.map(|idx| idx == *pill_idx)
.unwrap_or(false)
2023-07-21 16:08:55 +02:00
{
if let Some(renderer) = renderer.as_mut() {
elements.push(
BackdropShader::element(
*renderer,
placeholder_id.clone(),
Rectangle::from_loc_and_size(
(geo.loc.x - 8, geo.loc.y),
(16, geo.size.h),
),
8.,
alpha * 0.4,
group_color,
)
.into(),
);
2023-07-21 16:08:55 +02:00
}
geo.loc.x += 32;
geo.size.w -= 32;
2023-07-21 16:08:55 +02:00
}
}
2023-05-17 19:46:21 +02:00
}
stack.push((geo, depth + 1));
2023-05-17 19:46:21 +02:00
}
}
}
}
Data::Mapped { mapped, .. } => {
geo.loc += (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_right, element_gap_down).into();
2023-07-21 16:08:55 +02:00
if let Some(renderer) = renderer.as_mut() {
if render_potential_group {
elements.push(
IndicatorShader::element(
*renderer,
Key::Window(Usage::PotentialGroupIndicator, mapped.clone()),
geo,
4,
8,
alpha * 0.40,
output_scale,
group_color,
)
.into(),
);
geo.loc += (gap, gap).into();
geo.size -= (gap * 2, gap * 2).into();
2023-07-21 16:08:55 +02:00
}
let accent = theme.accent.base;
if focused
.as_ref()
.map(|focused_id| {
!tree
.ancestor_ids(&node_id)
.unwrap()
.any(|id| id == focused_id)
})
.unwrap_or(mouse_tiling.is_some())
{
let color = match mouse_tiling {
Some(Some(TargetZone::WindowStack(stack_id, _)))
if *stack_id == node_id =>
{
[accent.red, accent.green, accent.blue]
}
_ => group_color,
};
2023-07-27 20:24:02 +02:00
geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into();
geo.size -=
(WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into();
2023-06-09 19:37:26 +02:00
elements.push(
BackdropShader::element(
2023-07-21 16:08:55 +02:00
*renderer,
Key::Window(Usage::OverviewBackdrop, mapped.clone()),
2023-06-09 19:37:26 +02:00
geo,
8.,
alpha
* if focused
.as_ref()
.map(|focused_id| focused_id == &node_id)
.unwrap_or(
color == [accent.red, accent.green, accent.blue],
)
{
0.4
} else {
0.15
},
color,
2023-06-09 19:37:26 +02:00
)
.into(),
);
}
2023-05-30 13:20:46 +02:00
geo.loc += (WINDOW_BACKDROP_GAP, WINDOW_BACKDROP_GAP).into();
geo.size -= (WINDOW_BACKDROP_GAP * 2, WINDOW_BACKDROP_GAP * 2).into();
2023-05-17 19:46:21 +02:00
}
if matches!(swap_desc, Some(ref desc) if &desc.node == &node_id && desc.stack_window.is_none())
{
let swap_geo = swap_geometry(
geo.size.as_logical(),
focused_geo.unwrap_or({
let mut geo = non_exclusive_zone;
geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into();
geo.size -=
(WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into();
geo
}),
);
geo = ease(
Linear,
EaseRectangle(geo),
EaseRectangle(swap_geo),
transition,
)
.unwrap();
};
geometries.insert(node_id.clone(), geo);
}
Data::Placeholder { .. } => {
geo.loc += (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_left, element_gap_up).into();
geo.size -= (element_gap_right, element_gap_down).into();
if let Some(renderer) = renderer.as_mut() {
geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into();
geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into();
elements.push(
BackdropShader::element(
*renderer,
placeholder_id.clone(),
geo,
8.,
alpha * 0.4,
group_color,
)
.into(),
);
}
geometries.insert(node_id.clone(), geo);
2023-05-17 19:46:21 +02:00
}
}
}
}
(geometries, elements)
2023-05-17 19:46:21 +02:00
}
fn render_old_tree<R>(
reference_tree: &Tree<Data>,
target_tree: &Tree<Data>,
renderer: &mut R,
geometries: Option<HashMap<NodeId, Rectangle<i32, Local>>>,
2023-05-17 19:46:21 +02:00
output_scale: f64,
percentage: f32,
2024-02-23 20:08:57 +01:00
indicator_thickness: u8,
is_swap_mode: bool,
2024-02-23 20:08:57 +01:00
theme: &cosmic::theme::CosmicTheme,
) -> (
Vec<CosmicMappedRenderElement<R>>,
Vec<CosmicMappedRenderElement<R>>,
)
2023-05-17 19:46:21 +02:00
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
2023-06-08 13:19:30 +02:00
CosmicStackRenderElement<R>: RenderElement<R>,
2023-05-17 19:46:21 +02:00
{
2024-02-23 20:08:57 +01:00
let window_hint = crate::theme::active_window_hint(theme);
let mut window_elements = Vec::new();
let mut popup_elements = Vec::new();
2023-05-17 19:46:21 +02:00
if let Some(root) = reference_tree.root_node_id() {
let geometries = geometries.unwrap_or_default();
reference_tree
.traverse_pre_order_ids(root)
.unwrap()
.filter(|node_id| reference_tree.get(node_id).unwrap().data().is_mapped(None))
.map(
|node_id| match reference_tree.get(&node_id).unwrap().data() {
Data::Mapped {
mapped,
last_geometry,
2024-02-23 20:08:57 +01:00
minimize_rect,
2023-05-17 19:46:21 +02:00
..
2024-02-23 20:08:57 +01:00
} => (
mapped,
last_geometry,
geometries.get(&node_id).copied(),
minimize_rect,
),
2023-05-17 19:46:21 +02:00
_ => unreachable!(),
},
)
2024-02-23 20:08:57 +01:00
.filter(|(mapped, _, _, _)| {
2023-05-17 19:46:21 +02:00
if let Some(root) = target_tree.root_node_id() {
is_swap_mode
|| !target_tree
.traverse_pre_order(root)
.unwrap()
.any(|node| node.data().is_mapped(Some(mapped)))
2023-05-17 19:46:21 +02:00
} else {
true
}
})
2024-02-23 20:08:57 +01:00
.for_each(|(mapped, original_geo, mut scaled_geo, minimize_geo)| {
if let Some(minimize_geo) = minimize_geo {
scaled_geo = Some(
ease(
EaseInOutCubic,
EaseRectangle(*original_geo),
EaseRectangle(*minimize_geo),
percentage,
)
.unwrap(),
);
}
2023-05-17 19:46:21 +02:00
let (scale, offset) = scaled_geo
2024-02-23 20:08:57 +01:00
.map(|adapted_geo| scale_to_center(&original_geo, &adapted_geo))
2023-05-17 19:46:21 +02:00
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
let geo = scaled_geo
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
)
})
.unwrap_or(*original_geo);
let original_location = original_geo
.loc
.as_logical()
.to_physical_precise_round(output_scale)
2023-05-17 19:46:21 +02:00
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale);
2024-02-23 20:08:57 +01:00
let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale);
let (w_elements, p_elements) = mapped
.split_render_elements::<R, CosmicMappedRenderElement<R>>(
renderer,
original_location,
Scale::from(output_scale),
1.0 - percentage,
);
2024-02-23 20:08:57 +01:00
window_elements.extend(w_elements.into_iter().flat_map(|element| {
match element {
CosmicMappedRenderElement::Stack(elem) => constrain_render_elements(
std::iter::once(elem),
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
geo.as_logical().to_physical_precise_round(output_scale),
elem_geometry,
ConstrainScaleBehavior::Stretch,
ConstrainAlign::CENTER,
output_scale,
2024-02-23 20:08:57 +01:00
)
.next()
.map(CosmicMappedRenderElement::TiledStack),
CosmicMappedRenderElement::Window(elem) => constrain_render_elements(
std::iter::once(elem),
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
geo.as_logical().to_physical_precise_round(output_scale),
elem_geometry,
ConstrainScaleBehavior::Stretch,
ConstrainAlign::CENTER,
output_scale,
2024-02-23 20:08:57 +01:00
)
.next()
.map(CosmicMappedRenderElement::TiledWindow),
x => Some(x),
2024-02-23 20:08:57 +01:00
}
}));
2024-02-23 20:08:57 +01:00
if minimize_geo.is_some() && indicator_thickness > 0 {
window_elements.push(CosmicMappedRenderElement::FocusIndicator(
IndicatorShader::focus_element(
renderer,
Key::Window(Usage::FocusIndicator, mapped.clone().into()),
geo,
indicator_thickness,
output_scale,
1.0 - percentage,
[window_hint.red, window_hint.green, window_hint.blue],
),
));
}
popup_elements.extend(p_elements);
});
2023-05-17 19:46:21 +02:00
}
(window_elements, popup_elements)
2023-05-17 19:46:21 +02:00
}
fn render_new_tree<R>(
target_tree: &Tree<Data>,
reference_tree: Option<&Tree<Data>>,
renderer: &mut R,
non_exclusive_zone: Rectangle<i32, Local>,
geometries: Option<HashMap<NodeId, Rectangle<i32, Local>>>,
old_geometries: Option<HashMap<NodeId, Rectangle<i32, Local>>>,
2023-08-31 18:29:20 +02:00
is_overview: bool,
2023-05-30 21:02:19 +02:00
seat: Option<&Seat<State>>,
2023-07-06 00:03:26 +02:00
output: &Output,
2023-05-17 19:46:21 +02:00
percentage: f32,
transition: Option<f32>,
2023-05-17 19:46:21 +02:00
indicator_thickness: u8,
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
2023-07-06 00:03:26 +02:00
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
swap_desc: Option<NodeDesc>,
swapping_stack_surface_id: &Id,
placeholder_id: &Id,
theme: &cosmic::theme::CosmicTheme,
) -> (
Vec<CosmicMappedRenderElement<R>>,
Vec<CosmicMappedRenderElement<R>>,
)
2023-05-17 19:46:21 +02:00
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
2023-06-08 13:19:30 +02:00
CosmicStackRenderElement<R>: RenderElement<R>,
2023-05-17 19:46:21 +02:00
{
2023-05-30 21:02:19 +02:00
let focused = seat
.and_then(|seat| {
seat.get_keyboard()
.unwrap()
.current_focus()
.and_then(|target| TilingLayout::currently_focused_node(&target_tree, target))
})
2023-05-30 21:02:19 +02:00
.map(|(id, _)| id);
let focused_geo = if let Some(focused) = focused.as_ref() {
geometries
.as_ref()
.and_then(|geometries| geometries.get(focused))
.or_else(|| {
target_tree
.get(focused)
.ok()
.map(|node| node.data().geometry())
})
.cloned()
} else {
None
}
.unwrap_or({
let mut geo = non_exclusive_zone;
geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into();
geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into();
geo
});
let is_active_output = seat
.map(|seat| &seat.active_output() == output)
.unwrap_or(false);
2023-05-30 21:02:19 +02:00
let mut animating_window_elements = Vec::new();
let mut window_elements = Vec::new();
let mut popup_elements = Vec::new();
2023-06-09 19:37:26 +02:00
let mut group_backdrop = None;
let mut indicators = Vec::new();
2023-07-06 00:03:26 +02:00
let mut resize_elements = None;
let mut swap_elements = Vec::new();
2023-07-06 00:03:26 +02:00
let output_geo = output.geometry();
let output_scale = output.current_scale().fractional_scale();
2023-06-09 19:37:26 +02:00
let (swap_indicator, swap_tree) = overview.1.unzip();
let swap_tree = swap_tree.flatten().filter(|_| is_active_output);
let swap_desc = swap_desc.filter(|_| is_active_output);
let window_hint = crate::theme::active_window_hint(theme);
let group_color = group_color(theme);
// render placeholder, if we are swapping to an empty workspace
if target_tree.root_node_id().is_none() && swap_desc.is_some() {
window_elements.push(
BackdropShader::element(
renderer,
placeholder_id.clone(),
focused_geo,
8.,
transition.unwrap_or(1.0) * 0.4,
group_color,
)
.into(),
);
}
// render single stack window when swapping separately
if let Some(window) = swap_desc
.as_ref()
.and_then(|desc| desc.stack_window.clone())
{
let window_geo = window.geometry();
let swap_geo = ease(
Linear,
EaseRectangle({
let mut geo = focused_geo.clone();
geo.loc.x += STACK_TAB_HEIGHT;
geo.size.h -= STACK_TAB_HEIGHT;
geo
}),
EaseRectangle(swap_geometry(window_geo.size, focused_geo)),
transition.unwrap_or(1.0),
)
.unwrap();
indicators.push(IndicatorShader::focus_element(
renderer,
Key::Static(swapping_stack_surface_id.clone()),
swap_geo,
4,
output_scale,
transition.unwrap_or(1.0),
[window_hint.red, window_hint.green, window_hint.blue],
));
let render_loc =
(swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale);
swap_elements.extend(
window
.render_elements::<CosmicWindowRenderElement<R>>(
renderer,
render_loc,
output_scale.into(),
1.0,
)
.into_iter()
.map(|window| {
CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element(
window,
swap_geo
.loc
.as_logical()
.to_physical_precise_round(output_scale),
ease(
Linear,
1.0,
swap_factor(window_geo.size),
transition.unwrap_or(1.0),
),
))
}),
)
}
// render actual tree nodes
let old_geometries = old_geometries.unwrap_or_default();
let geometries = geometries.unwrap_or_default();
target_tree
.root_node_id()
.into_iter()
.flat_map(|root| target_tree.traverse_pre_order_ids(root).unwrap())
.map(|id| (target_tree, id))
.chain(
swap_tree
.into_iter()
.flat_map(|tree| {
let sub_root = &swap_desc.as_ref().unwrap().node;
if swap_desc.as_ref().unwrap().stack_window.is_none() {
Some(
tree.traverse_pre_order_ids(sub_root)
.unwrap()
.map(move |id| (tree, id)),
)
} else {
None
}
})
.flatten(),
)
.for_each(|(target_tree, node_id)| {
let data = target_tree.get(&node_id).unwrap().data();
let (original_geo, scaled_geo) = (data.geometry(), geometries.get(&node_id));
let (old_original_geo, old_scaled_geo) =
if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
reference_tree
.traverse_pre_order_ids(root)
.unwrap()
.find(|id| &node_id == id)
.map(|node_id| {
(
reference_tree.get(&node_id).unwrap().data().geometry(),
old_geometries.get(&node_id),
)
})
2023-05-17 19:46:21 +02:00
} else {
None
}
} else {
None
}
.unzip();
2024-02-23 20:08:57 +01:00
let mut old_geo = old_original_geo.map(|original_geo| {
let (scale, offset) = old_scaled_geo
.unwrap()
2023-05-17 19:46:21 +02:00
.map(|adapted_geo| scale_to_center(original_geo, adapted_geo))
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
2024-02-23 20:08:57 +01:00
(
old_scaled_geo
.unwrap()
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
)
})
.unwrap_or(*original_geo),
1.0,
)
});
2023-05-17 19:46:21 +02:00
2024-02-23 20:08:57 +01:00
if let Data::Mapped {
minimize_rect: Some(minimize_rect),
..
} = &data
{
old_geo = Some((*minimize_rect, percentage));
}
let (scale, offset) = scaled_geo
.map(|adapted_geo| scale_to_center(original_geo, adapted_geo))
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
let new_geo = scaled_geo
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
2023-05-17 19:46:21 +02:00
)
})
.unwrap_or(*original_geo);
2023-05-17 19:46:21 +02:00
2024-02-23 20:08:57 +01:00
let (geo, alpha, animating) = if let Some((old_geo, alpha)) = old_geo.filter(|_| {
swap_desc
.as_ref()
.map(|desc| desc.node != node_id && desc.stack_window.is_none())
.unwrap_or(true)
}) {
(
ease(
Linear,
EaseRectangle(old_geo),
EaseRectangle(new_geo),
percentage,
)
.unwrap(),
2024-02-23 20:08:57 +01:00
alpha,
old_geo != new_geo,
)
} else {
(new_geo, percentage, false)
};
2023-06-09 19:37:26 +02:00
if swap_desc.as_ref().map(|desc| &desc.node) == Some(&node_id)
|| focused.as_ref() == Some(&node_id)
{
if indicator_thickness > 0 || data.is_group() {
let mut geo = geo.clone();
if data.is_group() {
let outer_gap: i32 = (if is_overview { GAP_KEYBOARD } else { 4 } as f32
* percentage)
.round() as i32;
geo.loc += (outer_gap, outer_gap).into();
geo.size -= (outer_gap * 2, outer_gap * 2).into();
group_backdrop = Some(BackdropShader::element(
renderer,
match data {
Data::Group { alive, .. } => Key::Group(Arc::downgrade(alive)),
_ => unreachable!(),
},
geo,
8.,
0.4,
group_color,
));
}
if !swap_desc
.as_ref()
.map(|desc| desc.stack_window.is_some())
.unwrap_or(false)
|| focused.as_ref() == Some(&node_id)
{
indicators.push(IndicatorShader::focus_element(
renderer,
match data {
Data::Mapped { mapped, .. } => {
Key::Window(Usage::FocusIndicator, mapped.clone().into())
}
Data::Group { alive, .. } => Key::Group(Arc::downgrade(alive)),
_ => unreachable!(),
},
geo,
if data.is_group() {
4
} else {
indicator_thickness
},
output_scale,
2024-02-23 20:08:57 +01:00
alpha,
[window_hint.red, window_hint.green, window_hint.blue],
));
}
if focused.as_ref() == Some(&node_id)
2023-09-14 16:18:13 +02:00
&& (swap_desc.as_ref().map(|desc| &desc.node) != Some(&node_id)
|| swap_desc
.as_ref()
.and_then(|swap_desc| swap_desc.stack_window.as_ref())
.zip(focused.as_ref())
.map(|(stack_window, focused_id)| {
target_tree
.get(focused_id)
.ok()
.map(|focused| match focused.data() {
Data::Mapped { mapped, .. } => mapped
.stack_ref()
.map(|stack| &stack.active() != stack_window)
.unwrap_or(false),
_ => unreachable!(),
})
.unwrap_or(false)
})
.unwrap_or(false))
{
if let Some(swap) = swap_indicator.as_ref() {
swap.resize(geo.size.as_logical());
swap.output_enter(output, output_geo.as_logical());
swap_elements.extend(
swap.render_elements::<CosmicWindowRenderElement<R>>(
renderer,
geo.loc.as_logical().to_physical_precise_round(output_scale),
output_scale.into(),
alpha * overview.0.alpha().unwrap_or(1.0),
)
.into_iter()
.map(CosmicMappedRenderElement::from),
);
}
}
2023-05-17 19:46:21 +02:00
}
2023-05-12 20:01:37 +02:00
if let Some((mode, resize)) = resize_indicator.as_mut() {
let mut geo = geo.clone();
geo.loc -= (18, 18).into();
geo.size += (36, 36).into();
resize.resize(geo.size.as_logical());
resize.output_enter(output, output_geo.as_logical());
let possible_edges =
TilingLayout::possible_resizes(target_tree, node_id.clone());
if !possible_edges.is_empty() {
if resize.with_program(|internal| {
let mut edges = internal.edges.lock().unwrap();
if *edges != possible_edges {
*edges = possible_edges;
true
} else {
false
}
}) {
resize.force_update();
}
resize_elements = Some(
resize
.render_elements::<CosmicWindowRenderElement<R>>(
renderer,
geo.loc.as_logical().to_physical_precise_round(output_scale),
output_scale.into(),
alpha * mode.alpha().unwrap_or(1.0),
)
.into_iter()
.map(CosmicMappedRenderElement::from)
.collect::<Vec<_>>(),
);
}
}
}
if let Data::Mapped { mapped, .. } = data {
2024-02-23 20:08:57 +01:00
let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale);
let (mut w_elements, p_elements) = mapped
.split_render_elements::<R, CosmicMappedRenderElement<R>>(
renderer,
2024-02-23 20:08:57 +01:00
//original_location,
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
Scale::from(output_scale),
alpha,
);
if swap_desc
.as_ref()
2023-09-14 16:18:13 +02:00
.filter(|swap_desc| swap_desc.node == node_id)
.and_then(|swap_desc| swap_desc.stack_window.as_ref())
.zip(focused.as_ref())
.map(|(stack_window, focused_id)| {
target_tree
.get(focused_id)
.ok()
.map(|focused| match focused.data() {
Data::Mapped { mapped, .. } => mapped
.stack_ref()
.map(|stack| &stack.active() == stack_window)
.unwrap_or(false),
_ => unreachable!(),
})
.unwrap_or(false)
})
.unwrap_or(false)
{
let mut geo = mapped.active_window_geometry().as_local();
geo.loc += original_geo.loc;
w_elements.insert(
0,
CosmicMappedRenderElement::Overlay(BackdropShader::element(
renderer,
Key::Window(Usage::Overlay, mapped.clone()),
geo,
0.0,
0.3,
group_color,
)),
)
}
let w_elements = w_elements.into_iter().flat_map(|element| match element {
2024-02-23 20:08:57 +01:00
CosmicMappedRenderElement::Stack(elem) => constrain_render_elements(
std::iter::once(elem),
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
geo.as_logical().to_physical_precise_round(output_scale),
elem_geometry,
ConstrainScaleBehavior::Stretch,
ConstrainAlign::CENTER,
output_scale,
2024-02-23 20:08:57 +01:00
)
.next()
.map(CosmicMappedRenderElement::TiledStack),
CosmicMappedRenderElement::Window(elem) => constrain_render_elements(
std::iter::once(elem),
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
geo.as_logical().to_physical_precise_round(output_scale),
elem_geometry,
ConstrainScaleBehavior::Stretch,
ConstrainAlign::CENTER,
output_scale,
2024-02-23 20:08:57 +01:00
)
.next()
.map(CosmicMappedRenderElement::TiledWindow),
CosmicMappedRenderElement::Overlay(elem) => constrain_render_elements(
std::iter::once(elem),
geo.loc.as_logical().to_physical_precise_round(output_scale)
- elem_geometry.loc,
geo.as_logical().to_physical_precise_round(output_scale),
elem_geometry,
ConstrainScaleBehavior::Stretch,
ConstrainAlign::CENTER,
output_scale,
2024-02-23 20:08:57 +01:00
)
.next()
.map(CosmicMappedRenderElement::TiledOverlay),
x => Some(x),
});
if swap_desc
.as_ref()
.map(|swap_desc| {
(&swap_desc.node == &node_id
|| target_tree
.ancestor_ids(&node_id)
.unwrap()
.any(|id| &swap_desc.node == id))
&& swap_desc.stack_window.is_none()
})
.unwrap_or(false)
{
swap_elements.extend(w_elements);
} else {
if animating {
animating_window_elements.extend(w_elements);
} else {
window_elements.extend(w_elements);
}
if !mapped.is_maximized(false) {
popup_elements.extend(p_elements);
}
2023-05-30 21:02:19 +02:00
}
}
});
2023-05-30 21:02:19 +02:00
window_elements = resize_elements
.into_iter()
.flatten()
.chain(swap_elements)
.chain(indicators.into_iter().map(Into::into))
.chain(window_elements)
.chain(animating_window_elements)
.chain(group_backdrop.into_iter().map(Into::into))
.collect();
(window_elements, popup_elements)
2023-05-17 19:46:21 +02:00
}
2023-05-12 20:01:37 +02:00
fn scale_to_center<C>(
old_geo: &Rectangle<i32, C>,
new_geo: &Rectangle<i32, C>,
) -> (f64, Point<i32, C>) {
2023-05-17 19:46:21 +02:00
let scale_w = new_geo.size.w as f64 / old_geo.size.w as f64;
let scale_h = new_geo.size.h as f64 / old_geo.size.h as f64;
if scale_w > scale_h {
(
scale_h,
(
((new_geo.size.w as f64 - old_geo.size.w as f64 * scale_h) / 2.0).round() as i32,
0,
)
.into(),
)
} else {
(
scale_w,
(
0,
((new_geo.size.h as f64 - old_geo.size.h as f64 * scale_w) / 2.0).round() as i32,
)
.into(),
)
2022-09-28 12:01:29 +02:00
}
}