shell: Introduce MoveResult to implement stacking

This commit is contained in:
Victoria Brekenfeld 2023-06-12 17:23:54 +02:00
parent f00753071e
commit b400939dd9
3 changed files with 175 additions and 63 deletions

View file

@ -6,7 +6,7 @@ use crate::{
focus::{target::PointerFocusTarget, FocusDirection}, focus::{target::PointerFocusTarget, FocusDirection},
layout::{ layout::{
floating::SeatMoveGrabState, floating::SeatMoveGrabState,
tiling::{Direction, FocusResult}, tiling::{Direction, FocusResult, MoveResult},
}, },
OverviewMode, Workspace, OverviewMode, Workspace,
}, // shell::grabs::SeatMoveGrabState }, // shell::grabs::SeatMoveGrabState
@ -1048,53 +1048,55 @@ impl State {
return; // TODO, is this what we want? How do we indicate the switch? return; // TODO, is this what we want? How do we indicate the switch?
} }
if let Some(_move_further) = match workspace.tiling_layer.move_current_node(direction, seat) {
workspace.tiling_layer.move_current_node(direction, seat) MoveResult::MoveFurther(_move_further) => {
{ match (direction, self.common.config.static_conf.workspace_layout) {
// TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead) (Direction::Left, WorkspaceLayout::Horizontal)
match (direction, self.common.config.static_conf.workspace_layout) { | (Direction::Up, WorkspaceLayout::Vertical) => self.handle_action(
(Direction::Left, WorkspaceLayout::Horizontal) Action::MoveToPreviousWorkspace,
| (Direction::Up, WorkspaceLayout::Vertical) => self.handle_action( seat,
Action::MoveToPreviousWorkspace, serial,
seat, time,
serial, mods,
time, Some(direction),
mods, ),
Some(direction), (Direction::Right, WorkspaceLayout::Horizontal)
), | (Direction::Down, WorkspaceLayout::Vertical) => self.handle_action(
(Direction::Right, WorkspaceLayout::Horizontal) Action::MoveToNextWorkspace,
| (Direction::Down, WorkspaceLayout::Vertical) => self.handle_action( seat,
Action::MoveToNextWorkspace, serial,
seat, time,
serial, mods,
time, Some(direction),
mods, ),
Some(direction), (Direction::Left, WorkspaceLayout::Vertical)
), | (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action(
(Direction::Left, WorkspaceLayout::Vertical) Action::MoveToPreviousOutput,
| (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action( seat,
Action::MoveToPreviousOutput, serial,
seat, time,
serial, mods,
time, Some(direction),
mods, ),
Some(direction), (Direction::Right, WorkspaceLayout::Vertical)
), | (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action(
(Direction::Right, WorkspaceLayout::Vertical) Action::MoveToNextOutput,
| (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action( seat,
Action::MoveToNextOutput, serial,
seat, time,
serial, mods,
time, Some(direction),
mods, ),
Some(direction), }
),
} }
} else { MoveResult::ShiftFocus(shift) => {
let focus_stack = workspace.focus_stack.get(seat); Common::set_focus(self, Some(&shift), seat, None);
if let Some(focused_window) = focus_stack.last() { }
if workspace.is_tiled(focused_window) { MoveResult::Done => {
self.common.shell.set_overview_mode(Some(mods)); if let Some(focused_window) = workspace.focus_stack.get(seat).last() {
if workspace.is_tiled(focused_window) {
self.common.shell.set_overview_mode(Some(mods));
}
} }
} }
} }

View file

@ -48,6 +48,7 @@ use std::{
}; };
pub mod surface; pub mod surface;
use self::stack::MoveResult;
pub use self::surface::CosmicSurface; pub use self::surface::CosmicSurface;
pub mod stack; pub mod stack;
pub use self::stack::CosmicStack; pub use self::stack::CosmicStack;
@ -61,7 +62,10 @@ use smithay::backend::renderer::{element::texture::TextureRenderElement, gles::G
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use tracing::debug; use tracing::debug;
use super::{focus::FocusDirection, layout::floating::ResizeState}; use super::{
focus::FocusDirection,
layout::{floating::ResizeState, tiling::Direction},
};
space_elements! { space_elements! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -226,6 +230,14 @@ impl CosmicMapped {
}) })
} }
pub fn handle_move(&self, direction: Direction) -> MoveResult {
if let CosmicMappedInternal::Stack(stack) = &self.element {
stack.handle_move(direction)
} else {
MoveResult::Default
}
}
pub fn handle_focus(&self, direction: FocusDirection) -> bool { pub fn handle_focus(&self, direction: FocusDirection) -> bool {
if let CosmicMappedInternal::Stack(stack) = &self.element { if let CosmicMappedInternal::Stack(stack) = &self.element {
stack.handle_focus(direction) stack.handle_focus(direction)
@ -453,6 +465,13 @@ impl CosmicMapped {
} }
} }
pub fn stack_ref_mut(&mut self) -> Option<&mut CosmicStack> {
match &mut self.element {
CosmicMappedInternal::Stack(stack) => Some(stack),
_ => None,
}
}
pub fn convert_to_stack<'a>( pub fn convert_to_stack<'a>(
&mut self, &mut self,
outputs: impl Iterator<Item = (&'a Output, Rectangle<i32, Logical>)>, outputs: impl Iterator<Item = (&'a Output, Rectangle<i32, Logical>)>,
@ -460,7 +479,7 @@ impl CosmicMapped {
match &self.element { match &self.element {
CosmicMappedInternal::Window(window) => { CosmicMappedInternal::Window(window) => {
let surface = window.surface(); let surface = window.surface();
let activated = surface.is_activated(); let activated = surface.is_activated(true);
let handle = window.loop_handle(); let handle = window.loop_handle();
let stack = CosmicStack::new(std::iter::once(surface), handle); let stack = CosmicStack::new(std::iter::once(surface), handle);
@ -494,7 +513,7 @@ impl CosmicMapped {
for (output, overlap) in outputs { for (output, overlap) in outputs {
window.output_enter(output, overlap); window.output_enter(output, overlap);
} }
window.set_activate(self.is_activated()); window.set_activate(self.is_activated(true));
window.surface().send_configure(); window.surface().send_configure();
window.refresh(); window.refresh();

View file

@ -4,8 +4,9 @@ use crate::{
backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, GROUP_COLOR}, backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, GROUP_COLOR},
shell::{ shell::{
element::{ element::{
stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped, stack::{CosmicStackRenderElement, MoveResult as StackMoveResult},
CosmicMappedRenderElement, CosmicStack, CosmicWindow, window::CosmicWindowRenderElement,
CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow,
}, },
focus::{ focus::{
target::{KeyboardFocusTarget, WindowGroup}, target::{KeyboardFocusTarget, WindowGroup},
@ -101,6 +102,13 @@ pub enum FocusResult {
Some(KeyboardFocusTarget), Some(KeyboardFocusTarget),
} }
#[derive(Debug, Clone, PartialEq)]
pub enum MoveResult {
Done,
MoveFurther(KeyboardFocusTarget),
ShiftFocus(KeyboardFocusTarget),
}
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct TreeQueue { struct TreeQueue {
trees: VecDeque<(Tree<Data>, Option<TilingBlocker>)>, trees: VecDeque<(Tree<Data>, Option<TilingBlocker>)>,
@ -160,6 +168,12 @@ impl Data {
None => matches!(self, Data::Mapped { .. }), None => matches!(self, Data::Mapped { .. }),
} }
} }
fn is_stack(&self) -> bool {
match self {
Data::Mapped { mapped, .. } => mapped.is_stack(),
_ => false,
}
}
fn orientation(&self) -> Orientation { fn orientation(&self) -> Orientation {
match self { match self {
@ -595,19 +609,57 @@ impl TilingLayout {
&mut self, &mut self,
direction: Direction, direction: Direction,
seat: &Seat<State>, seat: &Seat<State>,
) -> Option<KeyboardFocusTarget> { ) -> MoveResult {
let output = seat.active_output(); let output = seat.active_output();
let queue = self.queues.get_mut(&output).unwrap(); let queue = self.queues.get_mut(&output).unwrap();
let mut tree = queue.trees.back().unwrap().0.copy_clone(); let mut tree = queue.trees.back().unwrap().0.copy_clone();
let (node_id, data) = TilingLayout::currently_focused_node(&mut tree, seat)?; let Some((node_id, data)) = TilingLayout::currently_focused_node(&mut tree, seat) else {
return MoveResult::Done
};
// 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).into();
mapped.output_enter(&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)),
});
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(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker);
return MoveResult::ShiftFocus(mapped.into());
}
StackMoveResult::Default => {} // continue normally
}
}
let mut child_id = node_id.clone(); let mut child_id = node_id.clone();
// Without a parent to start with, just return // Without a parent to start with, just return
let Some(og_parent) = tree.get(&node_id).unwrap().parent().cloned() else { let Some(og_parent) = tree.get(&node_id).unwrap().parent().cloned() else {
return match data { return match data {
FocusedNodeData::Window(window) => Some(window.into()), FocusedNodeData::Window(window) => MoveResult::MoveFurther(window.into()),
FocusedNodeData::Group(focus_stack, alive) => Some(WindowGroup { FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther(WindowGroup {
node: node_id, node: node_id,
output: output.downgrade(), output: output.downgrade(),
alive, alive,
@ -670,7 +722,7 @@ impl TilingLayout {
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker); queue.push_tree(tree, blocker);
return None; return MoveResult::Done;
} }
// now if the orientation matches // now if the orientation matches
@ -696,7 +748,7 @@ impl TilingLayout {
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker); queue.push_tree(tree, blocker);
return None; return MoveResult::Done;
} }
// we can maybe move inside the group, if we don't run out of elements // we can maybe move inside the group, if we don't run out of elements
@ -722,7 +774,40 @@ impl TilingLayout {
.nth(next_idx) .nth(next_idx)
.unwrap() .unwrap()
.clone(); .clone();
if tree.get(&next_child_id).unwrap().data().is_group() && len == 2 {
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()
{
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 {
// if it is a group, we want to move into the group // if it is a group, we want to move into the group
tree.move_node(&node_id, MoveBehavior::ToParent(&next_child_id)) tree.move_node(&node_id, MoveBehavior::ToParent(&next_child_id))
.unwrap(); .unwrap();
@ -783,6 +868,8 @@ impl TilingLayout {
.unwrap() .unwrap()
.data_mut() .data_mut()
.remove_window(og_idx); .remove_window(og_idx);
MoveResult::Done
} else if len == 2 && child_id == node_id { } else if len == 2 && child_id == node_id {
// if we are just us two in the group, lets swap // if we are just us two in the group, lets swap
tree.make_nth_sibling(&node_id, next_idx).unwrap(); tree.make_nth_sibling(&node_id, next_idx).unwrap();
@ -791,6 +878,8 @@ impl TilingLayout {
.unwrap() .unwrap()
.data_mut() .data_mut()
.swap_windows(idx, next_idx); .swap_windows(idx, next_idx);
MoveResult::Done
} else { } else {
// else we make a new fork // else we make a new fork
TilingLayout::new_group(&mut tree, &next_child_id, &node_id, orientation) TilingLayout::new_group(&mut tree, &next_child_id, &node_id, orientation)
@ -808,11 +897,13 @@ impl TilingLayout {
.unwrap() .unwrap()
.data_mut() .data_mut()
.remove_window(og_idx); .remove_window(og_idx);
}
MoveResult::Done
};
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
queue.push_tree(tree, blocker); queue.push_tree(tree, blocker);
return None; return result;
} }
// We have reached the end of our parent group, try to move out even higher. // We have reached the end of our parent group, try to move out even higher.
@ -821,8 +912,8 @@ impl TilingLayout {
} }
match data { match data {
FocusedNodeData::Window(window) => Some(window.into()), FocusedNodeData::Window(window) => MoveResult::MoveFurther(window.into()),
FocusedNodeData::Group(focus_stack, alive) => Some( FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther(
WindowGroup { WindowGroup {
node: node_id, node: node_id,
output: output.downgrade(), output: output.downgrade(),