diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 03a3cf62..dbf4f8c4 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -5,7 +5,7 @@ use crate::{ shell::{ focus::target::PointerFocusTarget, grabs::{GrabStartData, ReleaseMode}, - layout::Orientation, + layout::{tiling::ResizeTarget, Orientation}, }, utils::prelude::*, }; @@ -83,6 +83,11 @@ impl PointerTarget for ResizeForkTarget { data.common.event_loop_handle.insert_idle(move |state| { let pointer = seat.get_pointer().unwrap(); let location = pointer.current_location(); + let request = match orientation { + // NOTE: vertical split means we are resizing horizontially + Orientation::Horizontal => ResizeTarget::new_vertical(node, left_up_idx), + Orientation::Vertical => ResizeTarget::new_horizonital(node, left_up_idx), + }; pointer.set_grab( state, ResizeForkGrab::new( @@ -92,9 +97,7 @@ impl PointerTarget for ResizeForkTarget { location, }), location.as_global(), - node, - left_up_idx, - orientation, + request, output, ReleaseMode::NoMouseButtons, ), @@ -137,6 +140,11 @@ impl TouchTarget for ResizeForkTarget { let location = event.location; data.common.event_loop_handle.insert_idle(move |state| { let touch = seat.get_touch().unwrap(); + let request = match orientation { + // NOTE: vertical split means we are resizing horizontially + Orientation::Horizontal => ResizeTarget::new_vertical(node, left_up_idx), + Orientation::Vertical => ResizeTarget::new_horizonital(node, left_up_idx), + }; touch.set_grab( state, ResizeForkGrab::new( @@ -146,9 +154,7 @@ impl TouchTarget for ResizeForkTarget { location, }), location.as_global(), - node, - left_up_idx, - orientation, + request, output, ReleaseMode::NoMouseButtons, ), @@ -183,11 +189,10 @@ pub struct ResizeForkGrab { start_data: GrabStartData, last_loc: Point, old_tree: Option>, - accumulated_delta: f64, - node: NodeId, + accumulated_delta_left: f64, + accumulated_delta_up: f64, + target: ResizeTarget, output: WeakOutput, - left_up_idx: usize, - orientation: Orientation, release: ReleaseMode, } @@ -195,9 +200,7 @@ impl ResizeForkGrab { pub fn new( start_data: GrabStartData, pointer_loc: Point, - node: NodeId, - idx: usize, - orientation: Orientation, + request: ResizeTarget, output: WeakOutput, release: ReleaseMode, ) -> ResizeForkGrab { @@ -205,12 +208,11 @@ impl ResizeForkGrab { start_data, last_loc: pointer_loc, old_tree: None, - accumulated_delta: 0.0, - node, - output, - left_up_idx: idx, - orientation, + accumulated_delta_left: 0.0, + accumulated_delta_up: 0.0, + target: request, release, + output, } } } @@ -253,7 +255,8 @@ impl ResizeForkGrab { if !equal { *old_tree = tree.copy_clone(); - self.accumulated_delta = 0.0; + self.accumulated_delta_left = 0.0; + self.accumulated_delta_up = 0.0; } else { *tree = old_tree.copy_clone(); } @@ -262,66 +265,22 @@ impl ResizeForkGrab { *x = Some(tree.copy_clone()); } }; - if tree.get(&self.node).is_ok() { - let delta = match self.orientation { - Orientation::Vertical => delta.x, - Orientation::Horizontal => delta.y, - } - .round(); - self.accumulated_delta += delta; - - // check that we are still alive - let mut iter = tree - .children_ids(&self.node) - .unwrap() - .skip(self.left_up_idx); - let first_elem = iter.next(); - let second_elem = iter.next(); - if first_elem.is_none() || second_elem.is_none() { - return true; - }; - match tree.get_mut(&self.node).unwrap().data_mut() { - Data::Group { - sizes, orientation, .. - } => { - if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1] - < match orientation { - Orientation::Vertical => 720, - Orientation::Horizontal => 480, - } - { - return false; - }; - - let old_size = sizes[self.left_up_idx]; - sizes[self.left_up_idx] = (old_size - + self.accumulated_delta.round() as i32) - .max(if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }); - let diff = old_size - sizes[self.left_up_idx]; - let next_size = sizes[self.left_up_idx + 1] + diff; - sizes[self.left_up_idx + 1] = - next_size.max(if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }); - let next_diff = next_size - sizes[self.left_up_idx + 1]; - sizes[self.left_up_idx] += next_diff; - } - _ => unreachable!(), - } + self.accumulated_delta_left += delta.x.round(); + self.accumulated_delta_up += delta.y.round(); - self.last_loc = location.as_global(); - let blocker = TilingLayout::update_positions(&output, tree, gaps); - tiling_layer.pending_blockers.extend(blocker); - } else { - return true; + if let Some((left_node, left_idx)) = &self.target.left_node_idx { + perform_fork_grab_resize(tree, left_node, *left_idx, self.accumulated_delta_left); } + if let Some((up_node, up_idx)) = &self.target.up_node_idx { + perform_fork_grab_resize(tree, up_node, *up_idx, self.accumulated_delta_up); + } + + self.last_loc = location.as_global(); + let blocker = TilingLayout::update_positions(&output, tree, gaps); + tiling_layer.pending_blockers.extend(blocker); + } else { + return true; } false } @@ -557,3 +516,58 @@ impl TouchGrab for ResizeForkGrab { fn unset(&mut self, _data: &mut State) {} } + +fn perform_fork_grab_resize( + tree: &mut Tree, + node: &NodeId, + left_up_idx: usize, + delta: f64, +) -> bool { + if tree.get(&node).is_ok() { + // check that we are still alive + let mut iter = tree.children_ids(node).unwrap().skip(left_up_idx); + let first_elem = iter.next(); + let second_elem = iter.next(); + if first_elem.is_none() || second_elem.is_none() { + return true; + }; + + let node = tree.get_mut(node).unwrap(); + + match node.data_mut() { + Data::Group { + sizes, orientation, .. + } => { + if sizes[left_up_idx] + sizes[left_up_idx + 1] + < match orientation { + Orientation::Vertical => 720, + Orientation::Horizontal => 480, + } + { + return false; + }; + + let old_size = sizes[left_up_idx]; + sizes[left_up_idx] = (old_size + delta.round() as i32).max( + if *orientation == Orientation::Vertical { + 360 + } else { + 240 + }, + ); + let diff = old_size - sizes[left_up_idx]; + let next_size = sizes[left_up_idx + 1] + diff; + sizes[left_up_idx + 1] = next_size.max(if *orientation == Orientation::Vertical { + 360 + } else { + 240 + }); + let next_diff = next_size - sizes[left_up_idx + 1]; + sizes[left_up_idx] += next_diff; + } + _ => unreachable!(), + }; + } + + return true; +} diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index feeebcd8..bba11eb6 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -341,6 +341,30 @@ pub struct MinimizedTilingState { pub sizes: Vec, } +#[derive(Default)] +pub struct ResizeTarget { + pub left_node_idx: Option<(NodeId, usize)>, // node ID of group, and the child inside index inside it to resize + pub up_node_idx: Option<(NodeId, usize)>, +} + +impl ResizeTarget { + pub fn new_horizonital(group: NodeId, child_idx: usize) -> Self { + Self { + left_node_idx: Some((group, child_idx)), + up_node_idx: None, + } + } + pub fn new_vertical(group: NodeId, child_idx: usize) -> Self { + Self { + left_node_idx: None, + up_node_idx: Some((group, child_idx)), + } + } + pub fn is_empty(&self) -> bool { + self.left_node_idx.is_none() && self.up_node_idx.is_none() + } +} + impl TilingLayout { pub fn new(theme: cosmic::Theme, output: &Output) -> TilingLayout { TilingLayout { @@ -2463,41 +2487,78 @@ impl TilingLayout { edges } - pub fn resize_request( - &self, - mut node_id: NodeId, - edge: ResizeEdge, - ) -> Option<(NodeId, usize, Orientation)> { + pub fn resize_request(&self, window_node_id: NodeId, edge: ResizeEdge) -> ResizeTarget { let tree = self.tree(); + // need to find two NodeIds and associated index + // that indicate the groups and then the index that needs to be resized inside each one + // the one that's closer to the root is the "upper" one, and the one closer to the hovered window is the "lower" one - 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) + let mut try_lower_node_id = window_node_id; + while let Some(try_lower_group_id) = tree.get(&try_lower_node_id).unwrap().parent().cloned() + { + let orientation = tree.get(&try_lower_group_id).unwrap().data().orientation(); + let lower_node_idx_in_group = tree + .children_ids(&try_lower_group_id) .unwrap() - .position(|id| id == &node_id) + .position(|id| id == &try_lower_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)); + let lower_total = tree.children_ids(&try_lower_group_id).unwrap().count(); + + if let Some(left_up_idx) = + resize_edge_to_left_up_idx(orientation, lower_node_idx_in_group, lower_total, edge) + { + let lower_node_idx = Some((try_lower_group_id.clone(), left_up_idx)); // found a lower group id! + + // need to find closest parent that + // 1. is the opposite orientation as the lower group we found + // 2. can be resized in the direction we want + let mut try_upper_node_id = try_lower_group_id; + let mut parent_left_up_node_idx = None; + while let Some(try_upper_group_id) = + tree.get(&try_upper_node_id).unwrap().parent().cloned() + { + // ensure meets condition (1) + if tree.get(&try_upper_group_id).unwrap().data().orientation() != orientation { + let upper_node_idx_in_group = tree + .children_ids(&try_upper_group_id) + .unwrap() + .position(|id| id == &try_upper_node_id) + .unwrap(); + let upper_total = tree.children_ids(&try_upper_group_id).unwrap().count(); + + // ensure meets condition (2) + if let Some(idx) = resize_edge_to_left_up_idx( + !orientation, + upper_node_idx_in_group, + upper_total, + edge, + ) { + parent_left_up_node_idx = Some((try_upper_group_id.clone(), idx)); // found an upper group id! + break; + } + } + try_upper_node_id = try_upper_group_id; } + + return match orientation { + Orientation::Horizontal => ResizeTarget { + left_node_idx: parent_left_up_node_idx, + up_node_idx: lower_node_idx, + }, + Orientation::Vertical => ResizeTarget { + left_node_idx: lower_node_idx, + up_node_idx: parent_left_up_node_idx, + }, + }; } - node_id = group_id; + try_lower_node_id = try_lower_group_id; } - None + ResizeTarget { + left_node_idx: None, + up_node_idx: None, + } } pub fn resize( @@ -5413,3 +5474,28 @@ fn scale_to_center( ) } } + +fn resize_edge_to_left_up_idx( + orientation: Orientation, + node_idx: usize, + total: usize, + edge: ResizeEdge, +) -> Option { + if orientation == Orientation::Vertical { + if node_idx > 0 && edge.contains(ResizeEdge::LEFT) { + return Some(node_idx - 1); + } + if node_idx < total - 1 && edge.contains(ResizeEdge::RIGHT) { + return Some(node_idx); + } + } else { + if node_idx > 0 && edge.contains(ResizeEdge::TOP) { + return Some(node_idx - 1); + } + if node_idx < total - 1 && edge.contains(ResizeEdge::BOTTOM) { + return Some(node_idx); + } + } + + None +} diff --git a/src/shell/mod.rs b/src/shell/mod.rs index c32f5cc5..612fbd46 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3262,13 +3262,14 @@ impl Shell { grab.into() } else if let Some(ws) = self.space_for_mut(&mapped) { let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; - let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?; + let target = ws.tiling_layer.resize_request(node_id, edge); + if target.is_empty() { + return None; + } ResizeForkGrab::new( start_data, new_loc.to_f64(), - node, - left_up_idx, - orientation, + target, ws.output.downgrade(), ReleaseMode::Click, ) @@ -3464,14 +3465,15 @@ impl Shell { grab.into() } else if let Some(ws) = self.space_for_mut(&mapped) { let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; - let (node, left_up_idx, orientation) = - ws.tiling_layer.resize_request(node_id, edges)?; + let target = + ws.tiling_layer.resize_request(node_id, edges); + if target.is_empty() { + return None; + } ResizeForkGrab::new( start_data, seat.get_pointer().unwrap().current_location().as_global(), - node, - left_up_idx, - orientation, + target, ws.output.downgrade(), ReleaseMode::NoMouseButtons, )