shell: Introduce MoveResult to implement stacking
This commit is contained in:
parent
f00753071e
commit
b400939dd9
3 changed files with 175 additions and 63 deletions
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue