shell/tiling: Allow tiled windows to be resized

This commit is contained in:
Victoria Brekenfeld 2022-10-27 20:40:55 +02:00
parent 644d53c2da
commit ff32f48f39
7 changed files with 339 additions and 133 deletions

107
src/shell/grabs.rs Normal file
View file

@ -0,0 +1,107 @@
use smithay::{
input::pointer::{
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle,
},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::{Logical, Point},
};
use crate::state::State;
use super::{
focus::target::PointerFocusTarget,
layout::{floating::ResizeSurfaceGrab, tiling::ResizeForkGrab},
};
bitflags::bitflags! {
pub struct ResizeEdge: u32 {
const TOP = 0b0001;
const BOTTOM = 0b0010;
const LEFT = 0b0100;
const RIGHT = 0b1000;
const TOP_LEFT = Self::TOP.bits | Self::LEFT.bits;
const BOTTOM_LEFT = Self::BOTTOM.bits | Self::LEFT.bits;
const TOP_RIGHT = Self::TOP.bits | Self::RIGHT.bits;
const BOTTOM_RIGHT = Self::BOTTOM.bits | Self::RIGHT.bits;
}
}
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
#[inline]
fn from(x: xdg_toplevel::ResizeEdge) -> Self {
Self::from_bits(x.into()).unwrap()
}
}
impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
#[inline]
fn from(x: ResizeEdge) -> Self {
Self::try_from(x.bits()).unwrap()
}
}
pub enum ResizeGrab {
Floating(ResizeSurfaceGrab),
Tiling(ResizeForkGrab),
}
impl From<ResizeSurfaceGrab> for ResizeGrab {
fn from(grab: ResizeSurfaceGrab) -> Self {
ResizeGrab::Floating(grab)
}
}
impl From<ResizeForkGrab> for ResizeGrab {
fn from(grab: ResizeForkGrab) -> Self {
ResizeGrab::Tiling(grab)
}
}
impl PointerGrab<State> for ResizeGrab {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
event: &MotionEvent,
) {
match self {
ResizeGrab::Floating(grab) => grab.motion(data, handle, focus, event),
ResizeGrab::Tiling(grab) => grab.motion(data, handle, focus, event),
}
}
fn button(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &ButtonEvent,
) {
match self {
ResizeGrab::Floating(grab) => grab.button(data, handle, event),
ResizeGrab::Tiling(grab) => grab.button(data, handle, event),
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
match self {
ResizeGrab::Floating(grab) => grab.axis(data, handle, details),
ResizeGrab::Tiling(grab) => grab.axis(data, handle, details),
}
}
fn start_data(&self) -> &PointerGrabStartData<State> {
match self {
ResizeGrab::Floating(grab) => grab.start_data(),
ResizeGrab::Tiling(grab) => grab.start_data(),
}
}
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
shell::{element::CosmicMapped, focus::target::PointerFocusTarget},
shell::{element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge},
utils::prelude::*,
};
use smithay::{
@ -10,39 +10,8 @@ use smithay::{
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle,
},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::{IsAlive, Logical, Point, Size},
};
use std::convert::TryFrom;
bitflags::bitflags! {
pub struct ResizeEdge: u32 {
const TOP = 0b0001;
const BOTTOM = 0b0010;
const LEFT = 0b0100;
const RIGHT = 0b1000;
const TOP_LEFT = Self::TOP.bits | Self::LEFT.bits;
const BOTTOM_LEFT = Self::BOTTOM.bits | Self::LEFT.bits;
const TOP_RIGHT = Self::TOP.bits | Self::RIGHT.bits;
const BOTTOM_RIGHT = Self::BOTTOM.bits | Self::RIGHT.bits;
}
}
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
#[inline]
fn from(x: xdg_toplevel::ResizeEdge) -> Self {
Self::from_bits(x.into()).unwrap()
}
}
impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
#[inline]
fn from(x: ResizeEdge) -> Self {
Self::try_from(x.bits()).unwrap()
}
}
/// Information about the resize operation.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@ -185,12 +154,12 @@ impl ResizeSurfaceGrab {
pub fn new(
start_data: PointerGrabStartData<State>,
mapped: CosmicMapped,
edges: xdg_toplevel::ResizeEdge,
edges: ResizeEdge,
initial_window_location: Point<i32, Logical>,
initial_window_size: Size<i32, Logical>,
) -> ResizeSurfaceGrab {
let resize_state = ResizeState::Resizing(ResizeData {
edges: edges.into(),
edges,
initial_window_location,
initial_window_size,
});
@ -200,7 +169,7 @@ impl ResizeSurfaceGrab {
ResizeSurfaceGrab {
start_data,
window: mapped,
edges: edges.into(),
edges,
initial_window_size,
last_window_size: initial_window_size,
}

View file

@ -5,7 +5,6 @@ use smithay::{
desktop::{layer_map_for_output, space::SpaceElement, Space, Window},
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output,
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
render_elements,
utils::{Logical, Point, Rectangle, Serial},
};
@ -14,6 +13,7 @@ use std::collections::HashMap;
use crate::{
shell::{
element::{CosmicMapped, CosmicMappedRenderElement},
grabs::ResizeEdge,
OutputNotMapped,
},
state::State,

View file

@ -1,23 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{shell::layout::Orientation, utils::prelude::*};
use atomic_float::AtomicF64;
use crate::{
shell::{focus::target::PointerFocusTarget, layout::Orientation},
utils::prelude::*,
};
use id_tree::NodeId;
use smithay::{
input::pointer::{
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle,
},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{Logical, Point, Size},
output::{Output, WeakOutput},
utils::{Logical, Point},
};
use std::sync::{atomic::Ordering, Arc};
use super::Data;
pub struct ResizeForkGrab {
pub start_data: PointerGrabStartData<State>,
pub orientation: Orientation,
pub initial_size: Size<i32, Logical>,
pub initial_ratio: f64,
pub ratio: Arc<AtomicF64>,
start_data: PointerGrabStartData<State>,
idx: usize,
initial_size_upleft: i32,
initial_size_downright: i32,
node: NodeId,
output: WeakOutput,
}
impl ResizeForkGrab {
pub fn new(
start_data: PointerGrabStartData<State>,
node: NodeId,
output: &Output,
data: &Data,
idx: usize,
) -> ResizeForkGrab {
let sizes = match data {
Data::Group { ref sizes, .. } => sizes,
_ => panic!("Resizing without a group?!?"),
};
ResizeForkGrab {
start_data,
idx,
initial_size_upleft: sizes.iter().take(idx + 1).sum(),
initial_size_downright: sizes.iter().skip(idx + 1).sum(),
node,
output: output.downgrade(),
}
}
}
impl PointerGrab<State> for ResizeForkGrab {
@ -25,21 +54,139 @@ impl PointerGrab<State> for ResizeForkGrab {
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(WlSurface, Point<i32, Logical>)>,
_focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
event: &MotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
let delta = event.location - self.start_data.location;
let delta = match self.orientation {
Orientation::Vertical => delta.x / self.initial_size.w as f64,
Orientation::Horizontal => delta.y / self.initial_size.h as f64,
};
self.ratio.store(
0.9f64.min(0.1f64.max(self.initial_ratio + delta)),
Ordering::SeqCst,
);
if let Some(output) = self.output.upgrade() {
let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer;
if let Some(tree) = tiling_layer.trees.get_mut(&output) {
if tree.get(&self.node).is_ok() {
let orientation = tree.get(&self.node).unwrap().data().orientation();
let delta = match orientation {
Orientation::Vertical => delta.x,
Orientation::Horizontal => delta.y,
}
.round() as i32;
let upleft_node_id =
match tree.children_ids(&self.node).unwrap().skip(self.idx).next() {
Some(elem) => elem,
None => {
return;
}
};
let downright_node_id = match tree
.children_ids(&self.node)
.unwrap()
.skip(self.idx + 1)
.next()
{
Some(elem) => elem,
None => {
return;
}
};
let next_mapped = |mut node| loop {
if let Some(node_id) = node {
match tree.get(&node_id).unwrap().data() {
Data::Group { orientation: o, .. } if o == &orientation => {
node = tree.children_ids(&node_id).unwrap().last().cloned();
}
_ => {
break node_id;
}
}
} else {
unreachable!()
}
};
let upleft_mapped_id = next_mapped(Some(upleft_node_id.clone()));
let downright_mapped_id = next_mapped(Some(downright_node_id.clone()));
let new_upleft_size = self.initial_size_upleft + delta;
let new_downright_size = self.initial_size_downright - delta;
let new_upleft_mapped_size = match orientation {
Orientation::Horizontal => {
tree.get(&upleft_mapped_id)
.unwrap()
.data()
.geometry()
.size
.h
}
Orientation::Vertical => {
tree.get(&upleft_mapped_id)
.unwrap()
.data()
.geometry()
.size
.w
}
} + delta;
let new_downright_mapped_size = match orientation {
Orientation::Horizontal => {
tree.get(&downright_mapped_id)
.unwrap()
.data()
.geometry()
.size
.h
}
Orientation::Vertical => {
tree.get(&downright_mapped_id)
.unwrap()
.data()
.geometry()
.size
.w
}
} - delta;
if new_upleft_mapped_size > 100 && new_downright_mapped_size > 100 {
// lets update
{
let node = tree.get_mut(&self.node).unwrap();
let data = node.data_mut();
match data {
Data::Group { sizes, .. } => {
sizes[self.idx] = new_upleft_size;
sizes[self.idx + 1] = new_downright_size;
}
_ => unreachable!(),
};
}
for (mapped_id, mapped_size) in &[
(upleft_mapped_id, new_upleft_mapped_size),
(downright_mapped_id, new_downright_mapped_size),
] {
let parent = tree.get(mapped_id).unwrap().parent().cloned().unwrap();
if parent != self.node {
let idx = tree
.children_ids(&parent)
.unwrap()
.position(|id| id == mapped_id)
.unwrap();
let node = tree.get_mut(&parent).unwrap();
let data = node.data_mut();
match data {
Data::Group { sizes, .. } => {
sizes[idx] = *mapped_size;
}
_ => unreachable!(),
};
}
}
return tiling_layer.refresh();
}
}
}
}
}
fn button(

View file

@ -7,37 +7,27 @@ use crate::{
target::{KeyboardFocusTarget, WindowGroup},
FocusDirection,
},
grabs::ResizeEdge,
layout::Orientation,
OutputNotMapped,
},
utils::prelude::*,
wayland::handlers::xdg_shell::popup::{self, get_popup_toplevel},
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
};
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
use smithay::{
backend::renderer::{element::AsRenderElements, ImportAll, Renderer},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind, PopupManager, Window},
input::{
pointer::{Focus, GrabStartData as PointerGrabStartData},
Seat,
},
output::{Output, WeakOutput},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Window},
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output,
render_elements,
utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial},
};
use std::{
borrow::Borrow,
cell::RefCell,
collections::{HashMap, VecDeque},
hash::Hash,
sync::{atomic::AtomicBool, Arc},
};
use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc};
/*
mod grabs;
pub use self::grabs::*;
*/
#[derive(Debug, Clone)]
struct OutputData {
@ -908,68 +898,58 @@ impl TilingLayout {
TilingLayout::update_space_positions(&mut self.trees, self.gaps);
}
/*
pub fn resize_request(
window: &CosmicWindow,
seat: &Seat<State>,
serial: Serial,
&self,
mapped: &CosmicMapped,
_seat: &Seat<State>,
_serial: Serial,
start_data: PointerGrabStartData<State>,
edges: ResizeEdge,
) {
// it is so stupid, that we have to do this here. TODO: Refactor grabs
let workspace = state
.common
.shell
.space_for_window_mut(window.toplevel().wl_surface())
.unwrap();
let space = &mut workspace.space;
let trees = &mut workspace.tiling_layer.trees;
) -> Option<ResizeForkGrab> {
let (output, mut node_id) = self.trees.iter().find_map(|(output, tree)| {
let root_id = tree.root_node_id()?;
tree.traverse_pre_order_ids(root_id)
.unwrap()
.find(|id| tree.get(id).unwrap().data().is_mapped(Some(mapped)))
.map(|id| (&output.output, id))
})?;
if let Some(pointer) = seat.get_pointer() {
if let Some(info) = window.user_data().get::<RefCell<WindowInfo>>() {
let output = info.borrow().output;
let tree = TilingLayout::active_tree(trees, output);
let mut node_id = info.borrow().node.clone();
while let Some((fork, child)) = TilingLayout::find_fork(tree, node_id) {
if let &Data::Fork {
ref orientation,
ref ratio,
} = tree.get(&fork).unwrap().data()
{
// found a fork
// which child are we?
let first = tree.children_ids(&fork).unwrap().next() == Some(&child);
match (first, orientation, edges) {
(true, Orientation::Horizontal, ResizeEdge::Bottom)
| (false, Orientation::Horizontal, ResizeEdge::Top)
| (true, Orientation::Vertical, ResizeEdge::Right)
| (false, Orientation::Vertical, ResizeEdge::Left) => {
let output = space.outputs().nth(output).cloned();
if let Some(output) = output {
let grab = ResizeForkGrab {
start_data,
orientation: *orientation,
initial_ratio: ratio.load(Ordering::SeqCst),
initial_size: layer_map_for_output(&output)
.non_exclusive_zone()
.size,
ratio: ratio.clone(),
};
pointer.set_grab(state, grab, serial, Focus::Clear);
}
return;
}
_ => {} // continue iterating
}
}
node_id = fork;
}
let tree = self.trees.get(output).unwrap();
while let Some(group_id) = tree.get(&node_id).unwrap().parent() {
let orientation = tree.get(group_id).unwrap().data().orientation();
if !((orientation == Orientation::Vertical
&& (edges.contains(ResizeEdge::LEFT) || edges.contains(ResizeEdge::RIGHT)))
|| (orientation == Orientation::Horizontal
&& (edges.contains(ResizeEdge::TOP) || edges.contains(ResizeEdge::BOTTOM))))
{
node_id = group_id.clone();
continue;
}
let node_idx = tree
.children_ids(group_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
let idx = match edges {
x if x.intersects(ResizeEdge::TOP_LEFT) => node_idx - 1,
_ => node_idx,
};
if idx > tree.get(&group_id).unwrap().data().len() {
return None;
}
return Some(ResizeForkGrab::new(
start_data,
group_id.clone(),
output,
tree.get(&group_id).unwrap().data(),
idx,
));
}
None
}
*/
fn last_active_window<'a>(
tree: &mut Tree<Data>,

View file

@ -34,6 +34,7 @@ use crate::{
mod element;
pub mod focus;
pub mod grabs;
pub mod layout;
mod workspace;
pub use self::element::CosmicMappedRenderElement;

View file

@ -36,10 +36,8 @@ use std::{collections::HashMap, time::Duration};
use super::{
element::CosmicMapped,
focus::{FocusStack, FocusStackMut},
layout::{
floating::{FloatingRenderElement, ResizeSurfaceGrab},
tiling::TilingRenderElement,
},
grabs::ResizeGrab,
layout::{floating::FloatingRenderElement, tiling::TilingRenderElement},
};
#[derive(Debug)]
@ -266,16 +264,20 @@ impl Workspace {
serial: Serial,
start_data: PointerGrabStartData<State>,
edges: ResizeEdge,
) -> Option<ResizeSurfaceGrab> {
) -> Option<ResizeGrab> {
if mapped.is_fullscreen() || mapped.is_maximized() {
return None;
}
let edges = edges.into();
if self.floating_layer.mapped().any(|m| m == mapped) {
self.floating_layer
.resize_request(mapped, seat, serial, start_data.clone(), edges)
.map(Into::into)
} else if self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) {
//self.tiling_layer.resize_request(mapped, seat, serial, start_data, edges)
None
self.tiling_layer
.resize_request(mapped, seat, serial, start_data, edges)
.map(Into::into)
} else {
None
}