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
use crate::shell::layout::FocusDirection;
use serde::Deserialize;
use smithay::wayland::seat::Keysym;
pub use smithay::{
@ -181,15 +182,7 @@ pub enum Action {
Close,
Workspace(u8),
MoveToWorkspace(u8),
Focus(FocusAction),
Focus(FocusDirection),
Orientation(crate::shell::layout::Orientation),
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,
);
}
Action::Focus(focus) => match focus {
_ => { /* TODO */ }
},
Action::Focus(focus) => {
let current_output = active_output(seat, &self);
self.shell.move_focus(
seat,
&current_output,
*focus,
self.seats.iter(),
);
}
Action::Orientation(orientation) => {
let output = active_output(seat, &self);
self.shell.set_orientation(

View file

@ -1,4 +1,4 @@
use super::Layout;
use super::{FocusDirection, Layout, Orientation};
use smithay::{
desktop::{Space, Window},
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)
}
}
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,
}
#[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum FocusDirection {
Left,
Right,
Up,
Down,
}
pub trait Layout {
fn map_window<'a>(
&mut self,
@ -34,6 +42,16 @@ pub trait Layout {
fn refresh(&mut self, space: &mut Space);
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>(
&mut self,
orientation: Orientation,

View file

@ -1,6 +1,6 @@
// 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 smithay::{
desktop::{layer_map_for_output, Kind, Space, Window},
@ -119,12 +119,12 @@ impl Layout for TilingLayout {
Ok(window_id)
} else {
// we create a new fork
new_fork(tree, node_id, new_window)
TilingLayout::new_fork(tree, node_id, new_window)
}
} else {
// nothing? then we add to the root
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 {
tree.insert(new_window, InsertBehavior::AsRoot)
}
@ -146,12 +146,76 @@ impl Layout for TilingLayout {
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>(
&mut self,
new_orientation: Orientation,
seat: &Seat,
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);
@ -165,25 +229,15 @@ impl Layout for TilingLayout {
.borrow_mut();
let tree = &mut output_info.trees.entry(self.idx).or_insert_with(Tree::new);
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))
)
);
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 Some(last_active) = TilingLayout::last_active(tree, focus_stack) {
if let Some((fork, _child)) = TilingLayout::find_fork(tree, last_active) {
if let &mut Data::Fork {
ref mut orientation,
..
} = tree.get_mut(&parent_id).unwrap().data_mut()
} = tree.get_mut(&fork).unwrap().data_mut()
{
*orientation = new_orientation;
break;
}
node_id = parent_id;
}
}
}
@ -191,8 +245,10 @@ impl Layout for TilingLayout {
}
fn refresh(&mut self, space: &mut Space) {
while let Some(dead_windows) =
Some(update_space_positions(self.idx, space, self.gaps)).filter(|v| !v.is_empty())
while let Some(dead_windows) = Some(TilingLayout::update_space_positions(
self.idx, space, self.gaps,
))
.filter(|v| !v.is_empty())
{
for window in dead_windows {
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 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 {
ref orientation,
ref ratio,
} = tree.get(&parent_id).unwrap().data()
} = tree.get(&fork).unwrap().data()
{
// found a fork
// 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) {
(true, Orientation::Horizontal, ResizeEdge::Bottom)
| (false, Orientation::Horizontal, ResizeEdge::Top)
@ -242,22 +299,19 @@ impl Layout for TilingLayout {
| (false, Orientation::Vertical, ResizeEdge::Left) => {
let grab = ResizeForkGrab {
start_data,
node_id: parent_id,
node_id: fork,
initial_ratio: *ratio,
idx: self.idx,
output: output.clone(),
};
slog_scope::debug!("Tiling resize grabs");
pointer.set_grab(grab, serial, 0);
return;
}
(x, y, z) => {
slog_scope::debug!("Nope: {:?}, {:?}, {:?}", x, y, z);
}
_ => {} // continue iterating
}
}
node_id = parent_id;
node_id = fork;
}
}
}
@ -265,15 +319,42 @@ impl Layout for 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) {
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
let output = &info.borrow().output;
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 = &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 parent_id = tree
@ -335,151 +416,153 @@ impl TilingLayout {
None => {} // root
_ => unreachable!(),
}
// update_space_positions(space, self.gaps);
}
}
}
fn new_fork(
tree: &mut Tree<Data>,
old_id: &NodeId,
new: Node<Data>,
) -> Result<NodeId, NodeIdError> {
let new_fork = Node::new(Data::fork());
let old = tree.get(old_id)?;
let parent_id = old.parent().cloned();
let pos = parent_id.as_ref().and_then(|parent_id| {
tree.children_ids(parent_id)
.unwrap()
.position(|id| id == old_id)
});
fn new_fork(
tree: &mut Tree<Data>,
old_id: &NodeId,
new: Node<Data>,
) -> Result<NodeId, NodeIdError> {
let new_fork = Node::new(Data::fork());
let old = tree.get(old_id)?;
let parent_id = old.parent().cloned();
let pos = parent_id.as_ref().and_then(|parent_id| {
tree.children_ids(parent_id)
.unwrap()
.position(|id| id == old_id)
});
let fork_id = tree
.insert(
new_fork,
if let Some(parent) = parent_id.as_ref() {
InsertBehavior::UnderNode(parent)
} else {
InsertBehavior::AsRoot
},
)
.unwrap();
let fork_id = tree
.insert(
new_fork,
if let Some(parent) = parent_id.as_ref() {
InsertBehavior::UnderNode(parent)
} else {
InsertBehavior::AsRoot
},
)
.unwrap();
tree.move_node(old_id, MoveBehavior::ToParent(&fork_id))
.unwrap();
// keep position
if let Some(old_pos) = pos {
tree.make_nth_sibling(&fork_id, old_pos).unwrap();
tree.move_node(old_id, MoveBehavior::ToParent(&fork_id))
.unwrap();
// keep position
if let Some(old_pos) = pos {
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> {
let mut dead_windows = Vec::new();
let (outer, inner) = gaps;
for output in space.outputs().cloned().collect::<Vec<_>>().into_iter() {
if let Some(mut info) = output
.user_data()
.get::<RefCell<OutputInfo>>()
.map(|x| x.borrow_mut())
{
let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new);
if let Some(root) = tree.root_node_id() {
let mut stack = Vec::new();
fn update_space_positions(idx: u8, space: &mut Space, gaps: (i32, i32)) -> Vec<Window> {
let mut dead_windows = Vec::new();
let (outer, inner) = gaps;
for output in space.outputs().cloned().collect::<Vec<_>>().into_iter() {
if let Some(mut info) = output
.user_data()
.get::<RefCell<OutputInfo>>()
.map(|x| x.borrow_mut())
{
let tree = &mut info.trees.entry(idx).or_insert_with(Tree::new);
if let Some(root) = tree.root_node_id() {
let mut stack = Vec::new();
let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone());
// TODO saturate? minimum?
if let Some(mut geo) = geo.as_mut() {
geo.loc.x += outer;
geo.loc.y += outer;
geo.size.w -= outer * 2;
geo.size.h -= outer * 2;
}
let mut geo = Some(layer_map_for_output(&output).non_exclusive_zone());
// TODO saturate? minimum?
if let Some(mut geo) = geo.as_mut() {
geo.loc.x += outer;
geo.loc.y += outer;
geo.size.w -= outer * 2;
geo.size.h -= outer * 2;
}
for node in tree.traverse_pre_order(root).unwrap() {
let geo = stack.pop().unwrap_or(geo);
match node.data() {
Data::Fork { orientation, ratio } => {
if let Some(geo) = geo {
match orientation {
Orientation::Horizontal => {
let top_size = (
geo.size.w,
((geo.size.h as f64) * ratio).ceil() as i32,
);
stack.push(Some(Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + top_size.1),
(geo.size.w, geo.size.h - top_size.1),
)));
stack.push(Some(Rectangle::from_loc_and_size(
geo.loc, top_size,
)));
for node in tree.traverse_pre_order(root).unwrap() {
let geo = stack.pop().unwrap_or(geo);
match node.data() {
Data::Fork { orientation, ratio } => {
if let Some(geo) = geo {
match orientation {
Orientation::Horizontal => {
let top_size = (
geo.size.w,
((geo.size.h as f64) * ratio).ceil() as i32,
);
stack.push(Some(Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + top_size.1),
(geo.size.w, geo.size.h - top_size.1),
)));
stack.push(Some(Rectangle::from_loc_and_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 {
stack.push(None);
stack.push(None);
}
}
}
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();
}
Data::Stack { active, len } => {
for i in 0..*len {
if i == *active {
stack.push(geo);
} else {
stack.push(None);
}
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)
}
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>(
&mut self,
surface: Option<&WlSurface>,

View file

@ -126,6 +126,20 @@ impl Workspace {
.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) {
self.layout
.maximize_request(&mut self.space, window, output)