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},
layout::{
floating::SeatMoveGrabState,
tiling::{Direction, FocusResult},
tiling::{Direction, FocusResult, MoveResult},
},
OverviewMode, Workspace,
}, // shell::grabs::SeatMoveGrabState
@ -1048,53 +1048,55 @@ impl State {
return; // TODO, is this what we want? How do we indicate the switch?
}
if let Some(_move_further) =
workspace.tiling_layer.move_current_node(direction, seat)
{
// TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead)
match (direction, self.common.config.static_conf.workspace_layout) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToPreviousWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToNextWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Left, WorkspaceLayout::Vertical)
| (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToPreviousOutput,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Right, WorkspaceLayout::Vertical)
| (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToNextOutput,
seat,
serial,
time,
mods,
Some(direction),
),
match workspace.tiling_layer.move_current_node(direction, seat) {
MoveResult::MoveFurther(_move_further) => {
match (direction, self.common.config.static_conf.workspace_layout) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToPreviousWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToNextWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Left, WorkspaceLayout::Vertical)
| (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToPreviousOutput,
seat,
serial,
time,
mods,
Some(direction),
),
(Direction::Right, WorkspaceLayout::Vertical)
| (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToNextOutput,
seat,
serial,
time,
mods,
Some(direction),
),
}
}
} else {
let focus_stack = workspace.focus_stack.get(seat);
if let Some(focused_window) = focus_stack.last() {
if workspace.is_tiled(focused_window) {
self.common.shell.set_overview_mode(Some(mods));
MoveResult::ShiftFocus(shift) => {
Common::set_focus(self, Some(&shift), seat, None);
}
MoveResult::Done => {
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;
use self::stack::MoveResult;
pub use self::surface::CosmicSurface;
pub mod stack;
pub use self::stack::CosmicStack;
@ -61,7 +62,10 @@ use smithay::backend::renderer::{element::texture::TextureRenderElement, gles::G
#[cfg(feature = "debug")]
use tracing::debug;
use super::{focus::FocusDirection, layout::floating::ResizeState};
use super::{
focus::FocusDirection,
layout::{floating::ResizeState, tiling::Direction},
};
space_elements! {
#[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 {
if let CosmicMappedInternal::Stack(stack) = &self.element {
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>(
&mut self,
outputs: impl Iterator<Item = (&'a Output, Rectangle<i32, Logical>)>,
@ -460,7 +479,7 @@ impl CosmicMapped {
match &self.element {
CosmicMappedInternal::Window(window) => {
let surface = window.surface();
let activated = surface.is_activated();
let activated = surface.is_activated(true);
let handle = window.loop_handle();
let stack = CosmicStack::new(std::iter::once(surface), handle);
@ -494,7 +513,7 @@ impl CosmicMapped {
for (output, overlap) in outputs {
window.output_enter(output, overlap);
}
window.set_activate(self.is_activated());
window.set_activate(self.is_activated(true));
window.surface().send_configure();
window.refresh();

View file

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