pinnacle/src/layout.rs

566 lines
22 KiB
Rust
Raw Normal View History

2023-08-01 18:06:35 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
2023-06-26 00:18:50 +02:00
use std::sync::atomic::AtomicU32;
use smithay::{
2023-11-21 22:18:36 +01:00
backend::renderer::utils::with_renderer_surface_state,
2023-09-03 04:45:03 +02:00
desktop::layer_map_for_output,
output::Output,
2023-11-21 22:18:36 +01:00
reexports::wayland_server::Resource,
utils::{IsAlive, Logical, Point, Rectangle, Serial, Size},
wayland::compositor::{self, CompositorHandler},
};
2023-07-10 00:48:46 +02:00
use crate::{
state::{State, WithState},
2023-08-11 17:08:38 +02:00
window::{
2023-11-21 22:18:36 +01:00
blocker::TiledWindowBlocker,
2023-08-22 00:56:18 +02:00
window_state::{FloatingOrTiled, FullscreenOrMaximized, LocationRequestState},
2023-09-09 05:26:43 +02:00
WindowElement,
2023-08-11 17:08:38 +02:00
},
2023-07-10 00:48:46 +02:00
};
2023-08-29 05:53:24 +02:00
impl State {
2023-08-11 17:08:38 +02:00
/// Compute the positions and sizes of tiled windows on
/// `output` according to the provided [`Layout`].
fn tile_windows(&self, output: &Output, windows: Vec<WindowElement>, layout: Layout) {
let Some(rect) = self.space.output_geometry(output).map(|op_geo| {
let map = layer_map_for_output(output);
if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
op_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(op_geo.loc + zone.loc, zone.size)
}
}) else {
// TODO: maybe default to something like 800x800 like in anvil so people still see
// | windows open
tracing::error!("Failed to get output geometry");
return;
};
match layout {
Layout::MasterStack => master_stack(windows, rect),
Layout::Dwindle => dwindle(windows, rect),
Layout::Spiral => spiral(windows, rect),
layout @ (Layout::CornerTopLeft
| Layout::CornerTopRight
| Layout::CornerBottomLeft
| Layout::CornerBottomRight) => corner(&layout, windows, rect),
}
}
2023-12-17 04:20:29 +01:00
/// Compute tiled window locations and sizes, resize maximized and fullscreen windows correctly,
/// and send configures and that cool stuff.
2023-08-11 17:08:38 +02:00
pub fn update_windows(&mut self, output: &Output) {
// HACK: With the blocker implementation, if I opened up a bunch of windows quickly they
// | would freeze up.
// | So instead of being smart and figuring something out I instead decided "f*** it"
// | and stuck a static here to detect if update_windows was called again, causing
// | previous blockers to be unblocked.
static UPDATE_COUNT: AtomicU32 = AtomicU32::new(0);
UPDATE_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let current_update_count = UPDATE_COUNT.load(std::sync::atomic::Ordering::Relaxed);
2023-09-15 09:50:42 +02:00
tracing::debug!("Updating windows");
let Some(layout) =
output.with_state(|state| state.focused_tags().next().map(|tag| tag.layout()))
else {
return;
};
2023-08-11 17:08:38 +02:00
let (windows_on_foc_tags, mut windows_not_on_foc_tags): (Vec<_>, _) =
2023-08-11 17:08:38 +02:00
output.with_state(|state| {
let focused_tags = state.focused_tags().collect::<Vec<_>>();
self.windows
.iter()
.filter(|win| !win.is_x11_override_redirect())
.cloned()
.partition(|win| {
win.with_state(|state| {
state.tags.iter().any(|tg| focused_tags.contains(&tg))
})
})
2023-08-11 17:08:38 +02:00
});
// Don't unmap windows that aren't on `output` (that would clear all other monitors)
windows_not_on_foc_tags.retain(|win| win.output(self) == Some(output.clone()));
2023-08-11 17:08:38 +02:00
let tiled_windows = windows_on_foc_tags
.iter()
.filter(|win| {
win.with_state(|state| {
state.floating_or_tiled.is_tiled() && state.fullscreen_or_maximized.is_neither()
})
})
2023-08-11 17:08:38 +02:00
.cloned()
.collect::<Vec<_>>();
self.tile_windows(output, tiled_windows, layout);
let output_geo = self.space.output_geometry(output).expect("no output geo");
for window in windows_on_foc_tags.iter() {
match window.with_state(|state| state.fullscreen_or_maximized) {
FullscreenOrMaximized::Fullscreen => {
2023-08-11 17:08:38 +02:00
window.change_geometry(output_geo);
}
FullscreenOrMaximized::Maximized => {
2023-08-11 17:08:38 +02:00
let map = layer_map_for_output(output);
2023-12-17 04:20:29 +01:00
let geo = if map.layers().next().is_none() {
2023-08-11 17:08:38 +02:00
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
output_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(output_geo.loc + zone.loc, zone.size)
};
window.change_geometry(geo);
}
2023-08-22 00:56:18 +02:00
FullscreenOrMaximized::Neither => {
if let FloatingOrTiled::Floating(rect) =
window.with_state(|state| state.floating_or_tiled)
{
window.change_geometry(rect);
}
}
2023-08-11 17:08:38 +02:00
}
}
2023-11-21 22:18:36 +01:00
let mut pending_wins = Vec::<(WindowElement, Serial)>::new();
2023-09-09 02:56:40 +02:00
let mut non_pending_wins = Vec::<(Point<_, _>, WindowElement)>::new();
2023-12-17 04:20:29 +01:00
// TODO: completely refactor how tracking state changes works
2023-08-11 17:08:38 +02:00
for window in windows_on_foc_tags.iter() {
window.with_state(|state| {
if let LocationRequestState::Sent(loc) = state.loc_request_state {
match &window {
WindowElement::Wayland(win) => {
// If the above didn't cause any change to size or other state, simply
// map the window.
2023-09-09 05:08:56 +02:00
let current_state = win.toplevel().current_state();
let is_pending = win
.toplevel()
.with_pending_state(|state| state.size != current_state.size);
2023-12-17 04:20:29 +01:00
2023-09-09 05:08:56 +02:00
if !is_pending {
2023-09-12 16:29:21 +02:00
tracing::debug!("No pending changes, mapping window");
// TODO: map win here, not down there
state.loc_request_state = LocationRequestState::Idle;
2023-09-09 02:56:40 +02:00
non_pending_wins.push((loc, window.clone()));
} else {
2023-09-12 16:29:21 +02:00
tracing::debug!("Pending changes, requesting commit");
let serial = win.toplevel().send_configure();
state.loc_request_state =
LocationRequestState::Requested(serial, loc);
2023-11-21 22:18:36 +01:00
pending_wins.push((window.clone(), serial));
}
2023-08-11 17:08:38 +02:00
}
2023-08-22 00:56:18 +02:00
WindowElement::X11(surface) => {
2023-08-11 17:08:38 +02:00
// already configured, just need to map
// maybe wait for all wayland windows to commit before mapping
2023-09-09 02:56:40 +02:00
// self.space.map_element(window.clone(), loc, false);
2023-08-22 00:56:18 +02:00
surface
.set_mapped(true)
.expect("failed to set x11 win to mapped");
2023-08-11 17:08:38 +02:00
state.loc_request_state = LocationRequestState::Idle;
2023-09-09 02:56:40 +02:00
non_pending_wins.push((loc, window.clone()));
2023-08-11 17:08:38 +02:00
}
WindowElement::X11OverrideRedirect(_) => {
// filtered out up there somewhere
unreachable!();
}
2023-08-11 17:08:38 +02:00
}
}
});
}
2023-11-21 22:18:36 +01:00
let tiling_blocker = TiledWindowBlocker::new(pending_wins.clone());
for (win, _) in pending_wins.iter() {
if let Some(surface) = win.wl_surface() {
let client = surface
.client()
.expect("Surface has no client/is no longer alive");
self.client_compositor_state(&client)
.blocker_cleared(self, &self.display_handle.clone());
tracing::debug!("blocker cleared");
2023-11-21 22:18:36 +01:00
compositor::add_blocker(&surface, tiling_blocker.clone());
}
}
let pending_wins_clone = pending_wins.clone();
let pending_wins_clone2 = pending_wins.clone();
2023-11-21 22:18:36 +01:00
self.schedule(
move |data| {
if UPDATE_COUNT.load(std::sync::atomic::Ordering::Relaxed) > current_update_count {
for (win, _) in pending_wins_clone2.iter() {
let Some(surface) = win.wl_surface() else {
continue;
};
let client = surface
.client()
.expect("Surface has no client/is no longer alive");
data.state
.client_compositor_state(&client)
.blocker_cleared(&mut data.state, &data.display_handle);
tracing::debug!("blocker cleared");
}
}
2023-11-21 22:18:36 +01:00
tiling_blocker.ready()
&& pending_wins_clone2.iter().all(|(win, _)| {
if let Some(surface) = win.wl_surface() {
with_renderer_surface_state(&surface, |state| state.buffer().is_some())
} else {
true
}
})
},
move |data| {
for (win, _) in pending_wins_clone {
let Some(surface) = win.wl_surface() else {
continue;
};
let client = surface
.client()
.expect("Surface has no client/is no longer alive");
data.state
.client_compositor_state(&client)
.blocker_cleared(&mut data.state, &data.display_handle);
tracing::debug!("blocker cleared");
}
2023-09-09 05:08:56 +02:00
for (loc, win) in non_pending_wins {
data.state.space.map_element(win, loc, false);
2023-09-09 05:08:56 +02:00
}
2023-09-09 02:56:40 +02:00
},
);
2023-08-11 17:08:38 +02:00
}
}
// -------------------------------------------
2023-07-13 01:50:41 +02:00
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
2023-07-12 04:07:51 +02:00
pub enum Layout {
MasterStack,
Dwindle,
2023-07-13 01:50:41 +02:00
Spiral,
CornerTopLeft,
CornerTopRight,
CornerBottomLeft,
CornerBottomRight,
}
2023-08-11 17:08:38 +02:00
fn master_stack(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
2023-08-07 18:25:36 +02:00
let size = rect.size;
let loc = rect.loc;
2023-07-25 01:59:05 +02:00
let master = windows.first();
let stack = windows.iter().skip(1);
let Some(master) = master else { return };
let stack_count = stack.clone().count();
if stack_count == 0 {
// one window
2023-08-11 17:08:38 +02:00
master.change_geometry(Rectangle::from_loc_and_size(loc, size));
2023-07-25 01:59:05 +02:00
} else {
2023-08-11 17:08:38 +02:00
let loc: Point<i32, Logical> = (loc.x, loc.y).into();
2023-08-07 18:25:36 +02:00
let new_master_size: Size<i32, Logical> = (size.w / 2, size.h).into();
2023-08-11 17:08:38 +02:00
master.change_geometry(Rectangle::from_loc_and_size(loc, new_master_size));
2023-07-25 01:59:05 +02:00
2023-08-07 18:25:36 +02:00
let height = size.h as f32 / stack_count as f32;
2023-07-25 01:59:05 +02:00
let mut y_s = vec![];
for i in 0..stack_count {
y_s.push((i as f32 * height).round() as i32);
}
let heights = y_s
.windows(2)
.map(|pair| pair[1] - pair[0])
2023-08-07 18:25:36 +02:00
.chain(vec![size.h - y_s.last().expect("vec was empty")])
2023-07-25 01:59:05 +02:00
.collect::<Vec<_>>();
for (i, win) in stack.enumerate() {
2023-08-11 17:08:38 +02:00
win.change_geometry(Rectangle::from_loc_and_size(
Point::from((size.w / 2 + loc.x, y_s[i] + loc.y)),
Size::from((size.w / 2, i32::max(heights[i], 40))),
));
2023-07-25 01:59:05 +02:00
}
}
}
2023-08-11 17:08:38 +02:00
fn dwindle(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
2023-08-07 18:25:36 +02:00
let size = rect.size;
let loc = rect.loc;
2023-07-25 01:59:05 +02:00
let mut iter = windows.windows(2).peekable();
if iter.peek().is_none() {
if let Some(window) = windows.first() {
2023-08-11 17:08:38 +02:00
window.change_geometry(Rectangle::from_loc_and_size(loc, size));
2023-07-25 01:59:05 +02:00
}
} else {
2023-08-07 18:25:36 +02:00
let mut win1_size = size;
let mut win1_loc = loc;
2023-07-25 01:59:05 +02:00
for (i, wins) in iter.enumerate() {
let win1 = &wins[0];
let win2 = &wins[1];
enum Slice {
Right,
Below,
2023-07-12 04:07:51 +02:00
}
2023-08-04 16:36:40 +02:00
let slice = if i % 2 == 0 { Slice::Right } else { Slice::Below };
2023-07-25 01:59:05 +02:00
match slice {
Slice::Right => {
let width_partition = win1_size.w / 2;
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
2023-07-25 01:59:05 +02:00
win1_loc,
2023-08-11 17:08:38 +02:00
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
2023-07-25 01:59:05 +02:00
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
2023-07-13 01:50:41 +02:00
}
2023-07-25 01:59:05 +02:00
Slice::Below => {
let height_partition = win1_size.h / 2;
2023-07-13 01:50:41 +02:00
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
2023-07-25 01:59:05 +02:00
win1_loc,
2023-08-11 17:08:38 +02:00
Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
));
2023-07-13 01:50:41 +02:00
2023-07-25 01:59:05 +02:00
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
2023-07-12 04:07:51 +02:00
}
}
2023-07-25 01:59:05 +02:00
}
}
}
2023-08-11 17:08:38 +02:00
fn spiral(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
2023-08-07 18:25:36 +02:00
let size = rect.size;
let loc = rect.loc;
2023-07-25 01:59:05 +02:00
2023-12-17 04:20:29 +01:00
let mut window_pairs = windows.windows(2).peekable();
2023-07-25 01:59:05 +02:00
2023-12-17 04:20:29 +01:00
if window_pairs.peek().is_none() {
2023-07-25 01:59:05 +02:00
if let Some(window) = windows.first() {
2023-08-11 17:08:38 +02:00
window.change_geometry(Rectangle::from_loc_and_size(loc, size));
2023-07-25 01:59:05 +02:00
}
} else {
2023-08-07 18:25:36 +02:00
let mut win1_loc = loc;
let mut win1_size = size;
2023-07-25 01:59:05 +02:00
2023-12-17 04:20:29 +01:00
for (i, wins) in window_pairs.enumerate() {
2023-07-25 01:59:05 +02:00
let win1 = &wins[0];
let win2 = &wins[1];
enum Slice {
Above,
Below,
Left,
Right,
}
let slice = match i % 4 {
0 => Slice::Right,
1 => Slice::Below,
2 => Slice::Left,
3 => Slice::Above,
_ => unreachable!(),
};
match slice {
Slice::Above => {
let height_partition = win1_size.h / 2;
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
Point::from((win1_loc.x, win1_loc.y + height_partition)),
Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
));
2023-07-25 01:59:05 +02:00
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
2023-07-25 01:59:05 +02:00
Slice::Below => {
let height_partition = win1_size.h / 2;
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
2023-07-25 01:59:05 +02:00
win1_loc,
2023-08-11 17:08:38 +02:00
Size::from((win1_size.w, win1_size.h - i32::max(height_partition, 40))),
));
2023-07-25 01:59:05 +02:00
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
}
2023-07-25 01:59:05 +02:00
Slice::Left => {
let width_partition = win1_size.w / 2;
2023-07-18 03:40:56 +02:00
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
Point::from((win1_loc.x + width_partition, win1_loc.y)),
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
2023-07-18 03:40:56 +02:00
2023-07-25 01:59:05 +02:00
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
2023-07-25 01:59:05 +02:00
}
Slice::Right => {
let width_partition = win1_size.w / 2;
2023-07-18 03:40:56 +02:00
2023-08-11 17:08:38 +02:00
win1.change_geometry(Rectangle::from_loc_and_size(
2023-07-25 01:59:05 +02:00
win1_loc,
2023-08-11 17:08:38 +02:00
Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
));
2023-07-18 03:40:56 +02:00
2023-07-25 01:59:05 +02:00
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
2023-08-11 17:08:38 +02:00
win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
2023-07-25 01:59:05 +02:00
}
}
}
}
}
2023-08-11 17:08:38 +02:00
fn corner(layout: &Layout, windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
2023-08-07 18:25:36 +02:00
let size = rect.size;
let loc = rect.loc;
2023-07-25 01:59:05 +02:00
match windows.len() {
0 => (),
1 => {
2023-08-11 17:08:38 +02:00
windows[0].change_geometry(rect);
2023-07-25 01:59:05 +02:00
}
2 => {
2023-08-11 17:08:38 +02:00
windows[0].change_geometry(Rectangle::from_loc_and_size(
loc,
Size::from((size.w / 2, size.h)),
));
windows[1].change_geometry(Rectangle::from_loc_and_size(
Point::from((loc.x + size.w / 2, loc.y)),
Size::from((size.w / 2, size.h)),
));
2023-07-25 01:59:05 +02:00
}
_ => {
let mut windows = windows.into_iter();
let Some(corner) = windows.next() else { unreachable!() };
2023-09-11 05:53:24 +02:00
let mut horiz_stack = Vec::<WindowElement>::new();
let mut vert_stack = Vec::<WindowElement>::new();
for (i, win) in windows.enumerate() {
if i % 2 == 0 {
horiz_stack.push(win);
} else {
vert_stack.push(win);
}
}
2023-07-25 01:59:05 +02:00
let div_factor = 2;
2023-08-11 17:08:38 +02:00
corner.change_geometry(Rectangle::from_loc_and_size(
Point::from(match layout {
2023-08-07 18:25:36 +02:00
Layout::CornerTopLeft => (loc.x, loc.y),
Layout::CornerTopRight => (loc.x + size.w - size.w / div_factor, loc.y),
Layout::CornerBottomLeft => (loc.x, loc.y + size.h - size.h / div_factor),
2023-07-25 01:59:05 +02:00
Layout::CornerBottomRight => (
2023-08-07 18:25:36 +02:00
loc.x + size.w - size.w / div_factor,
loc.y + size.h - size.h / div_factor,
2023-07-25 01:59:05 +02:00
),
_ => unreachable!(),
2023-08-11 17:08:38 +02:00
}),
Size::from((size.w / div_factor, size.h / div_factor)),
));
2023-07-25 01:59:05 +02:00
let vert_stack_count = vert_stack.len();
2023-08-07 18:25:36 +02:00
let height = size.h as f32 / vert_stack_count as f32;
2023-07-25 01:59:05 +02:00
let mut y_s = vec![];
for i in 0..vert_stack_count {
y_s.push((i as f32 * height).round() as i32);
}
let heights = y_s
.windows(2)
.map(|pair| pair[1] - pair[0])
2023-08-07 18:25:36 +02:00
.chain(vec![size.h - y_s.last().expect("vec was empty")])
2023-07-25 01:59:05 +02:00
.collect::<Vec<_>>();
for (i, win) in vert_stack.iter().enumerate() {
2023-08-11 17:08:38 +02:00
win.change_geometry(Rectangle::from_loc_and_size(
Point::from((
2023-07-25 01:59:05 +02:00
match layout {
2023-08-07 18:25:36 +02:00
Layout::CornerTopLeft | Layout::CornerBottomLeft => size.w / 2 + loc.x,
Layout::CornerTopRight | Layout::CornerBottomRight => loc.x,
2023-07-25 01:59:05 +02:00
_ => unreachable!(),
},
2023-08-07 18:25:36 +02:00
y_s[i] + loc.y,
2023-08-11 17:08:38 +02:00
)),
Size::from((size.w / 2, i32::max(heights[i], 40))),
));
2023-07-25 01:59:05 +02:00
}
let horiz_stack_count = horiz_stack.len();
2023-08-07 18:25:36 +02:00
let width = size.w as f32 / 2.0 / horiz_stack_count as f32;
2023-07-25 01:59:05 +02:00
let mut x_s = vec![];
for i in 0..horiz_stack_count {
x_s.push((i as f32 * width).round() as i32);
}
let widths = x_s
.windows(2)
.map(|pair| pair[1] - pair[0])
2023-08-07 18:25:36 +02:00
.chain(vec![size.w / 2 - x_s.last().expect("vec was empty")])
2023-07-25 01:59:05 +02:00
.collect::<Vec<_>>();
for (i, win) in horiz_stack.iter().enumerate() {
2023-08-11 17:08:38 +02:00
win.change_geometry(Rectangle::from_loc_and_size(
Point::from(match layout {
2023-08-07 18:25:36 +02:00
Layout::CornerTopLeft => (x_s[i] + loc.x, loc.y + size.h / 2),
Layout::CornerTopRight => (x_s[i] + loc.x + size.w / 2, loc.y + size.h / 2),
Layout::CornerBottomLeft => (x_s[i] + loc.x, loc.y),
Layout::CornerBottomRight => (x_s[i] + loc.x + size.w / 2, loc.y),
2023-07-25 01:59:05 +02:00
_ => unreachable!(),
2023-08-11 17:08:38 +02:00
}),
Size::from((i32::max(widths[i], 1), size.h / 2)),
));
2023-07-25 01:59:05 +02:00
}
}
}
}
2023-08-29 05:53:24 +02:00
impl State {
2023-12-17 04:20:29 +01:00
/// Swaps two windows in the main window vec and updates all windows.
2023-07-25 01:59:05 +02:00
pub fn swap_window_positions(&mut self, win1: &WindowElement, win2: &WindowElement) {
2023-09-15 09:50:42 +02:00
let win1_index = self.windows.iter().position(|win| win == win1);
let win2_index = self.windows.iter().position(|win| win == win2);
2023-09-15 09:50:42 +02:00
if let (Some(first), Some(second)) = (win1_index, win2_index) {
self.windows.swap(first, second);
if let Some(output) = win1.output(self) {
self.update_windows(&output);
2023-09-04 08:54:10 +02:00
}
}
}
}