workspace/layout: Resize windows/groups
This commit is contained in:
parent
f5dcd97c42
commit
2004705080
4 changed files with 404 additions and 183 deletions
|
|
@ -3,6 +3,7 @@ use std::sync::Weak;
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::{element::CosmicMapped, CosmicSurface},
|
shell::{element::CosmicMapped, CosmicSurface},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
|
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
|
||||||
};
|
};
|
||||||
use id_tree::NodeId;
|
use id_tree::NodeId;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
|
|
@ -50,6 +51,16 @@ impl From<KeyboardFocusTarget> for PointerFocusTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyboardFocusTarget {
|
||||||
|
pub fn toplevel(&self) -> Option<WlSurface> {
|
||||||
|
match self {
|
||||||
|
KeyboardFocusTarget::Element(mapped) => mapped.wl_surface(),
|
||||||
|
KeyboardFocusTarget::Popup(PopupKind::Xdg(xdg)) => get_popup_toplevel(&xdg),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowGroup {
|
pub struct WindowGroup {
|
||||||
pub node: NodeId,
|
pub node: NodeId,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use smithay::{
|
||||||
element::{AsRenderElements, RenderElement},
|
element::{AsRenderElements, RenderElement},
|
||||||
ImportAll, ImportMem, Renderer,
|
ImportAll, ImportMem, Renderer,
|
||||||
},
|
},
|
||||||
desktop::{layer_map_for_output, space::SpaceElement, Space},
|
desktop::{layer_map_for_output, space::SpaceElement, Space, WindowSurfaceType},
|
||||||
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
|
||||||
output::Output,
|
output::Output,
|
||||||
utils::{Logical, Point, Rectangle, Size},
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
|
|
@ -19,8 +19,9 @@ use crate::{
|
||||||
stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped,
|
stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped,
|
||||||
CosmicMappedRenderElement,
|
CosmicMappedRenderElement,
|
||||||
},
|
},
|
||||||
|
focus::target::KeyboardFocusTarget,
|
||||||
grabs::ResizeEdge,
|
grabs::ResizeEdge,
|
||||||
CosmicSurface,
|
CosmicSurface, ResizeDirection, ResizeMode,
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
|
|
@ -252,6 +253,73 @@ impl FloatingLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(
|
||||||
|
&mut self,
|
||||||
|
focused: &KeyboardFocusTarget,
|
||||||
|
direction: ResizeDirection,
|
||||||
|
edge: ResizeEdge,
|
||||||
|
amount: i32,
|
||||||
|
) -> bool {
|
||||||
|
let Some(toplevel) = focused.toplevel() else { return false };
|
||||||
|
let Some(mapped) = self.space.elements().find(|m| m.has_surface(&toplevel, WindowSurfaceType::TOPLEVEL)) else { return false };
|
||||||
|
|
||||||
|
let Some(original_geo) = self.space.element_geometry(mapped) else {
|
||||||
|
return false // we don't have that window
|
||||||
|
};
|
||||||
|
let mut geo = original_geo.clone();
|
||||||
|
|
||||||
|
if edge.contains(ResizeEdge::RIGHT) || edge.contains(ResizeEdge::LEFT) {
|
||||||
|
if direction == ResizeDirection::Inwards {
|
||||||
|
geo.size.w -= amount;
|
||||||
|
} else {
|
||||||
|
geo.size.w += amount;
|
||||||
|
}
|
||||||
|
if edge.contains(ResizeEdge::LEFT) {
|
||||||
|
if direction == ResizeDirection::Inwards {
|
||||||
|
geo.loc.x += amount;
|
||||||
|
} else {
|
||||||
|
geo.loc.x -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if edge.contains(ResizeEdge::BOTTOM) || edge.contains(ResizeEdge::TOP) {
|
||||||
|
if direction == ResizeDirection::Inwards {
|
||||||
|
geo.size.h -= amount;
|
||||||
|
} else {
|
||||||
|
geo.size.h += amount;
|
||||||
|
}
|
||||||
|
if edge.contains(ResizeEdge::TOP) {
|
||||||
|
if direction == ResizeDirection::Inwards {
|
||||||
|
geo.loc.y += amount;
|
||||||
|
} else {
|
||||||
|
geo.loc.y -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(bounding_box) = self.space.outputs()
|
||||||
|
.map(|o| self.space.output_geometry(o).unwrap())
|
||||||
|
.filter(|output_geo| output_geo.overlaps(geo))
|
||||||
|
.fold(None, |res, output_geo| match res {
|
||||||
|
None => Some(output_geo),
|
||||||
|
Some(other) => Some(other.merge(output_geo)),
|
||||||
|
}) else { return true };
|
||||||
|
|
||||||
|
geo = geo.intersection(bounding_box).unwrap();
|
||||||
|
|
||||||
|
*mapped.resize_state.lock().unwrap() = Some(ResizeState::Resizing(ResizeData {
|
||||||
|
edges: edge,
|
||||||
|
initial_window_location: original_geo.loc,
|
||||||
|
initial_window_size: original_geo.size,
|
||||||
|
}));
|
||||||
|
|
||||||
|
mapped.set_resizing(true);
|
||||||
|
mapped.set_geometry(geo);
|
||||||
|
mapped.configure();
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
|
pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
|
||||||
self.space.elements().rev()
|
self.space.elements().rev()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
grabs::ResizeEdge,
|
grabs::ResizeEdge,
|
||||||
layout::Orientation,
|
layout::Orientation,
|
||||||
CosmicSurface, OutputNotMapped, OverviewMode,
|
CosmicSurface, OutputNotMapped, OverviewMode, ResizeDirection, ResizeMode,
|
||||||
},
|
},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -650,7 +650,8 @@ impl TilingLayout {
|
||||||
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 Some((node_id, data)) = TilingLayout::currently_focused_node(&mut tree, seat) else {
|
let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return MoveResult::Done };
|
||||||
|
let Some((node_id, data)) = TilingLayout::currently_focused_node(&mut tree, &seat.active_output(), target) else {
|
||||||
return MoveResult::Done
|
return MoveResult::Done
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -971,181 +972,181 @@ impl TilingLayout {
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
let tree = &self.queues.get(&output).unwrap().trees.back().unwrap().0;
|
let tree = &self.queues.get(&output).unwrap().trees.back().unwrap().0;
|
||||||
|
|
||||||
if let Some(focused) = TilingLayout::currently_focused_node(tree, seat).or_else(|| {
|
let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return FocusResult::None };
|
||||||
|
let Some(focused) = TilingLayout::currently_focused_node(tree, &seat.active_output(), target).or_else(|| {
|
||||||
TilingLayout::last_active_window(tree, focus_stack)
|
TilingLayout::last_active_window(tree, focus_stack)
|
||||||
.map(|(id, mapped)| (id, FocusedNodeData::Window(mapped)))
|
.map(|(id, mapped)| (id, FocusedNodeData::Window(mapped)))
|
||||||
}) {
|
}) else { return FocusResult::None };
|
||||||
let (last_node_id, data) = focused;
|
|
||||||
|
|
||||||
// stacks may handle focus internally
|
let (last_node_id, data) = focused;
|
||||||
if let FocusedNodeData::Window(window) = data.clone() {
|
|
||||||
if window.handle_focus(direction) {
|
// stacks may handle focus internally
|
||||||
return FocusResult::Handled;
|
if let FocusedNodeData::Window(window) = data.clone() {
|
||||||
}
|
if window.handle_focus(direction) {
|
||||||
|
return FocusResult::Handled;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if direction == FocusDirection::In {
|
if direction == FocusDirection::In {
|
||||||
if let FocusedNodeData::Group(mut stack, _) = data.clone() {
|
if let FocusedNodeData::Group(mut stack, _) = data.clone() {
|
||||||
let maybe_id = stack.pop().unwrap();
|
let maybe_id = stack.pop().unwrap();
|
||||||
let id = if tree
|
let id = if tree
|
||||||
.children_ids(&last_node_id)
|
.children_ids(&last_node_id)
|
||||||
.unwrap()
|
|
||||||
.any(|id| id == &maybe_id)
|
|
||||||
{
|
|
||||||
Some(maybe_id)
|
|
||||||
} else {
|
|
||||||
tree.children_ids(&last_node_id).unwrap().next().cloned()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(id) = id {
|
|
||||||
return match tree.get(&id).unwrap().data() {
|
|
||||||
Data::Mapped { mapped, .. } => {
|
|
||||||
if mapped.is_stack() {
|
|
||||||
mapped.stack_ref().unwrap().focus_stack();
|
|
||||||
}
|
|
||||||
FocusResult::Some(mapped.clone().into())
|
|
||||||
}
|
|
||||||
Data::Group { alive, .. } => FocusResult::Some(
|
|
||||||
WindowGroup {
|
|
||||||
node: id,
|
|
||||||
output: output.downgrade(),
|
|
||||||
alive: Arc::downgrade(alive),
|
|
||||||
focus_stack: stack,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut node_id = last_node_id.clone();
|
|
||||||
while let Some(group) = tree.get(&node_id).unwrap().parent() {
|
|
||||||
let child = node_id.clone();
|
|
||||||
let group_data = tree.get(&group).unwrap().data();
|
|
||||||
let main_orientation = group_data.orientation();
|
|
||||||
assert!(group_data.is_group());
|
|
||||||
|
|
||||||
if direction == FocusDirection::Out {
|
|
||||||
return FocusResult::Some(
|
|
||||||
WindowGroup {
|
|
||||||
node: group.clone(),
|
|
||||||
output: output.downgrade(),
|
|
||||||
alive: match group_data {
|
|
||||||
&Data::Group { ref alive, .. } => Arc::downgrade(alive),
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
focus_stack: match data {
|
|
||||||
FocusedNodeData::Group(mut stack, _) => {
|
|
||||||
stack.push(child);
|
|
||||||
stack
|
|
||||||
}
|
|
||||||
_ => vec![child],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// which child are we?
|
|
||||||
let idx = tree
|
|
||||||
.children_ids(&group)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.position(|id| id == &child)
|
.any(|id| id == &maybe_id)
|
||||||
.unwrap();
|
{
|
||||||
let len = group_data.len();
|
Some(maybe_id)
|
||||||
|
} else {
|
||||||
let focus_subtree = match (main_orientation, direction) {
|
tree.children_ids(&last_node_id).unwrap().next().cloned()
|
||||||
(Orientation::Horizontal, FocusDirection::Down)
|
|
||||||
| (Orientation::Vertical, FocusDirection::Right)
|
|
||||||
if idx < (len - 1) =>
|
|
||||||
{
|
|
||||||
tree.children_ids(&group).unwrap().skip(idx + 1).next()
|
|
||||||
}
|
|
||||||
(Orientation::Horizontal, FocusDirection::Up)
|
|
||||||
| (Orientation::Vertical, FocusDirection::Left)
|
|
||||||
if idx > 0 =>
|
|
||||||
{
|
|
||||||
tree.children_ids(&group).unwrap().skip(idx - 1).next()
|
|
||||||
}
|
|
||||||
_ => None, // continue iterating
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if focus_subtree.is_some() {
|
if let Some(id) = id {
|
||||||
let mut node_id = focus_subtree;
|
return match tree.get(&id).unwrap().data() {
|
||||||
while node_id.is_some() {
|
Data::Mapped { mapped, .. } => {
|
||||||
match tree.get(node_id.unwrap()).unwrap().data() {
|
if mapped.is_stack() {
|
||||||
Data::Group { orientation, .. } if orientation == &main_orientation => {
|
mapped.stack_ref().unwrap().focus_stack();
|
||||||
// if the group is layed out in the direction we care about,
|
}
|
||||||
// we can just use the first or last element (depending on the direction)
|
FocusResult::Some(mapped.clone().into())
|
||||||
match direction {
|
}
|
||||||
FocusDirection::Down | FocusDirection::Right => {
|
Data::Group { alive, .. } => FocusResult::Some(
|
||||||
node_id = tree
|
WindowGroup {
|
||||||
.children_ids(node_id.as_ref().unwrap())
|
node: id,
|
||||||
.unwrap()
|
output: output.downgrade(),
|
||||||
.next();
|
alive: Arc::downgrade(alive),
|
||||||
}
|
focus_stack: stack,
|
||||||
FocusDirection::Up | FocusDirection::Left => {
|
}
|
||||||
node_id = tree
|
.into(),
|
||||||
.children_ids(node_id.as_ref().unwrap())
|
),
|
||||||
.unwrap()
|
};
|
||||||
.last();
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
}
|
||||||
|
|
||||||
|
let mut node_id = last_node_id.clone();
|
||||||
|
while let Some(group) = tree.get(&node_id).unwrap().parent() {
|
||||||
|
let child = node_id.clone();
|
||||||
|
let group_data = tree.get(&group).unwrap().data();
|
||||||
|
let main_orientation = group_data.orientation();
|
||||||
|
assert!(group_data.is_group());
|
||||||
|
|
||||||
|
if direction == FocusDirection::Out {
|
||||||
|
return FocusResult::Some(
|
||||||
|
WindowGroup {
|
||||||
|
node: group.clone(),
|
||||||
|
output: output.downgrade(),
|
||||||
|
alive: match group_data {
|
||||||
|
&Data::Group { ref alive, .. } => Arc::downgrade(alive),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
focus_stack: match data {
|
||||||
|
FocusedNodeData::Group(mut stack, _) => {
|
||||||
|
stack.push(child);
|
||||||
|
stack
|
||||||
|
}
|
||||||
|
_ => vec![child],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// which child are we?
|
||||||
|
let idx = tree
|
||||||
|
.children_ids(&group)
|
||||||
|
.unwrap()
|
||||||
|
.position(|id| id == &child)
|
||||||
|
.unwrap();
|
||||||
|
let len = group_data.len();
|
||||||
|
|
||||||
|
let focus_subtree = match (main_orientation, direction) {
|
||||||
|
(Orientation::Horizontal, FocusDirection::Down)
|
||||||
|
| (Orientation::Vertical, FocusDirection::Right)
|
||||||
|
if idx < (len - 1) =>
|
||||||
|
{
|
||||||
|
tree.children_ids(&group).unwrap().skip(idx + 1).next()
|
||||||
|
}
|
||||||
|
(Orientation::Horizontal, FocusDirection::Up)
|
||||||
|
| (Orientation::Vertical, FocusDirection::Left)
|
||||||
|
if idx > 0 =>
|
||||||
|
{
|
||||||
|
tree.children_ids(&group).unwrap().skip(idx - 1).next()
|
||||||
|
}
|
||||||
|
_ => None, // continue iterating
|
||||||
|
};
|
||||||
|
|
||||||
|
if focus_subtree.is_some() {
|
||||||
|
let mut node_id = focus_subtree;
|
||||||
|
while node_id.is_some() {
|
||||||
|
match tree.get(node_id.unwrap()).unwrap().data() {
|
||||||
|
Data::Group { orientation, .. } if orientation == &main_orientation => {
|
||||||
|
// if the group is layed out in the direction we care about,
|
||||||
|
// we can just use the first or last element (depending on the direction)
|
||||||
|
match direction {
|
||||||
|
FocusDirection::Down | FocusDirection::Right => {
|
||||||
|
node_id = tree
|
||||||
|
.children_ids(node_id.as_ref().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.next();
|
||||||
}
|
}
|
||||||
}
|
FocusDirection::Up | FocusDirection::Left => {
|
||||||
Data::Group { .. } => {
|
node_id = tree
|
||||||
let center = {
|
.children_ids(node_id.as_ref().unwrap())
|
||||||
let geo = tree.get(&last_node_id).unwrap().data().geometry();
|
.unwrap()
|
||||||
let mut point = geo.loc;
|
.last();
|
||||||
match direction {
|
}
|
||||||
FocusDirection::Down => {
|
_ => unreachable!(),
|
||||||
point += Point::from((geo.size.w / 2 - 1, geo.size.h))
|
|
||||||
}
|
|
||||||
FocusDirection::Up => point.x += geo.size.w / 2 - 1,
|
|
||||||
FocusDirection::Left => point.y += geo.size.h / 2 - 1,
|
|
||||||
FocusDirection::Right => {
|
|
||||||
point += Point::from((geo.size.w, geo.size.h / 2 - 1))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
point.to_f64()
|
|
||||||
};
|
|
||||||
|
|
||||||
let distance = |candidate: &&NodeId| -> f64 {
|
|
||||||
let geo = tree.get(candidate).unwrap().data().geometry();
|
|
||||||
let mut point = geo.loc;
|
|
||||||
match direction {
|
|
||||||
FocusDirection::Up => {
|
|
||||||
point += Point::from((geo.size.w / 2, geo.size.h))
|
|
||||||
}
|
|
||||||
FocusDirection::Down => point.x += geo.size.w,
|
|
||||||
FocusDirection::Right => point.y += geo.size.h / 2,
|
|
||||||
FocusDirection::Left => {
|
|
||||||
point += Point::from((geo.size.w, geo.size.h / 2))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let point = point.to_f64();
|
|
||||||
((point.x - center.x).powi(2) + (point.y - center.y).powi(2))
|
|
||||||
.sqrt()
|
|
||||||
};
|
|
||||||
|
|
||||||
node_id = tree
|
|
||||||
.children_ids(node_id.as_ref().unwrap())
|
|
||||||
.unwrap()
|
|
||||||
.min_by(|node1, node2| {
|
|
||||||
distance(node1).abs().total_cmp(&distance(node2).abs())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Data::Mapped { mapped, .. } => {
|
|
||||||
return FocusResult::Some(mapped.clone().into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Data::Group { .. } => {
|
||||||
|
let center = {
|
||||||
|
let geo = tree.get(&last_node_id).unwrap().data().geometry();
|
||||||
|
let mut point = geo.loc;
|
||||||
|
match direction {
|
||||||
|
FocusDirection::Down => {
|
||||||
|
point += Point::from((geo.size.w / 2 - 1, geo.size.h))
|
||||||
|
}
|
||||||
|
FocusDirection::Up => point.x += geo.size.w / 2 - 1,
|
||||||
|
FocusDirection::Left => point.y += geo.size.h / 2 - 1,
|
||||||
|
FocusDirection::Right => {
|
||||||
|
point += Point::from((geo.size.w, geo.size.h / 2 - 1))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
point.to_f64()
|
||||||
|
};
|
||||||
|
|
||||||
|
let distance = |candidate: &&NodeId| -> f64 {
|
||||||
|
let geo = tree.get(candidate).unwrap().data().geometry();
|
||||||
|
let mut point = geo.loc;
|
||||||
|
match direction {
|
||||||
|
FocusDirection::Up => {
|
||||||
|
point += Point::from((geo.size.w / 2, geo.size.h))
|
||||||
|
}
|
||||||
|
FocusDirection::Down => point.x += geo.size.w,
|
||||||
|
FocusDirection::Right => point.y += geo.size.h / 2,
|
||||||
|
FocusDirection::Left => {
|
||||||
|
point += Point::from((geo.size.w, geo.size.h / 2))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let point = point.to_f64();
|
||||||
|
((point.x - center.x).powi(2) + (point.y - center.y).powi(2)).sqrt()
|
||||||
|
};
|
||||||
|
|
||||||
|
node_id = tree
|
||||||
|
.children_ids(node_id.as_ref().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.min_by(|node1, node2| {
|
||||||
|
distance(node1).abs().total_cmp(&distance(node2).abs())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Data::Mapped { mapped, .. } => {
|
||||||
|
return FocusResult::Some(mapped.clone().into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
node_id = group.clone();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
node_id = group.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1161,7 +1162,10 @@ impl TilingLayout {
|
||||||
let Some(queue) = self.queues.get_mut(&output) else { return };
|
let Some(queue) = self.queues.get_mut(&output) else { return };
|
||||||
let mut tree = queue.trees.back().unwrap().0.copy_clone();
|
let mut tree = queue.trees.back().unwrap().0.copy_clone();
|
||||||
|
|
||||||
if let Some((last_active, _)) = TilingLayout::currently_focused_node(&tree, seat) {
|
let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return };
|
||||||
|
if let Some((last_active, _)) =
|
||||||
|
TilingLayout::currently_focused_node(&tree, &seat.active_output(), target)
|
||||||
|
{
|
||||||
if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() {
|
if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() {
|
||||||
if let &mut Data::Group {
|
if let &mut Data::Group {
|
||||||
ref mut orientation,
|
ref mut orientation,
|
||||||
|
|
@ -1203,8 +1207,9 @@ impl TilingLayout {
|
||||||
let Some(queue) = self.queues.get_mut(&output) else { return };
|
let Some(queue) = self.queues.get_mut(&output) else { return };
|
||||||
let mut tree = queue.trees.back().unwrap().0.copy_clone();
|
let mut tree = queue.trees.back().unwrap().0.copy_clone();
|
||||||
|
|
||||||
|
let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return };
|
||||||
if let Some((last_active, last_active_data)) =
|
if let Some((last_active, last_active_data)) =
|
||||||
TilingLayout::currently_focused_node(&tree, seat)
|
TilingLayout::currently_focused_node(&tree, &seat.active_output(), target)
|
||||||
{
|
{
|
||||||
match last_active_data {
|
match last_active_data {
|
||||||
FocusedNodeData::Window(mapped) => {
|
FocusedNodeData::Window(mapped) => {
|
||||||
|
|
@ -1437,6 +1442,114 @@ impl TilingLayout {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn possible_resizes(tree: &Tree<Data>, mut node_id: NodeId) -> ResizeEdge {
|
||||||
|
let mut edges = ResizeEdge::empty();
|
||||||
|
|
||||||
|
while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() {
|
||||||
|
let orientation = tree.get(&group_id).unwrap().data().orientation();
|
||||||
|
|
||||||
|
let node_idx = tree
|
||||||
|
.children_ids(&group_id)
|
||||||
|
.unwrap()
|
||||||
|
.position(|id| id == &node_id)
|
||||||
|
.unwrap();
|
||||||
|
let total = tree.children_ids(&group_id).unwrap().count();
|
||||||
|
if orientation == Orientation::Vertical {
|
||||||
|
if node_idx > 0 {
|
||||||
|
edges.insert(ResizeEdge::LEFT);
|
||||||
|
}
|
||||||
|
if node_idx < total - 1 {
|
||||||
|
edges.insert(ResizeEdge::RIGHT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if node_idx > 0 {
|
||||||
|
edges.insert(ResizeEdge::TOP);
|
||||||
|
}
|
||||||
|
if node_idx < total - 1 {
|
||||||
|
edges.insert(ResizeEdge::BOTTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_id = group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
edges
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(
|
||||||
|
&mut self,
|
||||||
|
focused: &KeyboardFocusTarget,
|
||||||
|
direction: ResizeDirection,
|
||||||
|
edges: ResizeEdge,
|
||||||
|
amount: i32,
|
||||||
|
) -> bool {
|
||||||
|
let Some((output, mut node_id)) = self.queues.iter().find_map(|(output, queue)| {
|
||||||
|
let tree = &queue.trees.back().unwrap().0;
|
||||||
|
let root_id = tree.root_node_id()?;
|
||||||
|
let id = match TilingLayout::currently_focused_node(tree, &output.output, focused.clone()) {
|
||||||
|
Some((_id, FocusedNodeData::Window(mapped))) => // we need to make sure the id belongs to this tree..
|
||||||
|
tree.traverse_pre_order_ids(root_id)
|
||||||
|
.unwrap()
|
||||||
|
.find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped))),
|
||||||
|
Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the output was already matched, so the id is to be trusted
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
id.map(|id| (output.output.clone(), id))
|
||||||
|
}) else { return false };
|
||||||
|
|
||||||
|
let queue = self.queues.get_mut(&output).unwrap();
|
||||||
|
let mut tree = queue.trees.back().unwrap().0.copy_clone();
|
||||||
|
|
||||||
|
while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() {
|
||||||
|
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 Some(other_idx) = (match edges {
|
||||||
|
x if x.intersects(ResizeEdge::TOP_LEFT) => node_idx.checked_sub(1),
|
||||||
|
_ => if tree.children_ids(&group_id).unwrap().count() - 1 > node_idx { Some(node_idx + 1) } else { None },
|
||||||
|
}) else {
|
||||||
|
node_id = group_id.clone();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = tree.get_mut(&group_id).unwrap().data_mut();
|
||||||
|
|
||||||
|
match data {
|
||||||
|
Data::Group { sizes, .. } => {
|
||||||
|
let (shrink_idx, grow_idx) = if direction == ResizeDirection::Inwards {
|
||||||
|
(node_idx, other_idx)
|
||||||
|
} else {
|
||||||
|
(other_idx, node_idx)
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_size = sizes[shrink_idx];
|
||||||
|
sizes[shrink_idx] = (old_size - amount).max(10);
|
||||||
|
let diff = old_size - sizes[shrink_idx];
|
||||||
|
sizes[grow_idx] += diff;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps);
|
||||||
|
queue.push_tree(tree, Duration::ZERO, blocker);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn last_active_window<'a>(
|
fn last_active_window<'a>(
|
||||||
tree: &Tree<Data>,
|
tree: &Tree<Data>,
|
||||||
mut focus_stack: impl Iterator<Item = &'a CosmicMapped>,
|
mut focus_stack: impl Iterator<Item = &'a CosmicMapped>,
|
||||||
|
|
@ -1451,10 +1564,9 @@ impl TilingLayout {
|
||||||
|
|
||||||
fn currently_focused_node(
|
fn currently_focused_node(
|
||||||
tree: &Tree<Data>,
|
tree: &Tree<Data>,
|
||||||
seat: &Seat<State>,
|
output: &Output,
|
||||||
|
mut target: KeyboardFocusTarget,
|
||||||
) -> Option<(NodeId, FocusedNodeData)> {
|
) -> Option<(NodeId, FocusedNodeData)> {
|
||||||
let mut target = seat.get_keyboard().unwrap().current_focus()?;
|
|
||||||
|
|
||||||
// if the focus is currently on a popup, treat it's toplevel as the target
|
// if the focus is currently on a popup, treat it's toplevel as the target
|
||||||
if let KeyboardFocusTarget::Popup(popup) = target {
|
if let KeyboardFocusTarget::Popup(popup) = target {
|
||||||
let toplevel_surface = match popup {
|
let toplevel_surface = match popup {
|
||||||
|
|
@ -1487,7 +1599,7 @@ impl TilingLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyboardFocusTarget::Group(window_group) => {
|
KeyboardFocusTarget::Group(window_group) => {
|
||||||
if window_group.output == seat.active_output() {
|
if window_group.output == *output {
|
||||||
let node = tree.get(&window_group.node).ok()?;
|
let node = tree.get(&window_group.node).ok()?;
|
||||||
if node.data().is_group() {
|
if node.data().is_group() {
|
||||||
return Some((
|
return Some((
|
||||||
|
|
@ -1978,7 +2090,14 @@ where
|
||||||
let alpha = alpha * transition;
|
let alpha = alpha * transition;
|
||||||
|
|
||||||
let focused = seat
|
let focused = seat
|
||||||
.and_then(|seat| TilingLayout::currently_focused_node(&tree, seat))
|
.and_then(|seat| {
|
||||||
|
seat.get_keyboard()
|
||||||
|
.unwrap()
|
||||||
|
.current_focus()
|
||||||
|
.and_then(|target| {
|
||||||
|
TilingLayout::currently_focused_node(&tree, &seat.active_output(), target)
|
||||||
|
})
|
||||||
|
})
|
||||||
.map(|(id, _)| id);
|
.map(|(id, _)| id);
|
||||||
|
|
||||||
let has_potential_groups = if let Some(focused_id) = focused.as_ref() {
|
let has_potential_groups = if let Some(focused_id) = focused.as_ref() {
|
||||||
|
|
@ -2318,7 +2437,18 @@ where
|
||||||
CosmicStackRenderElement<R>: RenderElement<R>,
|
CosmicStackRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let focused = seat
|
let focused = seat
|
||||||
.and_then(|seat| TilingLayout::currently_focused_node(&target_tree, seat))
|
.and_then(|seat| {
|
||||||
|
seat.get_keyboard()
|
||||||
|
.unwrap()
|
||||||
|
.current_focus()
|
||||||
|
.and_then(|target| {
|
||||||
|
TilingLayout::currently_focused_node(
|
||||||
|
&target_tree,
|
||||||
|
&seat.active_output(),
|
||||||
|
target,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
.map(|(id, _)| id);
|
.map(|(id, _)| id);
|
||||||
|
|
||||||
let mut group_backdrop = None;
|
let mut group_backdrop = None;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ use crate::{
|
||||||
xwayland::XWaylandState,
|
xwayland::XWaylandState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::LoopHandle;
|
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
|
|
@ -45,12 +44,13 @@ use smithay::{
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, time::Instant};
|
use std::{collections::HashMap, time::Instant};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped},
|
element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped},
|
||||||
focus::{target::KeyboardFocusTarget, FocusStack, FocusStackMut},
|
focus::{target::KeyboardFocusTarget, FocusStack, FocusStackMut},
|
||||||
grabs::{ResizeEdge, ResizeGrab},
|
grabs::{ResizeEdge, ResizeGrab},
|
||||||
CosmicMappedRenderElement, CosmicSurface, ResizeDirection,
|
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -341,15 +341,27 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, seat: &Seat<State>, direction: ResizeDirection, edge: ResizeEdge) {
|
pub fn resize(
|
||||||
if let Some(KeyboardFocusTarget::Fullscreen(_)) =
|
&mut self,
|
||||||
seat.get_keyboard().unwrap().current_focus()
|
focused: &KeyboardFocusTarget,
|
||||||
{
|
direction: ResizeDirection,
|
||||||
return;
|
edge: ResizeEdge,
|
||||||
|
amount: i32,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(toplevel) = focused.toplevel() {
|
||||||
|
if self
|
||||||
|
.fullscreen
|
||||||
|
.values()
|
||||||
|
.any(|surface| surface.wl_surface().as_ref() == Some(&toplevel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.floating_layer.resize(seat, direction, edge) {
|
if !self.floating_layer.resize(focused, direction, edge, amount) {
|
||||||
self.tiling_layer.resize(seat, direction, edge);
|
self.tiling_layer.resize(focused, direction, edge, amount)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue