feat: tile managed windows with layout tree

This commit is contained in:
Samuel Onoja committed 2026-09-24 06:19:06 +01:00
1 parent 03f6b9b8e1
commit 58d91363d6
7 files changed
+116 -3

No files matched your search

+90 -1
View File
@@ -1,4 +1,6 @@
use std::{error::Error, ffi::OsString, sync::Arc, time::Instant};
use std::{collections::HashMap, error::Error, ffi::OsString, sync::Arc, time::Instant};
use ferese_layout::{Axis, LayoutTree, Rect, WindowId};
use smithay::{
desktop::{PopupManager, Space, Window, WindowSurfaceType},
@@ -35,6 +37,10 @@ pub struct Ferese {
pub display_handle: DisplayHandle,
pub loop_signal: LoopSignal,
pub space: Space<Window>,
pub layout: LayoutTree,
pub window_ids: HashMap<Window, WindowId>,
pub focused_window: Option<WindowId>,
next_window_id: u64,
pub popups: PopupManager,
pub seat: Seat<Self>,
pub compositor_state: CompositorState,
@@ -85,6 +91,10 @@ impl Ferese {
display_handle,
loop_signal: event_loop.get_signal(),
space: Space::default(),
layout: LayoutTree::default(),
window_ids: HashMap::new(),
focused_window: None,
next_window_id: 1,
popups: PopupManager::default(),
seat,
compositor_state,
@@ -145,6 +155,85 @@ impl Ferese {
})
})
}
pub fn add_tiled_window(&mut self, window: Window) {
let id = WindowId(self.next_window_id);
self.next_window_id += 1;
if let Err(error) = self
.layout
.insert(id, self.focused_window, Axis::Horizontal, 0.5)
{
tracing::error!(%error, ?id, "failed to insert window into layout");
return;
}
self.window_ids.insert(window.clone(), id);
self.focused_window = Some(id);
self.space.map_element(window, (0, 0), true);
self.relayout();
}
pub fn remove_tiled_window(&mut self, window: &Window) {
let Some(id) = self.window_ids.remove(window) else {
return;
};
self.space.unmap_elem(window);
if let Err(error) = self.layout.remove(id) {
tracing::error!(%error, ?id, "failed to remove window from layout");
}
if self.focused_window == Some(id) {
self.focused_window = self
.space
.elements()
.rev()
.find_map(|window| self.window_ids.get(window).copied());
}
self.relayout();
}
pub fn relayout(&mut self) {
let Some(output) = self.space.outputs().next() else {
return;
};
let Some(output_geometry) = self.space.output_geometry(output) else {
return;
};
let bounds = Rect::new(
output_geometry.loc.x as f64,
output_geometry.loc.y as f64,
output_geometry.size.w as f64,
output_geometry.size.h as f64,
);
let geometry = match self.layout.geometry(bounds) {
Ok(geometry) => geometry,
Err(error) => {
tracing::error!(%error, "failed to compute tiled geometry");
return;
}
};
let placements = self
.window_ids
.iter()
.filter_map(|(window, id)| geometry.get(id).map(|rect| (window.clone(), *rect)))
.collect::<Vec<_>>();
for (window, rect) in placements {
let location = (rect.x.round() as i32, rect.y.round() as i32);
let size = (
(rect.width.round() as i32).max(1),
(rect.height.round() as i32).max(1),
);
self.space.map_element(window.clone(), location, false);
if let Some(toplevel) = window.toplevel() {
toplevel.with_pending_state(|state| state.size = Some(size.into()));
toplevel.send_pending_configure();
}
}
}
}
#[derive(Default)]