colors: add workspaces
This commit is contained in:
parent
c4e3128528
commit
8493573721
@ -4,7 +4,6 @@ use libcolors::{
|
|||||||
};
|
};
|
||||||
// use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
|
// use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InputState {
|
pub struct InputState {
|
||||||
lshift: bool,
|
lshift: bool,
|
||||||
|
@ -13,12 +13,12 @@ use input::InputState;
|
|||||||
use libcolors::{
|
use libcolors::{
|
||||||
event::{EventData, KeyEvent, KeyModifiers, KeyboardKeyEvent, WindowEvent, WindowInfo},
|
event::{EventData, KeyEvent, KeyModifiers, KeyboardKeyEvent, WindowEvent, WindowInfo},
|
||||||
input::Key,
|
input::Key,
|
||||||
message::{ClientMessage, CreateWindowInfo, WindowType},
|
message::{ClientMessage, CreateWindowInfo},
|
||||||
};
|
};
|
||||||
use sys::{Backend, DisplaySurface, FromClient, Point, ServerSender, WindowServer};
|
use sys::{Backend, DisplaySurface, FromClient, Point, ServerSender, WindowServer};
|
||||||
use uipc::PeerAddress;
|
use uipc::PeerAddress;
|
||||||
use window::Window;
|
use window::Window;
|
||||||
use wm::{Direction, Workspace};
|
use wm::{display::Display, layout::Direction};
|
||||||
|
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
@ -36,7 +36,7 @@ pub struct Server<'a> {
|
|||||||
last_client_id: u32,
|
last_client_id: u32,
|
||||||
client_map: HashMap<u32, PeerAddress>,
|
client_map: HashMap<u32, PeerAddress>,
|
||||||
|
|
||||||
workspace: Workspace<u32>,
|
display: Display<u32>,
|
||||||
windows: BTreeMap<u32, Window<'a>>,
|
windows: BTreeMap<u32, Window<'a>>,
|
||||||
last_window_id: u32,
|
last_window_id: u32,
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ impl<'a> Server<'a> {
|
|||||||
client_map: HashMap::new(),
|
client_map: HashMap::new(),
|
||||||
|
|
||||||
windows: BTreeMap::new(),
|
windows: BTreeMap::new(),
|
||||||
workspace: Workspace::new(800, 600),
|
display: Display::new(800, 600),
|
||||||
last_window_id: 1,
|
last_window_id: 1,
|
||||||
|
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
@ -71,19 +71,11 @@ impl<'a> Server<'a> {
|
|||||||
) -> Result<(WindowInfo, RawFd), Error> {
|
) -> Result<(WindowInfo, RawFd), Error> {
|
||||||
let wid = self.last_window_id;
|
let wid = self.last_window_id;
|
||||||
self.last_window_id += 1;
|
self.last_window_id += 1;
|
||||||
let need_focus = match info.ty {
|
let need_focus = self
|
||||||
WindowType::Default => {
|
.display
|
||||||
if !self.workspace.create_window(wid) {
|
.create_window(wid, &info.ty)
|
||||||
todo!()
|
.expect("TODO: handle create window failed");
|
||||||
}
|
let frame = self.display.window_frame(wid).unwrap();
|
||||||
true
|
|
||||||
}
|
|
||||||
WindowType::Reservation(height) => {
|
|
||||||
self.workspace.add_reservation(wid, height);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let frame = self.workspace.window_frame(wid).unwrap();
|
|
||||||
let mapping_size = surface.width() * surface.height() * 4;
|
let mapping_size = surface.width() * surface.height() * 4;
|
||||||
// let mapping_size = self.display.width() * self.display.height() * 4;
|
// let mapping_size = self.display.width() * self.display.height() * 4;
|
||||||
let surface_shm = SharedMemory::new(mapping_size).unwrap();
|
let surface_shm = SharedMemory::new(mapping_size).unwrap();
|
||||||
@ -118,12 +110,13 @@ impl<'a> Server<'a> {
|
|||||||
if need_focus {
|
if need_focus {
|
||||||
self.focus_window(tx, wid);
|
self.focus_window(tx, wid);
|
||||||
}
|
}
|
||||||
|
self.redraw_all(tx);
|
||||||
|
|
||||||
Ok((info, fd))
|
Ok((info, fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_window(&mut self, tx: &mut ServerSender, wid: u32) {
|
fn focus_window(&mut self, tx: &mut ServerSender, wid: u32) {
|
||||||
let (old_wid, new) = self.workspace.focus_window(wid);
|
let (old_wid, new) = self.display.current_workspace_mut().focus_window(wid);
|
||||||
let old = old_wid.and_then(|wid| {
|
let old = old_wid.and_then(|wid| {
|
||||||
let window = self.windows.get(&wid)?;
|
let window = self.windows.get(&wid)?;
|
||||||
Some((wid, window))
|
Some((wid, window))
|
||||||
@ -147,21 +140,17 @@ impl<'a> Server<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn move_focus(&mut self, tx: &mut ServerSender, direction: Direction) {
|
fn move_focus(&mut self, tx: &mut ServerSender, direction: Direction) {
|
||||||
if let Some(wid) = self.workspace.window_towards_wrap(direction) {
|
if let Some(wid) = self
|
||||||
|
.display
|
||||||
|
.current_workspace_mut()
|
||||||
|
.window_towards_wrap(direction)
|
||||||
|
{
|
||||||
self.focus_window(tx, wid);
|
self.focus_window(tx, wid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_window(
|
fn redraw_all(&mut self, tx: &mut ServerSender) {
|
||||||
&mut self,
|
self.display.all_windows().for_each(|(wid, _)| {
|
||||||
surface: &mut DisplaySurface,
|
|
||||||
tx: &mut ServerSender,
|
|
||||||
direction: Direction,
|
|
||||||
) {
|
|
||||||
if self.workspace.move_window(direction) {
|
|
||||||
surface.fill(self.background);
|
|
||||||
self.flush_dirty_frames(tx);
|
|
||||||
self.workspace.all_windows().for_each(|(wid, _)| {
|
|
||||||
if let Some(window) = self.windows.get(&wid) {
|
if let Some(window) = self.windows.get(&wid) {
|
||||||
tx.send_event(
|
tx.send_event(
|
||||||
EventData::WindowEvent(wid, WindowEvent::RedrawRequested),
|
EventData::WindowEvent(wid, WindowEvent::RedrawRequested),
|
||||||
@ -170,10 +159,22 @@ impl<'a> Server<'a> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn move_window(
|
||||||
|
&mut self,
|
||||||
|
surface: &mut DisplaySurface,
|
||||||
|
tx: &mut ServerSender,
|
||||||
|
direction: Direction,
|
||||||
|
) {
|
||||||
|
if self.display.move_window(direction) {
|
||||||
|
surface.fill(self.background);
|
||||||
|
self.flush_dirty_frames(tx);
|
||||||
|
self.redraw_all(tx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_dirty_frames(&mut self, tx: &mut ServerSender) {
|
fn flush_dirty_frames(&mut self, tx: &mut ServerSender) {
|
||||||
for (wid, rect) in self.workspace.dirty_windows() {
|
for (wid, rect) in self.display.dirty_windows() {
|
||||||
let Some(window) = self.windows.get_mut(&wid) else {
|
let Some(window) = self.windows.get_mut(&wid) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -193,11 +194,72 @@ impl<'a> Server<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn switch_to_workspace(&mut self, tx: &mut ServerSender, next: usize) -> bool {
|
||||||
|
if next == self.display.current_workspace || next >= Display::<u32>::MAX {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Switch workspace {} -> {}",
|
||||||
|
self.display.current_workspace,
|
||||||
|
next
|
||||||
|
);
|
||||||
|
|
||||||
|
let old_focus_wid = self.display.current_workspace().focused_window();
|
||||||
|
let old_focus = old_focus_wid.and_then(|wid| {
|
||||||
|
let window = self.windows.get(&wid)?;
|
||||||
|
Some((wid, window))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((old_wid, old_window)) = old_focus {
|
||||||
|
log::info!("Unfocus #{old_wid}");
|
||||||
|
tx.send_event(
|
||||||
|
EventData::WindowEvent(old_wid, WindowEvent::FocusChanged(false)),
|
||||||
|
&old_window.peer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.display.current_workspace = next;
|
||||||
|
|
||||||
|
let new_focus_wid = self.display.current_workspace().focused_window();
|
||||||
|
let new_focus = new_focus_wid.and_then(|wid| {
|
||||||
|
let window = self.windows.get(&wid)?;
|
||||||
|
Some((wid, window))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((new_wid, new_window)) = new_focus {
|
||||||
|
log::info!("Focus #{new_wid}");
|
||||||
|
tx.send_event(
|
||||||
|
EventData::WindowEvent(new_wid, WindowEvent::FocusChanged(true)),
|
||||||
|
&new_window.peer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send redraw to all frames
|
||||||
|
self.flush_dirty_frames(tx);
|
||||||
|
self.redraw_all(tx);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_focus_to_workspace(&mut self, tx: &mut ServerSender, target: usize) -> bool {
|
||||||
|
if target == self.display.current_workspace && target >= Display::<u32>::MAX {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.display.send_focused_window(target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flush_dirty_frames(tx);
|
||||||
|
self.redraw_all(tx);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowServer for Server<'_> {
|
impl WindowServer for Server<'_> {
|
||||||
fn handle_initial(&mut self, mut surface: DisplaySurface) {
|
fn handle_initial(&mut self, mut surface: DisplaySurface) {
|
||||||
self.workspace
|
self.display
|
||||||
.resize(surface.width() as u32, surface.height() as u32);
|
.resize(surface.width() as u32, surface.height() as u32);
|
||||||
surface.fill(self.background);
|
surface.fill(self.background);
|
||||||
surface.present();
|
surface.present();
|
||||||
@ -242,7 +304,7 @@ impl WindowServer for Server<'_> {
|
|||||||
} => {
|
} => {
|
||||||
log::trace!("{:?}: BlitWindow", peer);
|
log::trace!("{:?}: BlitWindow", peer);
|
||||||
if let Some(window) = self.windows.get(&window_id) {
|
if let Some(window) = self.windows.get(&window_id) {
|
||||||
let Some(frame) = self.workspace.window_frame(window_id) else {
|
let Some(frame) = self.display.window_frame(window_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -274,14 +336,35 @@ impl WindowServer for Server<'_> {
|
|||||||
ClientMessage::DestroyWindow(wid) => {
|
ClientMessage::DestroyWindow(wid) => {
|
||||||
log::info!("{:?}: DestroyWindow", peer);
|
log::info!("{:?}: DestroyWindow", peer);
|
||||||
let window = self.windows.remove(&wid);
|
let window = self.windows.remove(&wid);
|
||||||
|
let old_focus = self.display.current_workspace().focused_window();
|
||||||
if window.is_some() {
|
if window.is_some() {
|
||||||
self.workspace.remove_window(wid);
|
if self.display.remove_window(wid) {
|
||||||
|
surface.fill(self.background);
|
||||||
self.flush_dirty_frames(tx);
|
self.flush_dirty_frames(tx);
|
||||||
|
self.redraw_all(tx);
|
||||||
|
|
||||||
|
let focus = self.display.current_workspace().focused_window();
|
||||||
|
|
||||||
|
if focus != old_focus {
|
||||||
|
let focus = focus.and_then(|wid| {
|
||||||
|
let window = self.windows.get(&wid)?;
|
||||||
|
Some((wid, window))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((wid, window)) = focus {
|
||||||
|
tx.send_event(
|
||||||
|
EventData::WindowEvent(wid, WindowEvent::FocusChanged(true)),
|
||||||
|
&window.peer,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
surface.present();
|
surface.present();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_keyboard_event(
|
fn handle_keyboard_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -306,6 +389,22 @@ impl WindowServer for Server<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match (input.modifiers, input.key) {
|
match (input.modifiers, input.key) {
|
||||||
|
(KeyModifiers::ALT, Key::Char(c)) if c.is_ascii_digit() => {
|
||||||
|
let number = ((c + 9) - b'0') % 10;
|
||||||
|
if self.switch_to_workspace(tx, number as usize) {
|
||||||
|
surface.fill(self.background);
|
||||||
|
surface.present();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(KeyModifiers::ALT_SHIFT, Key::Char(c)) if c.is_ascii_digit() => {
|
||||||
|
let number = ((c + 9) - b'0') % 10;
|
||||||
|
if self.send_focus_to_workspace(tx, number as usize) {
|
||||||
|
surface.fill(self.background);
|
||||||
|
surface.present();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
(KeyModifiers::ALT, Key::Char(b'l')) => {
|
(KeyModifiers::ALT, Key::Char(b'l')) => {
|
||||||
self.move_focus(tx, Direction::Right);
|
self.move_focus(tx, Direction::Right);
|
||||||
return;
|
return;
|
||||||
@ -344,22 +443,32 @@ impl WindowServer for Server<'_> {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Window keys
|
|
||||||
// if let Some((row, col)) = self.focused_frame {
|
|
||||||
// let row_len = self.rows[row].frames.len();
|
|
||||||
|
|
||||||
// if let Some((_, window)) = self.get_focused_window() {
|
|
||||||
// } else {
|
|
||||||
// self.focused_frame = None;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let focus = self.workspace.focused_window().and_then(|wid| {
|
let focus = self
|
||||||
|
.display
|
||||||
|
.current_workspace()
|
||||||
|
.focused_window()
|
||||||
|
.and_then(|wid| {
|
||||||
let window = self.windows.get(&wid)?;
|
let window = self.windows.get(&wid)?;
|
||||||
Some((wid, window))
|
Some((wid, window))
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((wid, window)) = focus {
|
if let Some((wid, window)) = focus {
|
||||||
|
if event.state {
|
||||||
|
match (input.modifiers, input.key) {
|
||||||
|
(KeyModifiers::ALT_SHIFT, Key::Char(b'q')) => {
|
||||||
|
log::info!("Send close request -> #{wid}");
|
||||||
|
tx.send_event(
|
||||||
|
EventData::WindowEvent(wid, WindowEvent::CloseRequested),
|
||||||
|
&window.peer,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if event.state {
|
if event.state {
|
||||||
tx.send_event(
|
tx.send_event(
|
||||||
EventData::WindowEvent(
|
EventData::WindowEvent(
|
||||||
|
@ -6,15 +6,15 @@ use libcolors::{
|
|||||||
};
|
};
|
||||||
use uipc::{PeerAddress, Sender};
|
use uipc::{PeerAddress, Sender};
|
||||||
|
|
||||||
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
|
||||||
pub mod yggdrasil;
|
|
||||||
#[cfg(any(rust_analyzer, unix))]
|
#[cfg(any(rust_analyzer, unix))]
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
|
|
||||||
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
||||||
pub use yggdrasil::{Backend, DisplaySurface, Error};
|
pub mod yggdrasil;
|
||||||
|
|
||||||
#[cfg(any(rust_analyzer, unix))]
|
#[cfg(any(rust_analyzer, unix))]
|
||||||
pub use unix::{Backend, DisplaySurface, Error};
|
pub use unix::{Backend, DisplaySurface, Error};
|
||||||
|
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
||||||
|
pub use yggdrasil::{Backend, DisplaySurface, Error};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FromClient {
|
pub struct FromClient {
|
||||||
|
@ -165,7 +165,12 @@ impl<S: WindowServer> Backend<S> {
|
|||||||
let mut surface = softbuffer::Surface::new(&context, self.window.clone())?;
|
let mut surface = softbuffer::Surface::new(&context, self.window.clone())?;
|
||||||
|
|
||||||
let size = self.window.inner_size();
|
let size = self.window.inner_size();
|
||||||
surface.resize(NonZero::new(size.width).unwrap(), NonZero::new(size.height).unwrap()).unwrap();
|
surface
|
||||||
|
.resize(
|
||||||
|
NonZero::new(size.width).unwrap(),
|
||||||
|
NonZero::new(size.height).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
self.server.handle_initial(DisplaySurface {
|
self.server.handle_initial(DisplaySurface {
|
||||||
inner: surface.buffer_mut().unwrap(),
|
inner: surface.buffer_mut().unwrap(),
|
||||||
width: size.width as _,
|
width: size.width as _,
|
||||||
|
@ -145,7 +145,7 @@ impl<S: WindowServer> Backend<'_, S> {
|
|||||||
|
|
||||||
pub fn run(mut self) -> Result<(), Error> {
|
pub fn run(mut self) -> Result<(), Error> {
|
||||||
self.server.handle_initial(DisplaySurface {
|
self.server.handle_initial(DisplaySurface {
|
||||||
display: &mut self.display
|
display: &mut self.display,
|
||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -169,7 +169,8 @@ impl<S: WindowServer> Backend<'_, S> {
|
|||||||
let surface = DisplaySurface {
|
let surface = DisplaySurface {
|
||||||
display: &mut self.display,
|
display: &mut self.display,
|
||||||
};
|
};
|
||||||
self.server.handle_keyboard_event(surface, &mut self.tx, event);
|
self.server
|
||||||
|
.handle_keyboard_event(surface, &mut self.tx, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,8 +272,5 @@ fn convert_key_event(raw: yggdrasil_abi::io::KeyboardKeyEvent) -> Option<Keyboar
|
|||||||
KeyboardKey::F(_) => return None,
|
KeyboardKey::F(_) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(KeyboardKeyEvent {
|
Some(KeyboardKeyEvent { key, state })
|
||||||
key,
|
|
||||||
state
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
212
userspace/colors/src/wm/display.rs
Normal file
212
userspace/colors/src/wm/display.rs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
use std::{fmt, hash::Hash, iter};
|
||||||
|
|
||||||
|
use libcolors::message::WindowType;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
layout::{Direction, NodeLayout, Rect},
|
||||||
|
workspace::Workspace,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Display<T> {
|
||||||
|
workspaces: Vec<Workspace<T>>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
reservations_top: Vec<Reservation<T>>,
|
||||||
|
reservation_height: u32,
|
||||||
|
pub current_workspace: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Reservation<T> {
|
||||||
|
layout: NodeLayout,
|
||||||
|
size: u32,
|
||||||
|
wid: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq + Hash + Copy> Display<T> {
|
||||||
|
pub const MAX: usize = 10;
|
||||||
|
|
||||||
|
pub fn new(width: u32, height: u32) -> Self {
|
||||||
|
let workspaces = (0..Self::MAX)
|
||||||
|
.map(|_| Workspace::new(width, height))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
workspaces,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
reservations_top: Vec::new(),
|
||||||
|
reservation_height: 0,
|
||||||
|
current_workspace: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, width: u32, height: u32) {
|
||||||
|
self.width = width;
|
||||||
|
self.height = height;
|
||||||
|
|
||||||
|
// Re-do reservation layout
|
||||||
|
self.update_reservation_layout();
|
||||||
|
|
||||||
|
for workspace in self.workspaces.iter_mut() {
|
||||||
|
workspace.resize(width, height, self.reservation_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn workspace(&self, index: usize) -> &Workspace<T> {
|
||||||
|
&self.workspaces[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn workspace_mut(&mut self, index: usize) -> &mut Workspace<T> {
|
||||||
|
&mut self.workspaces[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_workspace(&self) -> &Workspace<T> {
|
||||||
|
&self.workspaces[self.current_workspace]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_workspace_mut(&mut self) -> &mut Workspace<T> {
|
||||||
|
&mut self.workspaces[self.current_workspace]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_windows(&self) -> impl Iterator<Item = (T, &NodeLayout)> + '_ {
|
||||||
|
let reservation_windows = self.reservation_windows();
|
||||||
|
let workspace_windows = self.current_workspace().all_windows();
|
||||||
|
iter::chain(reservation_windows, workspace_windows)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dirty_windows(&self) -> impl Iterator<Item = (T, Rect)> + '_ {
|
||||||
|
let reservation_windows = self.dirty_reservation_windows();
|
||||||
|
let workspace_windows = self.current_workspace().dirty_windows();
|
||||||
|
iter::chain(reservation_windows, workspace_windows)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_frame(&self, wid: T) -> Option<Rect> {
|
||||||
|
if let Some(reservation) = self.lookup_reservation(wid) {
|
||||||
|
return reservation.layout.get();
|
||||||
|
}
|
||||||
|
self.current_workspace().window_frame(wid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_window(&mut self, wid: T, ty: &WindowType) -> Option<bool> {
|
||||||
|
match ty {
|
||||||
|
&WindowType::Reservation(height) => {
|
||||||
|
self.add_reservation(wid, height);
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
|
WindowType::Default => {
|
||||||
|
if !self.current_workspace_mut().create_window(wid) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.update_reservation_layout();
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_window(&mut self, direction: Direction) -> bool
|
||||||
|
where
|
||||||
|
T: fmt::Debug,
|
||||||
|
{
|
||||||
|
if self.current_workspace_mut().move_window(direction) {
|
||||||
|
self.update_reservation_layout();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_window(&mut self, wid: T) -> bool {
|
||||||
|
// TODO check if the window is a reservation
|
||||||
|
let mut reservation_removed = false;
|
||||||
|
self.reservations_top.retain(|res| {
|
||||||
|
if res.wid == wid {
|
||||||
|
reservation_removed = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if reservation_removed {
|
||||||
|
self.update_layout(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for workspace in self.workspaces.iter_mut() {
|
||||||
|
if workspace.remove_window(wid) {
|
||||||
|
self.update_reservation_layout();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_focused_window(&mut self, target: usize) -> bool {
|
||||||
|
let Some(focus_wid) = self.current_workspace().focused_window() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if !self.workspaces[target].create_window(focus_wid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.workspaces[target].focus_window(focus_wid);
|
||||||
|
self.current_workspace_mut().remove_window(focus_wid);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_reservation(&mut self, wid: T, height: u32) {
|
||||||
|
self.reservations_top.push(Reservation {
|
||||||
|
layout: NodeLayout::empty(),
|
||||||
|
size: height,
|
||||||
|
wid,
|
||||||
|
});
|
||||||
|
self.update_layout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dirty_reservation_windows(&self) -> impl Iterator<Item = (T, Rect)> + '_ {
|
||||||
|
self.reservation_windows().filter_map(|(wid, layout)| {
|
||||||
|
if !layout.clear_dirty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rect = layout.get()?;
|
||||||
|
Some((wid, rect))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reservation_windows(&self) -> impl Iterator<Item = (T, &NodeLayout)> + '_ {
|
||||||
|
self.reservations_top
|
||||||
|
.iter()
|
||||||
|
.map(|res| (res.wid, &res.layout))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_reservation(&self, wid: T) -> Option<&Reservation<T>> {
|
||||||
|
self.reservations_top.iter().find(|r| r.wid == wid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will also update the workspaces' layouts if reservation height changes
|
||||||
|
fn update_reservation_layout(&mut self) -> bool {
|
||||||
|
let mut res_y = 0;
|
||||||
|
for reservation in self.reservations_top.iter() {
|
||||||
|
reservation.layout.set(Rect {
|
||||||
|
x: 0,
|
||||||
|
y: res_y,
|
||||||
|
w: self.width,
|
||||||
|
h: reservation.size,
|
||||||
|
});
|
||||||
|
res_y += reservation.size;
|
||||||
|
}
|
||||||
|
let changed = res_y != self.reservation_height;
|
||||||
|
self.reservation_height = res_y;
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_layout(&mut self, force: bool) {
|
||||||
|
if self.update_reservation_layout() || force {
|
||||||
|
for workspace in self.workspaces.iter_mut() {
|
||||||
|
workspace.resize(self.width, self.height, self.reservation_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn add_reservation(&mut self, wid: T, height: u32) {
|
||||||
|
// }
|
||||||
|
}
|
67
userspace/colors/src/wm/layout.rs
Normal file
67
userspace/colors/src/wm/layout.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Rect {
|
||||||
|
pub x: u32,
|
||||||
|
pub y: u32,
|
||||||
|
pub w: u32,
|
||||||
|
pub h: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Orientation {
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NodeLayout {
|
||||||
|
pub rect: Cell<(Option<Rect>, bool)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeLayout {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
rect: Cell::new((None, false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, new: Rect) {
|
||||||
|
let (old, _) = self.rect.get();
|
||||||
|
let dirty = old.map_or(true, |old| old != new);
|
||||||
|
self.rect.set((Some(new), dirty));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> Option<Rect> {
|
||||||
|
let (value, _) = self.rect.get();
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.rect.set((None, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_dirty(&self) -> bool {
|
||||||
|
let (value, dirty) = self.rect.get();
|
||||||
|
self.rect.set((value, false));
|
||||||
|
dirty && value.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
pub fn split(&self) -> (Orientation, isize) {
|
||||||
|
match self {
|
||||||
|
Self::Left => (Orientation::Horizontal, -1),
|
||||||
|
Self::Right => (Orientation::Horizontal, 1),
|
||||||
|
Self::Up => (Orientation::Vertical, -1),
|
||||||
|
Self::Down => (Orientation::Vertical, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,703 +1,3 @@
|
|||||||
use std::{cell::Cell, collections::HashMap, hash::Hash, iter};
|
pub mod display;
|
||||||
|
pub mod layout;
|
||||||
pub type NodeId = u32;
|
pub mod workspace;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Direction {
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct Rect {
|
|
||||||
pub x: u32,
|
|
||||||
pub y: u32,
|
|
||||||
pub w: u32,
|
|
||||||
pub h: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Orientation {
|
|
||||||
Horizontal,
|
|
||||||
Vertical,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum NodeContent<T> {
|
|
||||||
Container(ContainerNode),
|
|
||||||
Window(WindowNode<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeLayout {
|
|
||||||
rect: Cell<(Option<Rect>, bool)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Node<T> {
|
|
||||||
parent: Option<NodeId>,
|
|
||||||
layout: NodeLayout,
|
|
||||||
|
|
||||||
content: NodeContent<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ContainerNode {
|
|
||||||
children: Vec<NodeId>,
|
|
||||||
orientation: Orientation,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WindowNode<T> {
|
|
||||||
pub wid: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Reservation<T> {
|
|
||||||
layout: NodeLayout,
|
|
||||||
size: u32,
|
|
||||||
wid: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Workspace<T> {
|
|
||||||
nodes: HashMap<NodeId, Node<T>>,
|
|
||||||
wid_to_nid: HashMap<T, NodeId>,
|
|
||||||
last_node_id: NodeId,
|
|
||||||
|
|
||||||
root: NodeId,
|
|
||||||
focus: Option<NodeId>,
|
|
||||||
|
|
||||||
margin: u32,
|
|
||||||
spacing: u32,
|
|
||||||
|
|
||||||
reservations_top: Vec<Reservation<T>>,
|
|
||||||
reservation_top: u32,
|
|
||||||
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Node<T> {
|
|
||||||
pub fn as_window(&self) -> Option<&WindowNode<T>> {
|
|
||||||
match &self.content {
|
|
||||||
NodeContent::Window(window) => Some(window),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_container(&self) -> Option<&ContainerNode> {
|
|
||||||
match &self.content {
|
|
||||||
NodeContent::Container(container) => Some(container),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_container_mut(&mut self) -> Option<&mut ContainerNode> {
|
|
||||||
match &mut self.content {
|
|
||||||
NodeContent::Container(container) => Some(container),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeLayout {
|
|
||||||
pub fn set(&self, new: Rect) {
|
|
||||||
let (old, _) = self.rect.get();
|
|
||||||
let dirty = old.map_or(true, |old| old != new);
|
|
||||||
self.rect.set((Some(new), dirty));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> Option<Rect> {
|
|
||||||
let (value, _) = self.rect.get();
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&self) {
|
|
||||||
self.rect.set((None, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_dirty(&self) -> bool {
|
|
||||||
let (value, dirty) = self.rect.get();
|
|
||||||
self.rect.set((value, false));
|
|
||||||
dirty && value.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Eq + Hash + Copy> Workspace<T> {
|
|
||||||
pub fn new(width: u32, height: u32) -> Self {
|
|
||||||
let root = Node {
|
|
||||||
parent: None,
|
|
||||||
layout: NodeLayout {
|
|
||||||
rect: Cell::new((None, false)),
|
|
||||||
},
|
|
||||||
content: NodeContent::Container(ContainerNode {
|
|
||||||
children: vec![],
|
|
||||||
orientation: Orientation::Horizontal,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
let nodes = HashMap::from_iter([(0, root)]);
|
|
||||||
|
|
||||||
let mut this = Self {
|
|
||||||
nodes,
|
|
||||||
wid_to_nid: HashMap::new(),
|
|
||||||
last_node_id: 0,
|
|
||||||
|
|
||||||
margin: 4,
|
|
||||||
spacing: 4,
|
|
||||||
|
|
||||||
root: 0,
|
|
||||||
focus: None,
|
|
||||||
|
|
||||||
reservation_top: 0,
|
|
||||||
reservations_top: Vec::new(),
|
|
||||||
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.update_layout();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_reservation(&mut self, wid: T, height: u32) {
|
|
||||||
self.reservations_top.push(Reservation {
|
|
||||||
layout: NodeLayout {
|
|
||||||
rect: Cell::new((None, false)),
|
|
||||||
},
|
|
||||||
size: height,
|
|
||||||
wid,
|
|
||||||
});
|
|
||||||
self.update_layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_windows(&self) -> impl Iterator<Item = (T, &NodeLayout)> + '_ {
|
|
||||||
let nodes_windows = self.nodes.iter().filter_map(|(_, node)| {
|
|
||||||
let window = node.as_window()?;
|
|
||||||
Some((window.wid, &node.layout))
|
|
||||||
// Some(node.as_window()?.wid)
|
|
||||||
});
|
|
||||||
let reservation_windows = self
|
|
||||||
.reservations_top
|
|
||||||
.iter()
|
|
||||||
.map(|res| (res.wid, &res.layout));
|
|
||||||
iter::chain(reservation_windows, nodes_windows)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dirty_windows(&self) -> impl Iterator<Item = (T, Rect)> + '_ {
|
|
||||||
self.all_windows().filter_map(|(wid, layout)| {
|
|
||||||
if !layout.clear_dirty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let rect = layout.get()?;
|
|
||||||
Some((wid, rect))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window_towards_wrap(&self, direction: Direction) -> Option<T> {
|
|
||||||
let current_nid = self.focus?;
|
|
||||||
let current_node = self.nodes.get(¤t_nid)?;
|
|
||||||
let parent_nid = current_node.parent?;
|
|
||||||
let parent_node = self.nodes.get(&parent_nid)?;
|
|
||||||
let parents_parent_nid = parent_node.parent;
|
|
||||||
let parent_container = parent_node.as_container()?;
|
|
||||||
|
|
||||||
let (orientation, delta) = direction.split();
|
|
||||||
|
|
||||||
let nid = if orientation == parent_container.orientation {
|
|
||||||
log::info!("Within parent {delta}, {:?}", parent_container.orientation);
|
|
||||||
let position_in_parent = parent_container
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.position(|&n| n == current_nid)?;
|
|
||||||
|
|
||||||
if delta > 0 {
|
|
||||||
if position_in_parent < parent_container.children.len() - 1 {
|
|
||||||
parent_container.children[position_in_parent + 1]
|
|
||||||
} else {
|
|
||||||
parent_container.children[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if position_in_parent > 0 {
|
|
||||||
parent_container.children[position_in_parent - 1]
|
|
||||||
} else {
|
|
||||||
parent_container.children[parent_container.children.len() - 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(parents_parent_nid) = parents_parent_nid {
|
|
||||||
let parents_parent_node = self.nodes.get(&parents_parent_nid)?;
|
|
||||||
let parents_parent_container = parents_parent_node.as_container()?;
|
|
||||||
assert_eq!(parents_parent_container.orientation, orientation);
|
|
||||||
let position_in_parent = parents_parent_container
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.position(|&n| n == parent_nid)?;
|
|
||||||
log::info!(
|
|
||||||
"Within parent's parent {delta}, {:?}",
|
|
||||||
parents_parent_container.orientation
|
|
||||||
);
|
|
||||||
log::info!("position_in_parent = {position_in_parent}");
|
|
||||||
|
|
||||||
if delta > 0 {
|
|
||||||
if position_in_parent < parent_container.children.len() - 1 {
|
|
||||||
parents_parent_container.children[position_in_parent + 1]
|
|
||||||
} else {
|
|
||||||
parents_parent_container.children[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if position_in_parent > 0 {
|
|
||||||
parents_parent_container.children[position_in_parent - 1]
|
|
||||||
} else {
|
|
||||||
parents_parent_container.children[parent_container.children.len() - 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.first_window_in(nid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first_window_in(&self, nid: NodeId) -> Option<T> {
|
|
||||||
let node = self.nodes.get(&nid)?;
|
|
||||||
|
|
||||||
match &node.content {
|
|
||||||
NodeContent::Window(window) => Some(window.wid),
|
|
||||||
NodeContent::Container(container) => {
|
|
||||||
let first = *container.children.first()?;
|
|
||||||
self.first_window_in(first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_window(&mut self, direction: Direction) -> bool
|
|
||||||
where
|
|
||||||
T: core::fmt::Debug,
|
|
||||||
{
|
|
||||||
if let Some(container_nid) = self.move_window_inner(direction) {
|
|
||||||
self.invalidate_layout(container_nid);
|
|
||||||
self.fixup_containers(self.root);
|
|
||||||
self.dump(self.root, 0);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump(&self, nid: NodeId, depth: usize)
|
|
||||||
where
|
|
||||||
T: core::fmt::Debug,
|
|
||||||
{
|
|
||||||
let Some(node) = self.nodes.get(&nid) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let rect = node.layout.get();
|
|
||||||
|
|
||||||
match &node.content {
|
|
||||||
NodeContent::Window(window) => {
|
|
||||||
log::info!(
|
|
||||||
"{:depth$}Window #{:?} {rect:?}",
|
|
||||||
"",
|
|
||||||
window.wid,
|
|
||||||
depth = depth * 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
NodeContent::Container(container) => {
|
|
||||||
log::info!("{:depth$}Container {rect:?} {{", "", depth = depth * 2);
|
|
||||||
for &child_nid in container.children.iter() {
|
|
||||||
self.dump(child_nid, depth + 1);
|
|
||||||
}
|
|
||||||
log::info!("{:depth$}}}", "", depth = depth * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn walk_windows<F: FnMut(T)>(&self, mut mapper: F) {
|
|
||||||
// self.walk_windows_inner(&mut mapper, self.root)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn walk_windows_inner<F: FnMut(T)>(&self, mapper: &mut F, nid: NodeId) {
|
|
||||||
// // let Some(node) = self.nodes.get(&nid) else {
|
|
||||||
// // return;
|
|
||||||
// // };
|
|
||||||
|
|
||||||
// // match &node.content {
|
|
||||||
// // NodeContent::Container(container) => {
|
|
||||||
// // for &child_nid in container.children.iter() {
|
|
||||||
// // self.walk_windows_inner(mapper, child_nid);
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// // NodeContent::Window(window) => {
|
|
||||||
// // mapper(window.wid);
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn invalidate_layout(&mut self, nid: NodeId) {
|
|
||||||
for reservation in self.reservations_top.iter() {
|
|
||||||
reservation.layout.clear();
|
|
||||||
}
|
|
||||||
self.invalidate_layout_inner(nid);
|
|
||||||
self.update_layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invalidate_layout_inner(&self, nid: NodeId) {
|
|
||||||
let Some(node) = self.nodes.get(&nid) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
node.layout.clear();
|
|
||||||
|
|
||||||
match &node.content {
|
|
||||||
NodeContent::Container(container) => {
|
|
||||||
for &child_nid in container.children.iter() {
|
|
||||||
self.invalidate_layout_inner(child_nid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixup_containers(&mut self, nid: NodeId) {
|
|
||||||
let Some(node) = self.nodes.get_mut(&nid) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let NodeContent::Container(container) = &mut node.content {
|
|
||||||
if container.children.is_empty() {
|
|
||||||
let Some(parent_nid) = node.parent else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Remove the empty container
|
|
||||||
if let Some(parent_container) = self
|
|
||||||
.nodes
|
|
||||||
.get_mut(&parent_nid)
|
|
||||||
.and_then(Node::as_container_mut)
|
|
||||||
{
|
|
||||||
parent_container.children.retain(|&n| n != nid);
|
|
||||||
self.nodes.remove(&nid);
|
|
||||||
self.invalidate_layout(parent_nid);
|
|
||||||
}
|
|
||||||
} else if container.children.len() == 1 {
|
|
||||||
// Remove the empty container and reparent its only child
|
|
||||||
if let Some(parent_nid) = node.parent {
|
|
||||||
let child_nid = container.children.remove(0);
|
|
||||||
let [Some(child), Some(parent)] =
|
|
||||||
self.nodes.get_many_mut([&child_nid, &parent_nid])
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
child.parent = Some(parent_nid);
|
|
||||||
if let Some(parent_container) = parent.as_container_mut() {
|
|
||||||
parent_container.children.retain(|&n| n != nid);
|
|
||||||
parent_container.children.push(child_nid);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.invalidate_layout(parent_nid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO borrowing stuff
|
|
||||||
let children = container.children.clone();
|
|
||||||
for child_nid in children {
|
|
||||||
self.fixup_containers(child_nid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_window_inner(&mut self, direction: Direction) -> Option<NodeId> {
|
|
||||||
let current_nid = self.focus?;
|
|
||||||
let current_node = self.nodes.get(¤t_nid)?;
|
|
||||||
let parent_nid = current_node.parent?;
|
|
||||||
let parent_node = self.nodes.get_mut(&parent_nid)?;
|
|
||||||
let parents_parent_nid = parent_node.parent;
|
|
||||||
let parent_container = parent_node.as_container_mut()?;
|
|
||||||
|
|
||||||
let (orientation, delta) = direction.split();
|
|
||||||
|
|
||||||
if parent_container.orientation == orientation {
|
|
||||||
let position_in_parent = parent_container
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.position(|&n| n == current_nid)?;
|
|
||||||
|
|
||||||
// TODO check if this item is also a container
|
|
||||||
|
|
||||||
if delta < 0 && position_in_parent > 0 {
|
|
||||||
parent_container
|
|
||||||
.children
|
|
||||||
.swap(position_in_parent - 1, position_in_parent);
|
|
||||||
Some(parent_nid)
|
|
||||||
} else if delta > 0 && position_in_parent < parent_container.children.len() - 1 {
|
|
||||||
parent_container
|
|
||||||
.children
|
|
||||||
.swap(position_in_parent, position_in_parent + 1);
|
|
||||||
Some(parent_nid)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else if let Some(parents_parent_nid) = parents_parent_nid {
|
|
||||||
// Go to parent's parent
|
|
||||||
let parents_parent_node = self.nodes.get_mut(&parents_parent_nid)?;
|
|
||||||
let parents_parent_container = parents_parent_node.as_container_mut()?;
|
|
||||||
assert_eq!(parents_parent_container.orientation, orientation);
|
|
||||||
todo!()
|
|
||||||
} else {
|
|
||||||
self.last_node_id += 1;
|
|
||||||
let new_root_nid = self.last_node_id;
|
|
||||||
|
|
||||||
// Remove child from parent
|
|
||||||
parent_container.children.retain(|&n| n != current_nid);
|
|
||||||
|
|
||||||
self.nodes.get_mut(¤t_nid)?.parent = Some(new_root_nid);
|
|
||||||
self.nodes.get_mut(&self.root)?.parent = Some(new_root_nid);
|
|
||||||
|
|
||||||
let children = if delta > 0 {
|
|
||||||
vec![self.root, current_nid]
|
|
||||||
} else {
|
|
||||||
vec![current_nid, self.root]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new root
|
|
||||||
let new_root = Node {
|
|
||||||
parent: None,
|
|
||||||
layout: NodeLayout {
|
|
||||||
rect: Cell::new((None, false)),
|
|
||||||
},
|
|
||||||
content: NodeContent::Container(ContainerNode {
|
|
||||||
children,
|
|
||||||
orientation,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
self.root = new_root_nid;
|
|
||||||
self.nodes.insert(new_root_nid, new_root);
|
|
||||||
|
|
||||||
Some(self.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn focus_window(&mut self, wid: T) -> (Option<T>, Option<NodeId>) {
|
|
||||||
let old = self.focus.take().and_then(|nid| self.wid(nid));
|
|
||||||
let new = self.wid_to_nid.get(&wid).copied();
|
|
||||||
self.focus = new;
|
|
||||||
(old, new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn focused_window(&self) -> Option<T> {
|
|
||||||
let nid = self.focus?;
|
|
||||||
Some(self.nodes.get(&nid)?.as_window()?.wid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window(&self, wid: T) -> Option<&Node<T>> {
|
|
||||||
let nid = *self.wid_to_nid.get(&wid)?;
|
|
||||||
self.nodes.get(&nid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reservation(&self, wid: T) -> Option<&Reservation<T>> {
|
|
||||||
self.reservations_top.iter().find(|r| r.wid == wid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window_frame(&self, wid: T) -> Option<Rect> {
|
|
||||||
if let Some(reservation) = self.reservation(wid) {
|
|
||||||
reservation.layout.get()
|
|
||||||
} else {
|
|
||||||
let node = self.window(wid)?;
|
|
||||||
node.layout.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&mut self, w: u32, h: u32) {
|
|
||||||
self.width = w;
|
|
||||||
self.height = h;
|
|
||||||
self.update_layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_reservation_layout(&mut self) -> u32 {
|
|
||||||
let mut res_y = 0;
|
|
||||||
for reservation in self.reservations_top.iter() {
|
|
||||||
reservation.layout.set(Rect {
|
|
||||||
x: 0,
|
|
||||||
y: res_y,
|
|
||||||
w: self.width,
|
|
||||||
h: reservation.size,
|
|
||||||
});
|
|
||||||
res_y += reservation.size;
|
|
||||||
}
|
|
||||||
self.reservation_top = res_y;
|
|
||||||
res_y
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_layout(&mut self) {
|
|
||||||
let res_y = self.update_reservation_layout();
|
|
||||||
self.update_layout_for(
|
|
||||||
self.root,
|
|
||||||
Rect {
|
|
||||||
x: self.margin,
|
|
||||||
y: self.margin + res_y,
|
|
||||||
w: self.width - self.margin * 2,
|
|
||||||
h: self.height - self.margin * 2 - res_y,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_window(&mut self, wid: T) -> bool {
|
|
||||||
if self.create_window_in(self.root, wid) {
|
|
||||||
for res in self.reservations_top.iter() {
|
|
||||||
res.layout.clear();
|
|
||||||
}
|
|
||||||
self.update_reservation_layout();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_window(&mut self, wid: T) -> bool {
|
|
||||||
let Some(nid) = self.wid_to_nid.remove(&wid) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if self.remove_inner(nid) {
|
|
||||||
self.fixup_containers(self.root);
|
|
||||||
self.update_layout();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_window_in(&mut self, container_nid: NodeId, wid: T) -> bool {
|
|
||||||
self.last_node_id += 1;
|
|
||||||
let nid = self.last_node_id;
|
|
||||||
self.nodes.insert(
|
|
||||||
nid,
|
|
||||||
Node {
|
|
||||||
parent: Some(container_nid),
|
|
||||||
layout: NodeLayout {
|
|
||||||
rect: Cell::new((None, false)),
|
|
||||||
},
|
|
||||||
content: NodeContent::Window(WindowNode { wid }),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.wid_to_nid.insert(wid, nid);
|
|
||||||
if !self.add_child_to(container_nid, nid) {
|
|
||||||
self.wid_to_nid.remove(&wid);
|
|
||||||
self.nodes.remove(&nid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self.update_layout();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wid(&self, nid: NodeId) -> Option<T> {
|
|
||||||
self.nodes.get(&nid).and_then(|node| match &node.content {
|
|
||||||
NodeContent::Window(window) => Some(window.wid),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_layout_for(&self, node_id: NodeId, rect: Rect) {
|
|
||||||
let Some(node) = self.nodes.get(&node_id) else {
|
|
||||||
log::warn!("update_layout_for: no node {node_id}");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
node.layout.set(rect);
|
|
||||||
|
|
||||||
match &node.content {
|
|
||||||
NodeContent::Container(container) => {
|
|
||||||
if container.children.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match container.orientation {
|
|
||||||
Orientation::Vertical => {
|
|
||||||
let hstep = (rect.h - self.spacing * container.children.len() as u32)
|
|
||||||
/ container.children.len() as u32;
|
|
||||||
let ystep = hstep + self.spacing;
|
|
||||||
|
|
||||||
for (i, &child_nid) in container.children.iter().enumerate() {
|
|
||||||
let i = i as u32;
|
|
||||||
let child_rect = Rect {
|
|
||||||
x: rect.x,
|
|
||||||
y: rect.y + i * ystep,
|
|
||||||
w: rect.w,
|
|
||||||
h: hstep,
|
|
||||||
};
|
|
||||||
log::info!("Frame #{child_nid} size: {child_rect:?}");
|
|
||||||
self.update_layout_for(child_nid, child_rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Orientation::Horizontal => {
|
|
||||||
let wstep = (rect.w - self.spacing * container.children.len() as u32)
|
|
||||||
/ container.children.len() as u32;
|
|
||||||
let xstep = wstep + self.spacing;
|
|
||||||
|
|
||||||
for (i, &child_nid) in container.children.iter().enumerate() {
|
|
||||||
let i = i as u32;
|
|
||||||
let child_rect = Rect {
|
|
||||||
x: rect.x + i * xstep,
|
|
||||||
y: rect.y,
|
|
||||||
w: wstep,
|
|
||||||
h: rect.h,
|
|
||||||
};
|
|
||||||
log::info!("Frame #{child_nid} size: {child_rect:?}");
|
|
||||||
self.update_layout_for(child_nid, child_rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_inner(&mut self, node_id: NodeId) -> bool {
|
|
||||||
if node_id == self.root {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let Some(node) = self.nodes.remove(&node_id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let container_nid = unsafe { node.parent.unwrap_unchecked() };
|
|
||||||
let Some(container_node) = self.nodes.get_mut(&container_nid) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let NodeContent::Container(container) = &mut container_node.content else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
container_node.layout.clear();
|
|
||||||
container.children.retain(|&c| c != node_id);
|
|
||||||
// Only root container can be empty
|
|
||||||
debug_assert!(!(container_node.parent.is_some() && container.children.is_empty()));
|
|
||||||
if container.children.len() == 1 && container_node.parent.is_some() {
|
|
||||||
// Lift the remaining node to the parent container
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_child_to(&mut self, container_nid: NodeId, child_nid: NodeId) -> bool {
|
|
||||||
let [Some(container_node), Some(child_node)] =
|
|
||||||
self.nodes.get_many_mut([&container_nid, &child_nid])
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let NodeContent::Container(container) = &mut container_node.content else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
container_node.layout.clear();
|
|
||||||
child_node.layout.clear();
|
|
||||||
child_node.parent = Some(container_nid);
|
|
||||||
container.children.push(child_nid);
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
pub fn split(&self) -> (Orientation, isize) {
|
|
||||||
match self {
|
|
||||||
Self::Left => (Orientation::Horizontal, -1),
|
|
||||||
Self::Right => (Orientation::Horizontal, 1),
|
|
||||||
Self::Up => (Orientation::Vertical, -1),
|
|
||||||
Self::Down => (Orientation::Vertical, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
599
userspace/colors/src/wm/workspace.rs
Normal file
599
userspace/colors/src/wm/workspace.rs
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
use std::{collections::HashMap, hash::Hash};
|
||||||
|
|
||||||
|
use super::layout::{Direction, NodeLayout, Orientation, Rect};
|
||||||
|
|
||||||
|
pub type NodeId = u32;
|
||||||
|
|
||||||
|
pub enum NodeContent<T> {
|
||||||
|
Container(ContainerNode),
|
||||||
|
Window(WindowNode<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Node<T> {
|
||||||
|
parent: Option<NodeId>,
|
||||||
|
layout: NodeLayout,
|
||||||
|
|
||||||
|
content: NodeContent<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ContainerNode {
|
||||||
|
children: Vec<NodeId>,
|
||||||
|
orientation: Orientation,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowNode<T> {
|
||||||
|
pub wid: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Workspace<T> {
|
||||||
|
nodes: HashMap<NodeId, Node<T>>,
|
||||||
|
wid_to_nid: HashMap<T, NodeId>,
|
||||||
|
last_node_id: NodeId,
|
||||||
|
|
||||||
|
root: NodeId,
|
||||||
|
focus: Option<NodeId>,
|
||||||
|
|
||||||
|
margin: u32,
|
||||||
|
spacing: u32,
|
||||||
|
|
||||||
|
// reservations_top: Vec<Reservation<T>>,
|
||||||
|
reservation_top: u32,
|
||||||
|
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Node<T> {
|
||||||
|
pub fn as_window(&self) -> Option<&WindowNode<T>> {
|
||||||
|
match &self.content {
|
||||||
|
NodeContent::Window(window) => Some(window),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_container(&self) -> Option<&ContainerNode> {
|
||||||
|
match &self.content {
|
||||||
|
NodeContent::Container(container) => Some(container),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_container_mut(&mut self) -> Option<&mut ContainerNode> {
|
||||||
|
match &mut self.content {
|
||||||
|
NodeContent::Container(container) => Some(container),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq + Hash + Copy> Workspace<T> {
|
||||||
|
pub fn new(width: u32, height: u32) -> Self {
|
||||||
|
let root = Node {
|
||||||
|
parent: None,
|
||||||
|
layout: NodeLayout::empty(),
|
||||||
|
content: NodeContent::Container(ContainerNode {
|
||||||
|
children: vec![],
|
||||||
|
orientation: Orientation::Horizontal,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let nodes = HashMap::from_iter([(0, root)]);
|
||||||
|
|
||||||
|
let mut this = Self {
|
||||||
|
nodes,
|
||||||
|
wid_to_nid: HashMap::new(),
|
||||||
|
last_node_id: 0,
|
||||||
|
|
||||||
|
margin: 4,
|
||||||
|
spacing: 4,
|
||||||
|
|
||||||
|
root: 0,
|
||||||
|
focus: None,
|
||||||
|
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
reservation_top: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.update_layout();
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_windows(&self) -> impl Iterator<Item = (T, &NodeLayout)> + '_ {
|
||||||
|
self.nodes.iter().filter_map(|(_, node)| {
|
||||||
|
let window = node.as_window()?;
|
||||||
|
Some((window.wid, &node.layout))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dirty_windows(&self) -> impl Iterator<Item = (T, Rect)> + '_ {
|
||||||
|
self.all_windows().filter_map(|(wid, layout)| {
|
||||||
|
if !layout.clear_dirty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rect = layout.get()?;
|
||||||
|
Some((wid, rect))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_towards_wrap(&self, direction: Direction) -> Option<T> {
|
||||||
|
let current_nid = self.focus?;
|
||||||
|
let current_node = self.nodes.get(¤t_nid)?;
|
||||||
|
let parent_nid = current_node.parent?;
|
||||||
|
let parent_node = self.nodes.get(&parent_nid)?;
|
||||||
|
let parents_parent_nid = parent_node.parent;
|
||||||
|
let parent_container = parent_node.as_container()?;
|
||||||
|
|
||||||
|
let (orientation, delta) = direction.split();
|
||||||
|
|
||||||
|
let nid = if orientation == parent_container.orientation {
|
||||||
|
log::info!("Within parent {delta}, {:?}", parent_container.orientation);
|
||||||
|
let position_in_parent = parent_container
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.position(|&n| n == current_nid)?;
|
||||||
|
|
||||||
|
if delta > 0 {
|
||||||
|
if position_in_parent < parent_container.children.len() - 1 {
|
||||||
|
parent_container.children[position_in_parent + 1]
|
||||||
|
} else {
|
||||||
|
parent_container.children[0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if position_in_parent > 0 {
|
||||||
|
parent_container.children[position_in_parent - 1]
|
||||||
|
} else {
|
||||||
|
parent_container.children[parent_container.children.len() - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(parents_parent_nid) = parents_parent_nid {
|
||||||
|
let parents_parent_node = self.nodes.get(&parents_parent_nid)?;
|
||||||
|
let parents_parent_container = parents_parent_node.as_container()?;
|
||||||
|
assert_eq!(parents_parent_container.orientation, orientation);
|
||||||
|
let position_in_parent = parents_parent_container
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.position(|&n| n == parent_nid)?;
|
||||||
|
log::info!(
|
||||||
|
"Within parent's parent {delta}, {:?}",
|
||||||
|
parents_parent_container.orientation
|
||||||
|
);
|
||||||
|
log::info!("position_in_parent = {position_in_parent}");
|
||||||
|
|
||||||
|
if delta > 0 {
|
||||||
|
if position_in_parent < parent_container.children.len() - 1 {
|
||||||
|
parents_parent_container.children[position_in_parent + 1]
|
||||||
|
} else {
|
||||||
|
parents_parent_container.children[0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if position_in_parent > 0 {
|
||||||
|
parents_parent_container.children[position_in_parent - 1]
|
||||||
|
} else {
|
||||||
|
parents_parent_container.children[parent_container.children.len() - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.first_window_in(nid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_window_in(&self, nid: NodeId) -> Option<T> {
|
||||||
|
let node = self.nodes.get(&nid)?;
|
||||||
|
|
||||||
|
match &node.content {
|
||||||
|
NodeContent::Window(window) => Some(window.wid),
|
||||||
|
NodeContent::Container(container) => {
|
||||||
|
let first = *container.children.first()?;
|
||||||
|
self.first_window_in(first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_window(&mut self, direction: Direction) -> bool
|
||||||
|
where
|
||||||
|
T: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
if let Some(container_nid) = self.move_window_inner(direction) {
|
||||||
|
self.invalidate_layout(container_nid);
|
||||||
|
self.fixup_containers(self.root);
|
||||||
|
self.dump(self.root, 0);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(&self, nid: NodeId, depth: usize)
|
||||||
|
where
|
||||||
|
T: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
let Some(node) = self.nodes.get(&nid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let rect = node.layout.get();
|
||||||
|
|
||||||
|
match &node.content {
|
||||||
|
NodeContent::Window(window) => {
|
||||||
|
log::info!(
|
||||||
|
"{:depth$}Window #{:?} {rect:?}",
|
||||||
|
"",
|
||||||
|
window.wid,
|
||||||
|
depth = depth * 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
NodeContent::Container(container) => {
|
||||||
|
log::info!("{:depth$}Container {rect:?} {{", "", depth = depth * 2);
|
||||||
|
for &child_nid in container.children.iter() {
|
||||||
|
self.dump(child_nid, depth + 1);
|
||||||
|
}
|
||||||
|
log::info!("{:depth$}}}", "", depth = depth * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn walk_windows<F: FnMut(T)>(&self, mut mapper: F) {
|
||||||
|
// self.walk_windows_inner(&mut mapper, self.root)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn walk_windows_inner<F: FnMut(T)>(&self, mapper: &mut F, nid: NodeId) {
|
||||||
|
// // let Some(node) = self.nodes.get(&nid) else {
|
||||||
|
// // return;
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // match &node.content {
|
||||||
|
// // NodeContent::Container(container) => {
|
||||||
|
// // for &child_nid in container.children.iter() {
|
||||||
|
// // self.walk_windows_inner(mapper, child_nid);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // NodeContent::Window(window) => {
|
||||||
|
// // mapper(window.wid);
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn invalidate_layout(&mut self, nid: NodeId) {
|
||||||
|
// for reservation in self.reservations_top.iter() {
|
||||||
|
// reservation.layout.clear();
|
||||||
|
// }
|
||||||
|
self.invalidate_layout_inner(nid);
|
||||||
|
self.update_layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate_layout_inner(&self, nid: NodeId) {
|
||||||
|
let Some(node) = self.nodes.get(&nid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
node.layout.clear();
|
||||||
|
|
||||||
|
match &node.content {
|
||||||
|
NodeContent::Container(container) => {
|
||||||
|
for &child_nid in container.children.iter() {
|
||||||
|
self.invalidate_layout_inner(child_nid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixup_containers(&mut self, nid: NodeId) {
|
||||||
|
let Some(node) = self.nodes.get_mut(&nid) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let NodeContent::Container(container) = &mut node.content {
|
||||||
|
if container.children.is_empty() {
|
||||||
|
let Some(parent_nid) = node.parent else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Remove the empty container
|
||||||
|
if let Some(parent_container) = self
|
||||||
|
.nodes
|
||||||
|
.get_mut(&parent_nid)
|
||||||
|
.and_then(Node::as_container_mut)
|
||||||
|
{
|
||||||
|
parent_container.children.retain(|&n| n != nid);
|
||||||
|
self.nodes.remove(&nid);
|
||||||
|
self.invalidate_layout(parent_nid);
|
||||||
|
}
|
||||||
|
} else if container.children.len() == 1 {
|
||||||
|
// Remove the empty container and reparent its only child
|
||||||
|
if let Some(parent_nid) = node.parent {
|
||||||
|
let child_nid = container.children.remove(0);
|
||||||
|
let [Some(child), Some(parent)] =
|
||||||
|
self.nodes.get_many_mut([&child_nid, &parent_nid])
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
child.parent = Some(parent_nid);
|
||||||
|
if let Some(parent_container) = parent.as_container_mut() {
|
||||||
|
parent_container.children.retain(|&n| n != nid);
|
||||||
|
parent_container.children.push(child_nid);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.invalidate_layout(parent_nid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO borrowing stuff
|
||||||
|
let children = container.children.clone();
|
||||||
|
for child_nid in children {
|
||||||
|
self.fixup_containers(child_nid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_window_inner(&mut self, direction: Direction) -> Option<NodeId> {
|
||||||
|
let current_nid = self.focus?;
|
||||||
|
let current_node = self.nodes.get(¤t_nid)?;
|
||||||
|
let parent_nid = current_node.parent?;
|
||||||
|
let parent_node = self.nodes.get_mut(&parent_nid)?;
|
||||||
|
let parents_parent_nid = parent_node.parent;
|
||||||
|
let parent_container = parent_node.as_container_mut()?;
|
||||||
|
|
||||||
|
let (orientation, delta) = direction.split();
|
||||||
|
|
||||||
|
if parent_container.orientation == orientation {
|
||||||
|
let position_in_parent = parent_container
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.position(|&n| n == current_nid)?;
|
||||||
|
|
||||||
|
// TODO check if this item is also a container
|
||||||
|
|
||||||
|
if delta < 0 && position_in_parent > 0 {
|
||||||
|
parent_container
|
||||||
|
.children
|
||||||
|
.swap(position_in_parent - 1, position_in_parent);
|
||||||
|
Some(parent_nid)
|
||||||
|
} else if delta > 0 && position_in_parent < parent_container.children.len() - 1 {
|
||||||
|
parent_container
|
||||||
|
.children
|
||||||
|
.swap(position_in_parent, position_in_parent + 1);
|
||||||
|
Some(parent_nid)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if let Some(parents_parent_nid) = parents_parent_nid {
|
||||||
|
// Go to parent's parent
|
||||||
|
let parents_parent_node = self.nodes.get_mut(&parents_parent_nid)?;
|
||||||
|
let parents_parent_container = parents_parent_node.as_container_mut()?;
|
||||||
|
assert_eq!(parents_parent_container.orientation, orientation);
|
||||||
|
todo!()
|
||||||
|
} else {
|
||||||
|
self.last_node_id += 1;
|
||||||
|
let new_root_nid = self.last_node_id;
|
||||||
|
|
||||||
|
// Remove child from parent
|
||||||
|
parent_container.children.retain(|&n| n != current_nid);
|
||||||
|
|
||||||
|
self.nodes.get_mut(¤t_nid)?.parent = Some(new_root_nid);
|
||||||
|
self.nodes.get_mut(&self.root)?.parent = Some(new_root_nid);
|
||||||
|
|
||||||
|
let children = if delta > 0 {
|
||||||
|
vec![self.root, current_nid]
|
||||||
|
} else {
|
||||||
|
vec![current_nid, self.root]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a new root
|
||||||
|
let new_root = Node {
|
||||||
|
parent: None,
|
||||||
|
layout: NodeLayout::empty(),
|
||||||
|
content: NodeContent::Container(ContainerNode {
|
||||||
|
children,
|
||||||
|
orientation,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
self.root = new_root_nid;
|
||||||
|
self.nodes.insert(new_root_nid, new_root);
|
||||||
|
|
||||||
|
Some(self.root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_window(&mut self, wid: T) -> (Option<T>, Option<NodeId>) {
|
||||||
|
let old = self.focus.take().and_then(|nid| self.wid(nid));
|
||||||
|
let new = self.wid_to_nid.get(&wid).copied();
|
||||||
|
self.focus = new;
|
||||||
|
(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focused_window(&self) -> Option<T> {
|
||||||
|
let nid = self.focus?;
|
||||||
|
Some(self.nodes.get(&nid)?.as_window()?.wid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn window(&self, wid: T) -> Option<&Node<T>> {
|
||||||
|
let nid = *self.wid_to_nid.get(&wid)?;
|
||||||
|
self.nodes.get(&nid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn reservation(&self, wid: T) -> Option<&Reservation<T>> {
|
||||||
|
// self.reservations_top.iter().find(|r| r.wid == wid)
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub(super) fn window_frame(&self, wid: T) -> Option<Rect> {
|
||||||
|
self.window(wid)?.layout.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn resize(&mut self, w: u32, h: u32, reservation_top: u32) {
|
||||||
|
self.width = w;
|
||||||
|
self.height = h;
|
||||||
|
self.reservation_top = reservation_top;
|
||||||
|
self.update_layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn update_reservation_layout(&mut self) -> u32 {
|
||||||
|
// res_y
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub(super) fn update_layout(&mut self) {
|
||||||
|
self.update_layout_for(
|
||||||
|
self.root,
|
||||||
|
Rect {
|
||||||
|
x: self.margin,
|
||||||
|
y: self.margin + self.reservation_top,
|
||||||
|
w: self.width - self.margin * 2,
|
||||||
|
h: self.height - self.margin * 2 - self.reservation_top,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn create_window(&mut self, wid: T) -> bool {
|
||||||
|
self.create_window_in(self.root, wid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_window(&mut self, wid: T) -> bool {
|
||||||
|
let Some(nid) = self.wid_to_nid.remove(&wid) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if self.remove_inner(nid) {
|
||||||
|
if self.focus == Some(nid) {
|
||||||
|
let wid = self.first_window_in(self.root);
|
||||||
|
if let Some(wid) = wid {
|
||||||
|
self.focus_window(wid);
|
||||||
|
} else {
|
||||||
|
self.focus = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fixup_containers(self.root);
|
||||||
|
self.update_layout();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_window_in(&mut self, container_nid: NodeId, wid: T) -> bool {
|
||||||
|
self.last_node_id += 1;
|
||||||
|
let nid = self.last_node_id;
|
||||||
|
self.nodes.insert(
|
||||||
|
nid,
|
||||||
|
Node {
|
||||||
|
parent: Some(container_nid),
|
||||||
|
layout: NodeLayout::empty(),
|
||||||
|
content: NodeContent::Window(WindowNode { wid }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.wid_to_nid.insert(wid, nid);
|
||||||
|
if !self.add_child_to(container_nid, nid) {
|
||||||
|
self.wid_to_nid.remove(&wid);
|
||||||
|
self.nodes.remove(&nid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.update_layout();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wid(&self, nid: NodeId) -> Option<T> {
|
||||||
|
self.nodes.get(&nid).and_then(|node| match &node.content {
|
||||||
|
NodeContent::Window(window) => Some(window.wid),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_layout_for(&self, node_id: NodeId, rect: Rect) {
|
||||||
|
let Some(node) = self.nodes.get(&node_id) else {
|
||||||
|
log::warn!("update_layout_for: no node {node_id}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
node.layout.set(rect);
|
||||||
|
|
||||||
|
match &node.content {
|
||||||
|
NodeContent::Container(container) => {
|
||||||
|
if container.children.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match container.orientation {
|
||||||
|
Orientation::Vertical => {
|
||||||
|
let hstep = (rect.h - self.spacing * container.children.len() as u32)
|
||||||
|
/ container.children.len() as u32;
|
||||||
|
let ystep = hstep + self.spacing;
|
||||||
|
|
||||||
|
for (i, &child_nid) in container.children.iter().enumerate() {
|
||||||
|
let i = i as u32;
|
||||||
|
let child_rect = Rect {
|
||||||
|
x: rect.x,
|
||||||
|
y: rect.y + i * ystep,
|
||||||
|
w: rect.w,
|
||||||
|
h: hstep,
|
||||||
|
};
|
||||||
|
log::info!("Frame #{child_nid} size: {child_rect:?}");
|
||||||
|
self.update_layout_for(child_nid, child_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Orientation::Horizontal => {
|
||||||
|
let wstep = (rect.w - self.spacing * container.children.len() as u32)
|
||||||
|
/ container.children.len() as u32;
|
||||||
|
let xstep = wstep + self.spacing;
|
||||||
|
|
||||||
|
for (i, &child_nid) in container.children.iter().enumerate() {
|
||||||
|
let i = i as u32;
|
||||||
|
let child_rect = Rect {
|
||||||
|
x: rect.x + i * xstep,
|
||||||
|
y: rect.y,
|
||||||
|
w: wstep,
|
||||||
|
h: rect.h,
|
||||||
|
};
|
||||||
|
log::info!("Frame #{child_nid} size: {child_rect:?}");
|
||||||
|
self.update_layout_for(child_nid, child_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_inner(&mut self, node_id: NodeId) -> bool {
|
||||||
|
if node_id == self.root {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(node) = self.nodes.remove(&node_id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let container_nid = unsafe { node.parent.unwrap_unchecked() };
|
||||||
|
let Some(container_node) = self.nodes.get_mut(&container_nid) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let NodeContent::Container(container) = &mut container_node.content else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
container_node.layout.clear();
|
||||||
|
container.children.retain(|&c| c != node_id);
|
||||||
|
// Only root container can be empty
|
||||||
|
debug_assert!(!(container_node.parent.is_some() && container.children.is_empty()));
|
||||||
|
if container.children.len() == 1 && container_node.parent.is_some() {
|
||||||
|
// Lift the remaining node to the parent container
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_child_to(&mut self, container_nid: NodeId, child_nid: NodeId) -> bool {
|
||||||
|
let [Some(container_node), Some(child_node)] =
|
||||||
|
self.nodes.get_many_mut([&container_nid, &child_nid])
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let NodeContent::Container(container) = &mut container_node.content else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
container_node.layout.clear();
|
||||||
|
child_node.layout.clear();
|
||||||
|
child_node.parent = Some(container_nid);
|
||||||
|
container.children.push(child_nid);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user