shell: One workspace per output
This commit is contained in:
parent
42aaafe586
commit
72df9d07e6
21 changed files with 1561 additions and 1987 deletions
|
|
@ -11,7 +11,6 @@ use smithay::{
|
|||
utils::{Logical, Point, Rectangle, Size},
|
||||
wayland::seat::WaylandFocus,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage},
|
||||
|
|
@ -28,9 +27,7 @@ use crate::{
|
|||
},
|
||||
state::State,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::xdg_shell::popup::get_popup_toplevel, protocols::toplevel_info::ToplevelInfoState,
|
||||
},
|
||||
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
|
||||
};
|
||||
|
||||
mod grabs;
|
||||
|
|
@ -42,67 +39,53 @@ pub struct FloatingLayout {
|
|||
}
|
||||
|
||||
impl FloatingLayout {
|
||||
pub fn new() -> FloatingLayout {
|
||||
Default::default()
|
||||
pub fn new(output: &Output) -> FloatingLayout {
|
||||
let mut layout = Self::default();
|
||||
layout.space.map_output(output, (0, 0));
|
||||
layout
|
||||
}
|
||||
|
||||
pub fn map_output(&mut self, output: &Output, location: Point<i32, Logical>) {
|
||||
self.space.map_output(output, location)
|
||||
}
|
||||
pub fn set_output(&mut self, output: &Output) {
|
||||
let old_output = self.space.outputs().next().unwrap().clone();
|
||||
self.space.unmap_output(&old_output);
|
||||
self.space.map_output(output, (0, 0));
|
||||
|
||||
/*
|
||||
TODO: rescale all positions? (evem rescale windows?)
|
||||
*/
|
||||
|
||||
pub fn unmap_output(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
toplevel_info: &mut ToplevelInfoState<State, CosmicSurface>,
|
||||
) {
|
||||
let windows = self
|
||||
.space
|
||||
.elements_for_output(output)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
for window in &windows {
|
||||
for (toplevel, _) in window.windows() {
|
||||
toplevel_info.toplevel_leave_output(&toplevel, output);
|
||||
}
|
||||
}
|
||||
self.space.unmap_output(output);
|
||||
self.refresh();
|
||||
for window in &windows {
|
||||
for output in self.space.outputs_for_element(&window) {
|
||||
for (toplevel, _) in window.windows() {
|
||||
toplevel_info.toplevel_enter_output(&toplevel, &output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
&mut self,
|
||||
mapped: impl Into<CosmicMapped>,
|
||||
seat: &Seat<State>,
|
||||
position: impl Into<Option<Point<i32, Logical>>>,
|
||||
position: impl Into<Option<Point<i32, Local>>>,
|
||||
) {
|
||||
let mapped = mapped.into();
|
||||
let output = seat.active_output();
|
||||
let position = position.into();
|
||||
|
||||
self.map_internal(mapped, &output, position)
|
||||
self.map_internal(mapped, position, None)
|
||||
}
|
||||
|
||||
pub(in crate::shell) fn map_internal(
|
||||
&mut self,
|
||||
mapped: CosmicMapped,
|
||||
output: &Output,
|
||||
position: Option<Point<i32, Logical>>,
|
||||
position: Option<Point<i32, Local>>,
|
||||
size: Option<Size<i32, Logical>>,
|
||||
) {
|
||||
let mut win_geo = mapped.geometry();
|
||||
let mut win_geo = mapped.geometry().as_local();
|
||||
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone();
|
||||
mapped.set_bounds(geometry.size);
|
||||
let last_geometry = mapped.last_geometry.lock().unwrap().clone();
|
||||
|
||||
if let Some(size) = last_geometry.map(|g| g.size) {
|
||||
if let Some(size) = size
|
||||
.map(SizeExt::as_local)
|
||||
.or(last_geometry.map(|g| g.size))
|
||||
{
|
||||
win_geo.size = size;
|
||||
} else {
|
||||
let (min_size, max_size) = (
|
||||
|
|
@ -150,18 +133,10 @@ impl FloatingLayout {
|
|||
});
|
||||
|
||||
mapped.set_tiled(false);
|
||||
let offset = output.geometry().loc
|
||||
- self
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.map(|g| g.loc)
|
||||
.unwrap_or_default();
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
position + offset,
|
||||
win_geo.size,
|
||||
));
|
||||
mapped
|
||||
.set_geometry(Rectangle::from_loc_and_size(position, win_geo.size).to_global(&output));
|
||||
mapped.configure();
|
||||
self.space.map_element(mapped, position, false);
|
||||
self.space.map_element(mapped, position.as_logical(), false);
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, window: &CosmicMapped) -> bool {
|
||||
|
|
@ -170,10 +145,8 @@ impl FloatingLayout {
|
|||
|
||||
if !is_maximized {
|
||||
if let Some(location) = self.space.element_location(window) {
|
||||
*window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size(
|
||||
location,
|
||||
window.geometry().size,
|
||||
));
|
||||
*window.last_geometry.lock().unwrap() =
|
||||
Some(Rectangle::from_loc_and_size(location, window.geometry().size).as_local());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,8 +155,8 @@ impl FloatingLayout {
|
|||
was_unmaped
|
||||
}
|
||||
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
|
||||
self.space.element_geometry(elem)
|
||||
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Local>> {
|
||||
self.space.element_geometry(elem).map(RectExt::as_local)
|
||||
}
|
||||
|
||||
pub fn maximize_request(&mut self, window: &CosmicSurface) {
|
||||
|
|
@ -339,13 +312,10 @@ impl FloatingLayout {
|
|||
}));
|
||||
|
||||
mapped.set_resizing(true);
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
match mapped.active_window() {
|
||||
CosmicSurface::X11(s) => s.geometry().loc,
|
||||
_ => (0, 0).into(),
|
||||
},
|
||||
geo.size,
|
||||
));
|
||||
mapped.set_geometry(
|
||||
geo.as_local()
|
||||
.to_global(self.space.outputs().next().unwrap()),
|
||||
);
|
||||
mapped.configure();
|
||||
|
||||
true
|
||||
|
|
@ -503,83 +473,26 @@ impl FloatingLayout {
|
|||
{
|
||||
// TODO what about windows leaving to the top with no headerbar to drag? can that happen? (Probably if the user is moving outputs down)
|
||||
*element.last_geometry.lock().unwrap() = None;
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
self.map_internal(element, &output, None);
|
||||
self.map_internal(element, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn most_overlapped_output_for_element(&self, elem: &CosmicMapped) -> Option<Output> {
|
||||
let elem_geo = self.space.element_geometry(elem)?;
|
||||
|
||||
if self.space.outputs().nth(1).is_none() {
|
||||
return self.space.outputs().next().cloned();
|
||||
}
|
||||
|
||||
Some(
|
||||
self.space
|
||||
.outputs_for_element(elem)
|
||||
.into_iter()
|
||||
.max_by_key(|o| {
|
||||
let output_geo = self.space.output_geometry(o).unwrap();
|
||||
if let Some(intersection) = output_geo.intersection(elem_geo) {
|
||||
intersection.size.w * intersection.size.h
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.unwrap_or(self.space.outputs().next().unwrap().clone()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, other: FloatingLayout) {
|
||||
let mut output_pos_map = HashMap::new();
|
||||
for output in self.space.outputs() {
|
||||
output_pos_map.insert(
|
||||
output.clone(),
|
||||
self.space.output_geometry(output).unwrap().loc
|
||||
- other
|
||||
.space
|
||||
.output_geometry(output)
|
||||
.map(|geo| geo.loc)
|
||||
.unwrap_or_else(|| (0, 0).into()),
|
||||
);
|
||||
}
|
||||
for element in other.space.elements() {
|
||||
let mut elem_geo = other.space.element_geometry(element).unwrap();
|
||||
let output = other
|
||||
let elem_loc = other
|
||||
.space
|
||||
.outputs_for_element(element)
|
||||
.into_iter()
|
||||
.filter(|o| self.space.outputs().any(|o2| o == o2))
|
||||
.max_by_key(|o| {
|
||||
let output_geo = other.space.output_geometry(o).unwrap();
|
||||
let intersection = output_geo.intersection(elem_geo).unwrap();
|
||||
intersection.size.w * intersection.size.h
|
||||
})
|
||||
.unwrap_or(self.space.outputs().next().unwrap().clone());
|
||||
elem_geo.loc += output_pos_map
|
||||
.get(&output)
|
||||
.copied()
|
||||
.unwrap_or_else(|| (0, 0).into());
|
||||
let offset = output.geometry().loc
|
||||
- self
|
||||
.space
|
||||
.output_geometry(&output)
|
||||
.map(|g| g.loc)
|
||||
.unwrap_or_default();
|
||||
element.set_geometry(Rectangle::from_loc_and_size(
|
||||
elem_geo.loc + offset,
|
||||
elem_geo.size,
|
||||
));
|
||||
self.space.map_element(element.clone(), elem_geo.loc, false);
|
||||
.element_geometry(element)
|
||||
.unwrap()
|
||||
.loc
|
||||
.as_local();
|
||||
self.map_internal(element.clone(), Some(elem_loc), None);
|
||||
}
|
||||
self.refresh(); //fixup any out of bounds elements
|
||||
}
|
||||
|
||||
pub fn render_output<R>(
|
||||
pub fn render<R>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
focused: Option<&CosmicMapped>,
|
||||
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||
indicator_thickness: u8,
|
||||
|
|
@ -598,68 +511,65 @@ impl FloatingLayout {
|
|||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
let output_scale = output.current_scale().fractional_scale();
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
|
||||
let mut window_elements = Vec::new();
|
||||
let mut popup_elements = Vec::new();
|
||||
|
||||
self.space
|
||||
.elements_for_output(output)
|
||||
.rev()
|
||||
.for_each(|elem| {
|
||||
let render_location = self.space.element_location(elem).unwrap()
|
||||
- output_geo.loc
|
||||
- elem.geometry().loc;
|
||||
let (w_elements, p_elements) = elem.split_render_elements(
|
||||
renderer,
|
||||
render_location.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha,
|
||||
);
|
||||
self.space.elements().rev().for_each(|elem| {
|
||||
let render_location = self.space.element_location(elem).unwrap() - elem.geometry().loc;
|
||||
let (w_elements, p_elements) = elem.split_render_elements(
|
||||
renderer,
|
||||
render_location.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha,
|
||||
);
|
||||
|
||||
if focused == Some(elem) {
|
||||
let mut indicator_geometry = Rectangle::from_loc_and_size(
|
||||
self.space.element_location(elem).unwrap() - output_geo.loc,
|
||||
elem.geometry().size,
|
||||
if focused == Some(elem) {
|
||||
let mut indicator_geometry = Rectangle::from_loc_and_size(
|
||||
self.space.element_location(elem).unwrap(),
|
||||
elem.geometry().size,
|
||||
)
|
||||
.as_local();
|
||||
|
||||
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||
indicator_geometry.loc -= (18, 18).into();
|
||||
indicator_geometry.size += (36, 36).into();
|
||||
resize.resize(indicator_geometry.size.as_logical());
|
||||
resize.output_enter(output, Rectangle::default() /* unused */);
|
||||
window_elements.extend(
|
||||
resize
|
||||
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||
renderer,
|
||||
indicator_geometry
|
||||
.loc
|
||||
.as_logical()
|
||||
.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha * mode.alpha().unwrap_or(1.0),
|
||||
)
|
||||
.into_iter()
|
||||
.map(CosmicMappedRenderElement::Window),
|
||||
);
|
||||
|
||||
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||
indicator_geometry.loc -= (18, 18).into();
|
||||
indicator_geometry.size += (36, 36).into();
|
||||
resize.resize(indicator_geometry.size);
|
||||
resize.output_enter(output, output_geo);
|
||||
window_elements.extend(
|
||||
resize
|
||||
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||
renderer,
|
||||
indicator_geometry
|
||||
.loc
|
||||
.to_physical_precise_round(output_scale),
|
||||
output_scale.into(),
|
||||
alpha * mode.alpha().unwrap_or(1.0),
|
||||
)
|
||||
.into_iter()
|
||||
.map(CosmicMappedRenderElement::Window),
|
||||
);
|
||||
}
|
||||
|
||||
if indicator_thickness > 0 {
|
||||
let element = IndicatorShader::focus_element(
|
||||
renderer,
|
||||
Key::Window(Usage::FocusIndicator, elem.clone()),
|
||||
indicator_geometry,
|
||||
indicator_thickness,
|
||||
output_scale,
|
||||
alpha,
|
||||
);
|
||||
window_elements.push(element.into());
|
||||
}
|
||||
}
|
||||
|
||||
window_elements.extend(w_elements);
|
||||
popup_elements.extend(p_elements);
|
||||
});
|
||||
if indicator_thickness > 0 {
|
||||
let element = IndicatorShader::focus_element(
|
||||
renderer,
|
||||
Key::Window(Usage::FocusIndicator, elem.clone()),
|
||||
indicator_geometry,
|
||||
indicator_thickness,
|
||||
output_scale,
|
||||
alpha,
|
||||
);
|
||||
window_elements.push(element.into());
|
||||
}
|
||||
}
|
||||
|
||||
window_elements.extend(w_elements);
|
||||
popup_elements.extend(p_elements);
|
||||
});
|
||||
|
||||
(window_elements, popup_elements)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,67 +137,64 @@ impl PointerGrab<State> for ResizeForkGrab {
|
|||
|
||||
if let Some(output) = self.output.upgrade() {
|
||||
let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer;
|
||||
if let Some(queue) = tiling_layer.queues.get_mut(&output) {
|
||||
let tree = &mut queue.trees.back_mut().unwrap().0;
|
||||
if tree.get(&self.node).is_ok() {
|
||||
let delta = match self.orientation {
|
||||
Orientation::Vertical => delta.x,
|
||||
Orientation::Horizontal => delta.y,
|
||||
}
|
||||
.round() as i32;
|
||||
|
||||
// check that we are still alive
|
||||
let mut iter = tree
|
||||
.children_ids(&self.node)
|
||||
.unwrap()
|
||||
.skip(self.left_up_idx);
|
||||
let first_elem = iter.next();
|
||||
let second_elem = iter.next();
|
||||
if first_elem.is_none() || second_elem.is_none() {
|
||||
return handle.unset_grab(data, event.serial, event.time);
|
||||
};
|
||||
|
||||
match tree.get_mut(&self.node).unwrap().data_mut() {
|
||||
Data::Group {
|
||||
sizes, orientation, ..
|
||||
} => {
|
||||
if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1]
|
||||
< match orientation {
|
||||
Orientation::Vertical => 720,
|
||||
Orientation::Horizontal => 480,
|
||||
}
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
let old_size = sizes[self.left_up_idx];
|
||||
sizes[self.left_up_idx] = (old_size + delta).max(
|
||||
if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
},
|
||||
);
|
||||
let diff = old_size - sizes[self.left_up_idx];
|
||||
let next_size = sizes[self.left_up_idx + 1] + diff;
|
||||
sizes[self.left_up_idx + 1] =
|
||||
next_size.max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let next_diff = next_size - sizes[self.left_up_idx + 1];
|
||||
sizes[self.left_up_idx] += next_diff;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.last_loc = event.location;
|
||||
let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps);
|
||||
tiling_layer.pending_blockers.extend(blocker);
|
||||
} else {
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
let tree = &mut tiling_layer.queue.trees.back_mut().unwrap().0;
|
||||
if tree.get(&self.node).is_ok() {
|
||||
let delta = match self.orientation {
|
||||
Orientation::Vertical => delta.x,
|
||||
Orientation::Horizontal => delta.y,
|
||||
}
|
||||
.round() as i32;
|
||||
|
||||
// check that we are still alive
|
||||
let mut iter = tree
|
||||
.children_ids(&self.node)
|
||||
.unwrap()
|
||||
.skip(self.left_up_idx);
|
||||
let first_elem = iter.next();
|
||||
let second_elem = iter.next();
|
||||
if first_elem.is_none() || second_elem.is_none() {
|
||||
return handle.unset_grab(data, event.serial, event.time);
|
||||
};
|
||||
|
||||
match tree.get_mut(&self.node).unwrap().data_mut() {
|
||||
Data::Group {
|
||||
sizes, orientation, ..
|
||||
} => {
|
||||
if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1]
|
||||
< match orientation {
|
||||
Orientation::Vertical => 720,
|
||||
Orientation::Horizontal => 480,
|
||||
}
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
let old_size = sizes[self.left_up_idx];
|
||||
sizes[self.left_up_idx] =
|
||||
(old_size + delta).max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let diff = old_size - sizes[self.left_up_idx];
|
||||
let next_size = sizes[self.left_up_idx + 1] + diff;
|
||||
sizes[self.left_up_idx + 1] =
|
||||
next_size.max(if self.orientation == Orientation::Vertical {
|
||||
360
|
||||
} else {
|
||||
240
|
||||
});
|
||||
let next_diff = next_size - sizes[self.left_up_idx + 1];
|
||||
sizes[self.left_up_idx] += next_diff;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
self.last_loc = event.location;
|
||||
let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps);
|
||||
tiling_layer.pending_blockers.extend(blocker);
|
||||
} else {
|
||||
handle.unset_grab(data, event.serial, event.time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ impl KeyboardGrab<State> for SwapWindowGrab {
|
|||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
if self.desc.output.upgrade().is_none()
|
||||
|| !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc)
|
||||
if !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc)
|
||||
{
|
||||
handle.unset_grab(data, serial, false);
|
||||
return;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue