input: Implement focus switched on tiling layout

This commit is contained in:
Victoria Brekenfeld 2022-03-30 23:08:35 +02:00
parent f7ac9654d1
commit 69c71179d4
7 changed files with 336 additions and 166 deletions

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::shell::layout::FocusDirection;
use serde::Deserialize; use serde::Deserialize;
use smithay::wayland::seat::Keysym; use smithay::wayland::seat::Keysym;
pub use smithay::{ pub use smithay::{
@ -181,15 +182,7 @@ pub enum Action {
Close, Close,
Workspace(u8), Workspace(u8),
MoveToWorkspace(u8), MoveToWorkspace(u8),
Focus(FocusAction), Focus(FocusDirection),
Orientation(crate::shell::layout::Orientation), Orientation(crate::shell::layout::Orientation),
Spawn(String), Spawn(String),
} }
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum FocusAction {
Left,
Right,
Up,
Down,
}

View file

@ -302,9 +302,15 @@ impl Common {
*num, *num,
); );
} }
Action::Focus(focus) => match focus { Action::Focus(focus) => {
_ => { /* TODO */ } let current_output = active_output(seat, &self);
}, self.shell.move_focus(
seat,
&current_output,
*focus,
self.seats.iter(),
);
}
Action::Orientation(orientation) => { Action::Orientation(orientation) => {
let output = active_output(seat, &self); let output = active_output(seat, &self);
self.shell.set_orientation( self.shell.set_orientation(

View file

@ -1,4 +1,4 @@
use super::Layout; use super::{FocusDirection, Layout, Orientation};
use smithay::{ use smithay::{
desktop::{Space, Window}, desktop::{Space, Window},
reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge, reexports::wayland_protocols::xdg_shell::server::xdg_toplevel::ResizeEdge,
@ -108,4 +108,44 @@ impl<A: Layout, B: Layout> Layout for Combined<A, B> {
.resize_request(space, window, seat, serial, start_data, edges) .resize_request(space, window, seat, serial, start_data, edges)
} }
} }
fn move_focus<'a>(
&mut self,
direction: FocusDirection,
seat: &Seat,
space: &mut Space,
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) -> Option<Window> {
let focus_stack = focus_stack.collect::<Vec<_>>();
match self.first.move_focus(
direction,
seat,
space,
Box::new(focus_stack.clone().into_iter()),
) {
Some(window) => Some(window),
None => {
self.second
.move_focus(direction, seat, space, Box::new(focus_stack.into_iter()))
}
}
}
fn update_orientation<'a>(
&mut self,
orientation: Orientation,
seat: &Seat,
space: &mut Space,
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) {
let focus_stack = focus_stack.collect::<Vec<_>>();
self.first.update_orientation(
orientation,
seat,
space,
Box::new(focus_stack.clone().into_iter()),
);
self.second
.update_orientation(orientation, seat, space, Box::new(focus_stack.into_iter()));
}
} }

View file

@ -23,6 +23,14 @@ pub enum Orientation {
Vertical, Vertical,
} }
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum FocusDirection {
Left,
Right,
Up,
Down,
}
pub trait Layout { pub trait Layout {
fn map_window<'a>( fn map_window<'a>(
&mut self, &mut self,
@ -34,6 +42,16 @@ pub trait Layout {
fn refresh(&mut self, space: &mut Space); fn refresh(&mut self, space: &mut Space);
fn unmap_window(&mut self, space: &mut Space, window: &Window); fn unmap_window(&mut self, space: &mut Space, window: &Window);
fn move_focus<'a>(
&mut self,
direction: FocusDirection,
seat: &Seat,
space: &mut Space,
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) -> Option<Window> {
let _ = (direction, seat, space, focus_stack);
None
}
fn update_orientation<'a>( fn update_orientation<'a>(
&mut self, &mut self,
orientation: Orientation, orientation: Orientation,

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::shell::layout::{Layout, Orientation}; use crate::shell::layout::{FocusDirection, Layout, Orientation};
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
use smithay::{ use smithay::{
desktop::{layer_map_for_output, Kind, Space, Window}, desktop::{layer_map_for_output, Kind, Space, Window},
@ -119,12 +119,12 @@ impl Layout for TilingLayout {
Ok(window_id) Ok(window_id)
} else { } else {
// we create a new fork // we create a new fork
new_fork(tree, node_id, new_window) TilingLayout::new_fork(tree, node_id, new_window)
} }
} else { } else {
// nothing? then we add to the root // nothing? then we add to the root
if let Some(root_id) = tree.root_node_id().cloned() { if let Some(root_id) = tree.root_node_id().cloned() {
new_fork(tree, &root_id, new_window) TilingLayout::new_fork(tree, &root_id, new_window)
} else { } else {
tree.insert(new_window, InsertBehavior::AsRoot) tree.insert(new_window, InsertBehavior::AsRoot)
} }
@ -146,12 +146,76 @@ impl Layout for TilingLayout {
self.refresh(space); self.refresh(space);
} }
fn move_focus<'a>(
&mut self,
direction: FocusDirection,
seat: &Seat,
space: &mut Space,
focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) -> Option<Window> {
let output = super::output_from_seat(seat, space);
output
.user_data()
.insert_if_missing(|| RefCell::new(OutputInfo::default()));
let mut output_info = output
.user_data()
.get::<RefCell<OutputInfo>>()
.unwrap()
.borrow_mut();
let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new);
if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) {
let mut node_id = last_active;
while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) {
if let &Data::Fork {
ref orientation, ..
} = tree.get(&fork).unwrap().data()
{
// found a fork
// which child are we?
let first = tree.children_ids(&fork).unwrap().next() == Some(&child);
let focus_subtree = match (first, orientation, direction) {
(true, Orientation::Horizontal, FocusDirection::Down)
| (true, Orientation::Vertical, FocusDirection::Right) => {
tree.children_ids(&fork).unwrap().skip(1).next()
}
(false, Orientation::Horizontal, FocusDirection::Up)
| (false, Orientation::Vertical, FocusDirection::Left) => {
tree.children_ids(&fork).unwrap().next()
}
_ => None, // continue iterating
};
if focus_subtree.is_some() {
let mut node_id = focus_subtree;
while node_id.is_some()
&& !matches!(
tree.get(node_id.as_ref().unwrap()).unwrap().data(),
Data::Window(_)
)
{
// TODO, if ndoe_id is a stack, we want to assign the active node instead of the first
node_id = tree.children_ids(node_id.as_ref().unwrap()).unwrap().next();
}
if let Some(Data::Window(window)) =
node_id.and_then(|i| tree.get(&i).ok()).map(|n| n.data())
{
return Some(window.clone());
}
}
}
node_id = fork;
}
}
None
}
fn update_orientation<'a>( fn update_orientation<'a>(
&mut self, &mut self,
new_orientation: Orientation, new_orientation: Orientation,
seat: &Seat, seat: &Seat,
space: &mut Space, space: &mut Space,
mut focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>, focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) { ) {
{ {
let output = super::output_from_seat(seat, space); let output = super::output_from_seat(seat, space);
@ -165,25 +229,15 @@ impl Layout for TilingLayout {
.borrow_mut(); .borrow_mut();
let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new);
let last_active = focus_stack if let Some(last_active) = TilingLayout::last_active(tree, focus_stack) {
.find_map(|window| tree.root_node_id() if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) {
.and_then(|root| tree.traverse_pre_order_ids(root).unwrap()
.find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window))
)
);
if let Some(ref node_id) = last_active {
let mut node_id = node_id.clone();
while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() {
if let &mut Data::Fork { if let &mut Data::Fork {
ref mut orientation, ref mut orientation,
.. ..
} = tree.get_mut(&parent_id).unwrap().data_mut() } = tree.get_mut(&fork).unwrap().data_mut()
{ {
*orientation = new_orientation; *orientation = new_orientation;
break;
} }
node_id = parent_id;
} }
} }
} }
@ -191,8 +245,10 @@ impl Layout for TilingLayout {
} }
fn refresh(&mut self, space: &mut Space) { fn refresh(&mut self, space: &mut Space) {
while let Some(dead_windows) = while let Some(dead_windows) = Some(TilingLayout::update_space_positions(
Some(update_space_positions(self.idx, space, self.gaps)).filter(|v| !v.is_empty()) self.idx, space, self.gaps,
))
.filter(|v| !v.is_empty())
{ {
for window in dead_windows { for window in dead_windows {
self.unmap_window(&window); self.unmap_window(&window);
@ -226,15 +282,16 @@ impl Layout for TilingLayout {
let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new);
let mut node_id = info.borrow().node.clone(); let mut node_id = info.borrow().node.clone();
while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() {
while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) {
if let &Data::Fork { if let &Data::Fork {
ref orientation, ref orientation,
ref ratio, ref ratio,
} = tree.get(&parent_id).unwrap().data() } = tree.get(&fork).unwrap().data()
{ {
// found a fork // found a fork
// which child are we? // which child are we?
let first = tree.children_ids(&parent_id).unwrap().next() == Some(&node_id); let first = tree.children_ids(&fork).unwrap().next() == Some(&child);
match (first, orientation, edges) { match (first, orientation, edges) {
(true, Orientation::Horizontal, ResizeEdge::Bottom) (true, Orientation::Horizontal, ResizeEdge::Bottom)
| (false, Orientation::Horizontal, ResizeEdge::Top) | (false, Orientation::Horizontal, ResizeEdge::Top)
@ -242,22 +299,19 @@ impl Layout for TilingLayout {
| (false, Orientation::Vertical, ResizeEdge::Left) => { | (false, Orientation::Vertical, ResizeEdge::Left) => {
let grab = ResizeForkGrab { let grab = ResizeForkGrab {
start_data, start_data,
node_id: parent_id, node_id: fork,
initial_ratio: *ratio, initial_ratio: *ratio,
idx: self.idx, idx: self.idx,
output: output.clone(), output: output.clone(),
}; };
slog_scope::debug!("Tiling resize grabs");
pointer.set_grab(grab, serial, 0); pointer.set_grab(grab, serial, 0);
return; return;
} }
(x, y, z) => { _ => {} // continue iterating
slog_scope::debug!("Nope: {:?}, {:?}, {:?}", x, y, z);
}
} }
} }
node_id = parent_id; node_id = fork;
} }
} }
} }
@ -265,15 +319,42 @@ impl Layout for TilingLayout {
} }
impl TilingLayout { impl TilingLayout {
fn last_active<'a>(
tree: &mut Tree<Data>,
mut focus_stack: Box<dyn Iterator<Item = &'a Window> + 'a>,
) -> Option<NodeId> {
let last_active = focus_stack
.find_map(|window| tree.root_node_id()
.and_then(|root| tree.traverse_pre_order_ids(root).unwrap()
.find(|id| matches!(tree.get(id).map(|n| n.data()), Ok(Data::Window(w)) if w == window))
)
);
last_active
}
fn find_fork(tree: &mut Tree<Data>, mut node_id: NodeId) -> Option<(NodeId, NodeId)> {
while let Some(parent_id) = tree.get(&node_id).unwrap().parent().cloned() {
if let &Data::Fork { .. } = tree.get(&parent_id).unwrap().data() {
return Some((parent_id, node_id));
}
node_id = parent_id;
}
None
}
fn unmap_window(&mut self, window: &Window) { fn unmap_window(&mut self, window: &Window) {
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() { if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
let output = &info.borrow().output; let output = &info.borrow().output;
output
.user_data()
.insert_if_missing(|| RefCell::new(OutputInfo::default()));
let mut output_info = output let mut output_info = output
.user_data() .user_data()
.get::<RefCell<OutputInfo>>() .get::<RefCell<OutputInfo>>()
.unwrap() .unwrap()
.borrow_mut(); .borrow_mut();
let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new); let tree = output_info.trees.entry(self.idx).or_insert_with(Tree::new);
let node_id = info.borrow().node.clone(); let node_id = info.borrow().node.clone();
let parent_id = tree let parent_id = tree
@ -335,151 +416,153 @@ impl TilingLayout {
None => {} // root None => {} // root
_ => unreachable!(), _ => unreachable!(),
} }
// update_space_positions(space, self.gaps);
} }
} }
}
fn new_fork( fn new_fork(
tree: &mut Tree<Data>, tree: &mut Tree<Data>,
old_id: &NodeId, old_id: &NodeId,
new: Node<Data>, new: Node<Data>,
) -> Result<NodeId, NodeIdError> { ) -> Result<NodeId, NodeIdError> {
let new_fork = Node::new(Data::fork()); let new_fork = Node::new(Data::fork());
let old = tree.get(old_id)?; let old = tree.get(old_id)?;
let parent_id = old.parent().cloned(); let parent_id = old.parent().cloned();
let pos = parent_id.as_ref().and_then(|parent_id| { let pos = parent_id.as_ref().and_then(|parent_id| {
tree.children_ids(parent_id) tree.children_ids(parent_id)
.unwrap() .unwrap()
.position(|id| id == old_id) .position(|id| id == old_id)
}); });
let fork_id = tree let fork_id = tree
.insert( .insert(
new_fork, new_fork,
if let Some(parent) = parent_id.as_ref() { if let Some(parent) = parent_id.as_ref() {
InsertBehavior::UnderNode(parent) InsertBehavior::UnderNode(parent)
} else { } else {
InsertBehavior::AsRoot InsertBehavior::AsRoot
}, },
) )
.unwrap(); .unwrap();
tree.move_node(old_id, MoveBehavior::ToParent(&fork_id)) tree.move_node(old_id, MoveBehavior::ToParent(&fork_id))
.unwrap(); .unwrap();
// keep position // keep position
if let Some(old_pos) = pos { if let Some(old_pos) = pos {
tree.make_nth_sibling(&fork_id, old_pos).unwrap(); tree.make_nth_sibling(&fork_id, old_pos).unwrap();
}
tree.insert(new, InsertBehavior::UnderNode(&fork_id))
} }
tree.insert(new, InsertBehavior::UnderNode(&fork_id))
}
fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec<Window> { fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec<Window> {
let mut dead_windows = Vec::new(); let mut dead_windows = Vec::new();
let (outer, inner) = gaps; let (outer, inner) = gaps;
for output in space.outputs().cloned().collect::<Vec<_>>().into_iter() { for output in space.outputs().cloned().collect::<Vec<_>>().into_iter() {
if let Some(mut info) = output if let Some(mut info) = output
.user_data() .user_data()
.get::<RefCell<OutputInfo>>() .get::<RefCell<OutputInfo>>()
.map(|x| x.borrow_mut()) .map(|x| x.borrow_mut())
{ {
let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new); let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new);
if let Some(root) = tree.root_node_id() { if let Some(root) = tree.root_node_id() {
let mut stack = Vec::new(); let mut stack = Vec::new();
let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone()); let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone());
// TODO saturate? minimum? // TODO saturate? minimum?
if let Some(mut geo) = geo.as_mut() { if let Some(mut geo) = geo.as_mut() {
geo.loc.x += outer; geo.loc.x += outer;
geo.loc.y += outer; geo.loc.y += outer;
geo.size.w -= outer * 2; geo.size.w -= outer * 2;
geo.size.h -= outer * 2; geo.size.h -= outer * 2;
} }
for node in tree.traverse_pre_order(root).unwrap() { for node in tree.traverse_pre_order(root).unwrap() {
let geo = stack.pop().unwrap_or(geo); let geo = stack.pop().unwrap_or(geo);
match node.data() { match node.data() {
Data::Fork { orientation, ratio } => { Data::Fork { orientation, ratio } => {
if let Some(geo) = geo { if let Some(geo) = geo {
match orientation { match orientation {
Orientation::Horizontal => { Orientation::Horizontal => {
let top_size = ( let top_size = (
geo.size.w, geo.size.w,
((geo.size.h as f64) * ratio).ceil() as i32, ((geo.size.h as f64) * ratio).ceil() as i32,
); );
stack.push(Some(Rectangle::from_loc_and_size( stack.push(Some(Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + top_size.1), (geo.loc.x, geo.loc.y + top_size.1),
(geo.size.w, geo.size.h - top_size.1), (geo.size.w, geo.size.h - top_size.1),
))); )));
stack.push(Some(Rectangle::from_loc_and_size( stack.push(Some(Rectangle::from_loc_and_size(
geo.loc, top_size, geo.loc, top_size,
))); )));
}
Orientation::Vertical => {
let left_size = (
((geo.size.w as f64) * ratio).ceil() as i32,
geo.size.h,
);
stack.push(Some(Rectangle::from_loc_and_size(
(geo.loc.x + left_size.0, geo.loc.y),
(geo.size.w - left_size.0, geo.size.h),
)));
stack.push(Some(Rectangle::from_loc_and_size(
geo.loc, left_size,
)));
}
} }
Orientation::Vertical => {
let left_size = (
((geo.size.w as f64) * ratio).ceil() as i32,
geo.size.h,
);
stack.push(Some(Rectangle::from_loc_and_size(
(geo.loc.x + left_size.0, geo.loc.y),
(geo.size.w - left_size.0, geo.size.h),
)));
stack.push(Some(Rectangle::from_loc_and_size(
geo.loc, left_size,
)));
}
}
} else {
stack.push(None);
stack.push(None);
}
}
Data::Stack { active, len } => {
for i in 0..*len {
if i == *active {
stack.push(geo);
} else { } else {
stack.push(None); stack.push(None);
stack.push(None);
} }
} }
} Data::Stack { active, len } => {
Data::Window(window) => { for i in 0..*len {
if window.toplevel().alive() { if i == *active {
if let Some(geo) = geo { stack.push(geo);
#[allow(irrefutable_let_patterns)] } else {
if let Kind::Xdg(xdg) = &window.toplevel() { stack.push(None);
let ret = xdg.with_pending_state(|state| {
state.size = Some(
(geo.size.w - inner * 2, geo.size.h - inner * 2)
.into(),
);
state.states.set(XdgState::TiledLeft);
state.states.set(XdgState::TiledRight);
state.states.set(XdgState::TiledTop);
state.states.set(XdgState::TiledBottom);
});
if ret.is_ok() {
xdg.send_configure();
}
} }
let window_geo = window.geometry();
space.map_window(
&window,
(
geo.loc.x + inner - window_geo.loc.x,
geo.loc.y + inner - window_geo.loc.y,
),
false,
);
} }
} else { }
dead_windows.push(window.clone()); Data::Window(window) => {
if window.toplevel().alive() {
if let Some(geo) = geo {
#[allow(irrefutable_let_patterns)]
if let Kind::Xdg(xdg) = &window.toplevel() {
let ret = xdg.with_pending_state(|state| {
state.size = Some(
(
geo.size.w - inner * 2,
geo.size.h - inner * 2,
)
.into(),
);
state.states.set(XdgState::TiledLeft);
state.states.set(XdgState::TiledRight);
state.states.set(XdgState::TiledTop);
state.states.set(XdgState::TiledBottom);
});
if ret.is_ok() {
xdg.send_configure();
}
}
let window_geo = window.geometry();
space.map_window(
&window,
(
geo.loc.x + inner - window_geo.loc.x,
geo.loc.y + inner - window_geo.loc.y,
),
false,
);
}
} else {
dead_windows.push(window.clone());
}
} }
} }
} }
} }
} }
} }
dead_windows
} }
dead_windows
} }

