The focus tracking method currently used is something like this:
struct WM {
windows: Vec<WindowState>,
focused_window: Option<WindowState>,
}
and then to set the focused window we do this:
fn set_focus(&mut self, window: Window) {
if let Some(win_state) = self.windows.iter().find(|w| w.window == window) {
self.focused_window = win_state.clone();
}
}
but cloning it means that it's not gonna be up to date with the windows
vector,
so it requires additional bookkeeping or having to get it from the windows
vector every time
one solution to this problem could be by using a different data structure like the Zipper.
I found out about it from the penrose from scratch series ep09 (thank you @sminez !)
the basic idea is to have this kind of structure:
struct Windows {
left: Vec<WindowState>,
focus: Option<WindowState>,
right: Vec<WindowState>,
}
and that allows for direct access to the focused windows and no need to keep a separate manually updated focused_window
field
also this can make it much easier for nesting the same structure for workspaces/tags like this:
struct Screens {
left: Vec<Screen>,
focus: Option<Screen>, // arguably should not be an option
right: Vec<Screen>,
}
struct Screen {
tags: Tags
}
struct Tags {
left: Vec<Tag>,
focus: Option<Tag>,
right: Vec<Tag>,
}
struct Tag {
windows: Windows,
}
struct Windows {
left: Vec<WindowState>,
focus: Option<WindowState>,
right: Vec<WindowState>,
}
struct WindowState {
// window data
}
then add methods to make accessing nested stuff easier
it's relatively more complicated than the current method, but I think it's worth adding.
it's better than just having a Option<usize>
because the index is not guaranteed to be there, and even if it exists it could be pointing to another element, so it needs to be manually managed, also it might introduce lifetime issues when using the windows
vector, not sure tho, so before doing that, experimenting with something like focus: Option<usize>
on another branch could be worth doing
also could check out something like indexmap
UPDATE:
after thinking about it for a bit I think using indexmap could be better than using a zipper, because zippers are better for relative movement, so if I want to have directional focus with MOD + k
for example I would need to use the index or something like that, but if I use an indexmap
I can get the handle to the window (which will be the window id from the x session) and then access it like that, while also being able to iterate through the windows since it preserves the insertion order (but still need to make sure it actually does, because the docs mentions it's ordered until you remove an element (?))