View file

@ -409,6 +409,22 @@ impl Shell {
.update_orientation(seat, orientation) .update_orientation(seat, orientation)
} }
pub fn move_focus<'a>(
&mut self,
seat: &Seat,
output: &Output,
focus: layout::FocusDirection,
seats: impl Iterator<Item = &'a Seat>,
) {
if let Some(surface) = self
.active_space_mut(output)
.move_focus(seat, focus)
.and_then(|window| window.toplevel().get_surface().cloned())
{
self.set_focus(Some(&surface), seat, seats, None)
}
}
fn set_focus<'a>( fn set_focus<'a>(
&mut self, &mut self,
surface: Option<&WlSurface>, surface: Option<&WlSurface>,

View file

@ -126,6 +126,20 @@ impl Workspace {
.update_orientation(orientation, seat, &mut self.space, focus_stack.iter()) .update_orientation(orientation, seat, &mut self.space, focus_stack.iter())
} }
pub fn move_focus(&mut self, seat: &Seat, focus: layout::FocusDirection) -> Option<Window> {
seat.user_data()
.insert_if_missing(|| FocusStackData::new((HashMap::new(), IndexSet::new())));
let focus_stack = FocusStackMut(RefMut::map(
seat.user_data()
.get::<FocusStackData>()
.unwrap()
.borrow_mut(),
|map| map.0.entry(self.idx).or_insert_with(|| IndexSet::new()),
));
self.layout
.move_focus(focus, seat, &mut self.space, focus_stack.iter())
}
pub fn maximize_request(&mut self, window: &Window, output: &Output) { pub fn maximize_request(&mut self, window: &Window, output: &Output) {
self.layout self.layout
.maximize_request(&mut self.space, window, output) .maximize_request(&mut self.space, window, output)