mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-13 08:01:05 +01:00
Rework window tracking
This commit is contained in:
parent
c85ada88e8
commit
ba69b21c52
9 changed files with 227 additions and 343 deletions
|
@ -7,10 +7,7 @@
|
|||
// The MessagePack format for these is a one-element map where the element's key is the enum name and its
|
||||
// value is a map of the enum's values
|
||||
|
||||
use crate::{
|
||||
tag::TagId,
|
||||
window::{window_state::WindowId, WindowProperties},
|
||||
};
|
||||
use crate::window::{window_state::WindowId, WindowProperties};
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
|
||||
pub struct CallbackId(pub u32);
|
||||
|
@ -42,25 +39,26 @@ pub enum Msg {
|
|||
},
|
||||
MoveWindowToTag {
|
||||
window_id: WindowId,
|
||||
tag_id: TagId,
|
||||
tag_id: String,
|
||||
},
|
||||
ToggleTagOnWindow {
|
||||
window_id: WindowId,
|
||||
tag_id: TagId,
|
||||
tag_id: String,
|
||||
},
|
||||
|
||||
// Tag management
|
||||
ToggleTag {
|
||||
tag_id: TagId,
|
||||
tag_id: String,
|
||||
},
|
||||
SwitchToTag {
|
||||
tag_id: TagId,
|
||||
tag_id: String,
|
||||
},
|
||||
AddTags {
|
||||
tags: Vec<TagId>,
|
||||
tags: Vec<String>,
|
||||
},
|
||||
RemoveTags {
|
||||
tags: Vec<TagId>,
|
||||
// TODO:
|
||||
tags: Vec<String>,
|
||||
},
|
||||
|
||||
// Process management
|
||||
|
|
|
@ -21,8 +21,6 @@ use smithay::{
|
|||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
layout::{Layout, LayoutVec},
|
||||
output::OutputState,
|
||||
state::State,
|
||||
window::window_state::{WindowResizeState, WindowState},
|
||||
};
|
||||
|
@ -93,25 +91,7 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
|
|||
return;
|
||||
}
|
||||
|
||||
// data.swap_window_positions(&self.window, &window_under);
|
||||
let output = data.focus_state.focused_output.as_ref().unwrap();
|
||||
OutputState::with(output, |state| {
|
||||
let mut tags = data
|
||||
.tag_state
|
||||
.tags
|
||||
.iter_mut()
|
||||
.filter(|tg| state.tags.contains(&tg.id));
|
||||
|
||||
if let Some(first) = tags.next() {
|
||||
let mut layout = first.windows.as_master_stack();
|
||||
|
||||
for tg in tags {
|
||||
layout = layout.chain_with(&mut tg.windows);
|
||||
}
|
||||
|
||||
layout.swap(&data.space, &self.window, &window_under);
|
||||
}
|
||||
})
|
||||
data.swap_window_positions(&self.window, &window_under);
|
||||
}
|
||||
} else {
|
||||
let delta = event.location - self.start_data.location;
|
||||
|
|
102
src/handlers.rs
102
src/handlers.rs
|
@ -50,7 +50,7 @@ use crate::{
|
|||
layout::{Layout, LayoutVec},
|
||||
output::OutputState,
|
||||
state::{ClientState, State},
|
||||
window::window_state::{WindowResizeState, WindowState},
|
||||
window::window_state::{CommitState, WindowResizeState, WindowState},
|
||||
};
|
||||
|
||||
impl<B: Backend> BufferHandler for State<B> {
|
||||
|
@ -122,6 +122,17 @@ impl<B: Backend> CompositorHandler for State<B> {
|
|||
state.resize_state = WindowResizeState::Idle;
|
||||
self.space.map_element(window.clone(), new_pos, false);
|
||||
}
|
||||
|
||||
if let CommitState::Acked = state.needs_raise {
|
||||
let clone = window.clone();
|
||||
|
||||
// FIXME: happens before the other windows ack, so it's useless
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
tracing::debug!("raising window");
|
||||
data.state.space.raise_element(&clone, true);
|
||||
});
|
||||
state.needs_raise = CommitState::Idle;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -226,28 +237,31 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
let window = Window::new(surface);
|
||||
|
||||
WindowState::with(&window, |state| {
|
||||
state.tags = if let Some(focused_output) = &self.focus_state.focused_output {
|
||||
OutputState::with(focused_output, |state| {
|
||||
let output_tags: Vec<crate::tag::TagId> = state.tags.iter().cloned().collect();
|
||||
state.tags = match (
|
||||
&self.focus_state.focused_output,
|
||||
self.space.outputs().next(),
|
||||
) {
|
||||
(Some(output), _) | (None, Some(output)) => OutputState::with(output, |state| {
|
||||
let output_tags = state
|
||||
.focused_tags()
|
||||
.map(|tag| tag.id.clone())
|
||||
.collect::<Vec<_>>();
|
||||
if !output_tags.is_empty() {
|
||||
output_tags
|
||||
} else if let Some(first_tag) = self.tag_state.tags.first() {
|
||||
} else if let Some(first_tag) = state.tags.first() {
|
||||
vec![first_tag.id.clone()]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}) // TODO: repetition
|
||||
} else if let Some(first_tag) = self.tag_state.tags.first() {
|
||||
vec![first_tag.id.clone()]
|
||||
} else {
|
||||
vec![]
|
||||
}),
|
||||
(None, None) => vec![],
|
||||
};
|
||||
|
||||
tracing::debug!("new window, tags are {:?}", state.tags);
|
||||
});
|
||||
|
||||
self.windows.push(window.clone());
|
||||
// self.space.map_element(window.clone(), (0, 0), true);
|
||||
let clone = window.clone();
|
||||
self.loop_handle.insert_idle(move |data| {
|
||||
data.state
|
||||
.seat
|
||||
|
@ -255,7 +269,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
.expect("Seat had no keyboard") // FIXME: actually handle error
|
||||
.set_focus(
|
||||
&mut data.state,
|
||||
Some(clone.toplevel().wl_surface().clone()),
|
||||
Some(window.toplevel().wl_surface().clone()),
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
);
|
||||
});
|
||||
|
@ -263,26 +277,10 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
self.loop_handle.insert_idle(move |data| {
|
||||
if let Some(focused_output) = &data.state.focus_state.focused_output {
|
||||
OutputState::with(focused_output, |state| {
|
||||
let window = window.clone();
|
||||
let mut tags = data
|
||||
.state
|
||||
.tag_state
|
||||
.tags
|
||||
.iter_mut()
|
||||
.filter(|tg| state.tags.contains(&tg.id));
|
||||
|
||||
if let Some(first) = tags.next() {
|
||||
let mut layout = first.windows.as_master_stack();
|
||||
|
||||
for tg in tags {
|
||||
layout = layout.chain_with(&mut tg.windows);
|
||||
}
|
||||
|
||||
layout.add(&data.state.space, focused_output, window);
|
||||
}
|
||||
for tag in data.state.tag_state.tags.iter() {
|
||||
tracing::debug!("tag {:?}, {}", tag.id, tag.windows.len());
|
||||
}
|
||||
data.state
|
||||
.windows
|
||||
.to_master_stack(state.focused_tags().map(|tag| tag.id.clone()).collect())
|
||||
.layout(&data.state.space, focused_output);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -290,44 +288,15 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
|
||||
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
||||
tracing::debug!("toplevel destroyed");
|
||||
let window = self
|
||||
.windows
|
||||
.iter()
|
||||
.find(|&win| win.toplevel() == &surface)
|
||||
.unwrap();
|
||||
self.windows.retain(|window| window.toplevel() != &surface);
|
||||
if let Some(focused_output) = self.focus_state.focused_output.as_ref() {
|
||||
OutputState::with(focused_output, |state| {
|
||||
let mut tags = self
|
||||
.tag_state
|
||||
.tags
|
||||
.iter_mut()
|
||||
.filter(|tg| state.tags.contains(&tg.id));
|
||||
|
||||
if let Some(first) = tags.next() {
|
||||
tracing::debug!("first tag: {:?}", first.id);
|
||||
let mut layout = first.windows.as_master_stack();
|
||||
|
||||
for tg in tags {
|
||||
tracing::debug!("tag: {:?}", tg.id);
|
||||
layout = layout.chain_with(&mut tg.windows);
|
||||
}
|
||||
|
||||
// This will only remove the window from focused tags...
|
||||
layout.remove(&self.space, focused_output, window);
|
||||
}
|
||||
|
||||
// ...so here we remove the window from any tag that isn't focused
|
||||
for tag in self.tag_state.tags.iter_mut() {
|
||||
tag.windows.retain(|el| el != window);
|
||||
}
|
||||
self.windows
|
||||
.to_master_stack(state.focused_tags().map(|tag| tag.id.clone()).collect())
|
||||
.layout(&self.space, focused_output);
|
||||
});
|
||||
}
|
||||
|
||||
for tag in self.tag_state.tags.iter() {
|
||||
tracing::debug!("tag {:?}, {}", tag.id, tag.windows.len());
|
||||
}
|
||||
|
||||
self.windows.retain(|window| window.toplevel() != &surface);
|
||||
// let mut windows: Vec<Window> = self.space.elements().cloned().collect();
|
||||
// windows.retain(|window| window.toplevel() != &surface);
|
||||
// Layouts::master_stack(self, windows, crate::layout::Direction::Left);
|
||||
|
@ -443,6 +412,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
|||
Configure::Popup(_) => todo!(),
|
||||
}
|
||||
}
|
||||
if let CommitState::RequestReceived(_serial) = state.needs_raise {
|
||||
state.needs_raise = CommitState::Acked;
|
||||
}
|
||||
});
|
||||
|
||||
// HACK: If a window is currently going through something that generates a bunch of
|
||||
|
|
238
src/layout.rs
238
src/layout.rs
|
@ -4,15 +4,18 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use itertools::Itertools;
|
||||
use smithay::{
|
||||
desktop::{space::SpaceElement, Space, Window},
|
||||
output::Output,
|
||||
utils::{Logical, Size},
|
||||
wayland::{compositor, shell::xdg::XdgToplevelSurfaceData},
|
||||
};
|
||||
|
||||
use crate::window::window_state::{WindowResizeState, WindowState};
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
state::State,
|
||||
tag::TagId,
|
||||
window::window_state::{WindowResizeState, WindowState},
|
||||
};
|
||||
|
||||
pub enum Direction {
|
||||
Left,
|
||||
|
@ -21,41 +24,26 @@ pub enum Direction {
|
|||
Bottom,
|
||||
}
|
||||
|
||||
pub struct MasterStack<'a, S: SpaceElement> {
|
||||
inner: Vec<&'a mut Vec<S>>,
|
||||
pub struct MasterStack<S: SpaceElement> {
|
||||
inner: Vec<S>,
|
||||
}
|
||||
|
||||
pub trait Layout<'a, S: SpaceElement> {
|
||||
/// Add a [`SpaceElement`] to this layout and update positions.
|
||||
fn add(&mut self, space: &Space<S>, output: &Output, elem: S);
|
||||
/// Remove a [`SpaceElement`] from this layout and update positions.
|
||||
fn remove(&mut self, space: &Space<S>, output: &Output, elem: &S);
|
||||
|
||||
// TODO: return result
|
||||
/// Swap two elements in this layout and update their positions.
|
||||
fn swap(&mut self, space: &Space<S>, elem1: &S, elem2: &S);
|
||||
|
||||
/// Perform a full layout with all elements. Use this when you are switching from another layout.
|
||||
fn layout(&self, space: &Space<S>, output: &Output);
|
||||
|
||||
fn chain_with(self, vec: &'a mut Vec<S>) -> Self;
|
||||
}
|
||||
|
||||
impl MasterStack<'_, Window> {
|
||||
impl MasterStack<Window> {
|
||||
pub fn master(&self) -> Option<&Window> {
|
||||
self.inner.iter().flat_map(|vec| vec.iter()).next()
|
||||
self.inner.first()
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> impl Iterator<Item = &Window> {
|
||||
self.inner
|
||||
.iter()
|
||||
.flat_map(|vec| vec.iter())
|
||||
.unique()
|
||||
.skip(1)
|
||||
self.inner.iter().skip(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl MasterStack<'_, Window> {
|
||||
impl MasterStack<Window> {
|
||||
fn layout_stack(&self, space: &Space<Window>, output: &Output) {
|
||||
let stack_count = self.stack().count();
|
||||
|
||||
|
@ -81,113 +69,36 @@ impl MasterStack<'_, Window> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn swap_window_positions(space: &Space<Window>, win1: &Window, win2: &Window) {
|
||||
// FIXME: moving the mouse quickly will break swapping
|
||||
impl<B: Backend> State<B> {
|
||||
pub fn swap_window_positions(&mut self, win1: &Window, win2: &Window) {
|
||||
// FIXME: moving the mouse quickly will break swapping
|
||||
|
||||
let win1_loc = space.element_location(win1).unwrap(); // TODO: handle unwraps
|
||||
let win2_loc = space.element_location(win2).unwrap();
|
||||
let win1_geo = win1.geometry();
|
||||
let win2_geo = win2.geometry();
|
||||
let win1_loc = self.space.element_location(win1).unwrap(); // TODO: handle unwraps
|
||||
let win2_loc = self.space.element_location(win2).unwrap();
|
||||
let win1_geo = win1.geometry();
|
||||
let win2_geo = win2.geometry();
|
||||
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(win2_geo.size);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(win1_geo.size);
|
||||
});
|
||||
win1.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(win2_geo.size);
|
||||
});
|
||||
win2.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(win1_geo.size);
|
||||
});
|
||||
|
||||
let serial = win1.toplevel().send_configure();
|
||||
WindowState::with(win1, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(serial, win2_loc);
|
||||
});
|
||||
let serial = win1.toplevel().send_configure();
|
||||
WindowState::with(win1, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(serial, win2_loc);
|
||||
});
|
||||
|
||||
let serial = win2.toplevel().send_configure();
|
||||
WindowState::with(win2, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(serial, win1_loc);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a, Window> for MasterStack<'a, Window> {
|
||||
fn add(&mut self, space: &Space<Window>, output: &Output, elem: Window) {
|
||||
for vec in self.inner.iter_mut() {
|
||||
vec.push(elem.clone());
|
||||
}
|
||||
|
||||
if self.stack().count() == 0 {
|
||||
let Some(master) = self.master() else { unreachable!() };
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
master.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
WindowState::with(master, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(
|
||||
master.toplevel().send_configure(),
|
||||
(0, 0).into(),
|
||||
);
|
||||
});
|
||||
} else if self.stack().count() == 1 {
|
||||
let Some(master) = self.master() else { unreachable!() };
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
master.toplevel().with_pending_state(|state| {
|
||||
state.size = Some((output_geo.size.w / 2, output_geo.size.h).into());
|
||||
});
|
||||
|
||||
WindowState::with(master, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(
|
||||
master.toplevel().send_configure(),
|
||||
(0, 0).into(),
|
||||
);
|
||||
});
|
||||
self.layout_stack(space, output);
|
||||
} else {
|
||||
self.layout_stack(space, output);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, space: &Space<Window>, output: &Output, elem: &Window) {
|
||||
for vec in self.inner.iter_mut() {
|
||||
vec.retain(|el| el != elem);
|
||||
}
|
||||
|
||||
let Some(master) = self.master() else { return };
|
||||
|
||||
let Some(output_geo) = space.output_geometry(output) else {
|
||||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
|
||||
if self.stack().count() == 0 {
|
||||
master.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(output_geo.size);
|
||||
});
|
||||
|
||||
WindowState::with(master, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(
|
||||
master.toplevel().send_configure(),
|
||||
(0, 0).into(),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
self.layout_stack(space, output);
|
||||
}
|
||||
}
|
||||
|
||||
fn swap(&mut self, space: &Space<Window>, elem1: &Window, elem2: &Window) {
|
||||
tracing::debug!("top of swap");
|
||||
let serial = win2.toplevel().send_configure();
|
||||
WindowState::with(win2, |state| {
|
||||
state.resize_state = WindowResizeState::WaitingForAck(serial, win1_loc);
|
||||
});
|
||||
|
||||
let mut elems = self
|
||||
.inner
|
||||
.windows
|
||||
.iter_mut()
|
||||
.flat_map(|vec| vec.iter_mut())
|
||||
.filter(|elem| *elem == elem1 || *elem == elem2)
|
||||
.unique_by(|win| WindowState::with(win, |state| state.id));
|
||||
.filter(|win| *win == win1 || *win == win2);
|
||||
|
||||
let (first, second) = (elems.next(), elems.next());
|
||||
|
||||
|
@ -196,33 +107,10 @@ impl<'a> Layout<'a, Window> for MasterStack<'a, Window> {
|
|||
std::mem::swap(first, second);
|
||||
}
|
||||
}
|
||||
|
||||
let wins = self
|
||||
.inner
|
||||
.iter()
|
||||
.map(|vec| {
|
||||
vec.iter()
|
||||
.enumerate()
|
||||
.map(|(i, win)| {
|
||||
compositor::with_states(win.toplevel().wl_surface(), |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData doesn't exist")
|
||||
.lock()
|
||||
.expect("Couldn't lock XdgToplevelSurfaceData");
|
||||
(i, lock.app_id.clone().unwrap_or("".to_string()))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tracing::debug!("windows are: {wins:?}");
|
||||
|
||||
swap_window_positions(space, elem1, elem2);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a, Window> for MasterStack<Window> {
|
||||
fn layout(&self, space: &Space<Window>, output: &Output) {
|
||||
let Some(master) = self.master() else {
|
||||
return;
|
||||
|
@ -232,27 +120,6 @@ impl<'a> Layout<'a, Window> for MasterStack<'a, Window> {
|
|||
tracing::error!("could not get output geometry");
|
||||
return;
|
||||
};
|
||||
let wins = self
|
||||
.inner
|
||||
.iter()
|
||||
.map(|vec| {
|
||||
vec.iter()
|
||||
.enumerate()
|
||||
.map(|(i, win)| {
|
||||
compositor::with_states(win.toplevel().wl_surface(), |states| {
|
||||
let lock = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.expect("XdgToplevelSurfaceData doesn't exist")
|
||||
.lock()
|
||||
.expect("Couldn't lock XdgToplevelSurfaceData");
|
||||
(i, lock.app_id.clone().unwrap_or("".to_string()))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tracing::debug!("windows are: {wins:?}");
|
||||
|
||||
if self.stack().count() == 0 {
|
||||
// one window
|
||||
|
@ -282,22 +149,33 @@ impl<'a> Layout<'a, Window> for MasterStack<'a, Window> {
|
|||
self.layout_stack(space, output);
|
||||
}
|
||||
}
|
||||
|
||||
/// Chain another tag's windows to this one to be layed out.
|
||||
fn chain_with(mut self, vec: &'a mut Vec<Window>) -> Self {
|
||||
self.inner.push(vec);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LayoutVec<S: SpaceElement> {
|
||||
/// Interpret this vec as a master-stack layout.
|
||||
fn as_master_stack(&mut self) -> MasterStack<S>;
|
||||
fn to_master_stack(&self, tags: Vec<TagId>) -> MasterStack<S>;
|
||||
// fn as_binary_tree(&mut self); TODO:
|
||||
}
|
||||
|
||||
impl<S: SpaceElement> LayoutVec<S> for Vec<S> {
|
||||
fn as_master_stack(&mut self) -> MasterStack<S> {
|
||||
MasterStack { inner: vec![self] }
|
||||
impl LayoutVec<Window> for Vec<Window> {
|
||||
fn to_master_stack(&self, tags: Vec<TagId>) -> MasterStack<Window> {
|
||||
MasterStack {
|
||||
inner: self
|
||||
.iter()
|
||||
.filter(|window| {
|
||||
WindowState::with(window, |state| {
|
||||
state.floating.is_tiled() && {
|
||||
for tag_id in state.tags.iter() {
|
||||
if tags.iter().any(|tag| tag == tag_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,21 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{cell::RefCell, collections::HashSet};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use smithay::output::Output;
|
||||
|
||||
use crate::tag::TagId;
|
||||
use crate::tag::Tag;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OutputState {
|
||||
pub tags: HashSet<TagId>,
|
||||
pub tags: Vec<Tag>,
|
||||
}
|
||||
|
||||
impl OutputState {
|
||||
pub fn focused_tags(&mut self) -> impl Iterator<Item = &mut Tag> {
|
||||
self.tags.iter_mut().filter(|tag| tag.active)
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputState {
|
||||
|
@ -24,10 +30,10 @@ impl OutputState {
|
|||
.user_data()
|
||||
.insert_if_missing(RefCell::<Self>::default);
|
||||
|
||||
let state = output
|
||||
let mut state = output
|
||||
.user_data()
|
||||
.get::<RefCell<Self>>()
|
||||
.expect("RefCell doesn't exist in data map (This should NEVER happen. If you see this, something oofed big-time.)");
|
||||
.expect("RefCell not in data map");
|
||||
|
||||
func(&mut state.borrow_mut())
|
||||
}
|
||||
|
|
108
src/state.rs
108
src/state.rs
|
@ -21,7 +21,7 @@ use crate::{
|
|||
focus::FocusState,
|
||||
layout::{Layout, LayoutVec},
|
||||
output::OutputState,
|
||||
tag::{Tag, TagState},
|
||||
tag::Tag,
|
||||
window::{window_state::WindowState, WindowProperties},
|
||||
};
|
||||
use calloop::futures::Scheduler;
|
||||
|
@ -88,7 +88,6 @@ pub struct State<B: Backend> {
|
|||
pub input_state: InputState,
|
||||
pub api_state: ApiState,
|
||||
pub focus_state: FocusState,
|
||||
pub tag_state: TagState,
|
||||
|
||||
pub popup_manager: PopupManager,
|
||||
|
||||
|
@ -152,7 +151,15 @@ impl<B: Backend> State<B> {
|
|||
.find(|&win| WindowState::with(win, |state| state.id == window_id))
|
||||
{
|
||||
WindowState::with(window, |state| {
|
||||
state.tags = vec![tag_id.clone()];
|
||||
OutputState::with(
|
||||
&self.focus_state.focused_output.as_ref().unwrap(),
|
||||
|op_state| {
|
||||
let tag = op_state.tags.iter().find(|tag| tag.name == tag_id);
|
||||
if let Some(tag) = tag {
|
||||
state.tags = vec![tag.id.clone()];
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -165,11 +172,19 @@ impl<B: Backend> State<B> {
|
|||
.find(|&win| WindowState::with(win, |state| state.id == window_id))
|
||||
{
|
||||
WindowState::with(window, |state| {
|
||||
if state.tags.contains(&tag_id) {
|
||||
state.tags.retain(|id| id != &tag_id);
|
||||
} else {
|
||||
state.tags.push(tag_id.clone());
|
||||
}
|
||||
OutputState::with(
|
||||
&self.focus_state.focused_output.as_ref().unwrap(),
|
||||
|op_state| {
|
||||
let tag = op_state.tags.iter().find(|tag| tag.name == tag_id);
|
||||
if let Some(tag) = tag {
|
||||
if state.tags.contains(&tag.id) {
|
||||
state.tags.retain(|id| id != &tag.id);
|
||||
} else {
|
||||
state.tags.push(tag.id.clone());
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
self.re_layout();
|
||||
|
@ -179,13 +194,8 @@ impl<B: Backend> State<B> {
|
|||
OutputState::with(
|
||||
self.focus_state.focused_output.as_ref().unwrap(), // TODO: handle error
|
||||
|state| {
|
||||
let should_remove = state.tags.get(&tag_id).is_some();
|
||||
if should_remove {
|
||||
state.tags.remove(&tag_id);
|
||||
tracing::debug!("toggled tag {tag_id:?} off");
|
||||
} else {
|
||||
state.tags.insert(tag_id.clone());
|
||||
tracing::debug!("toggled tag {tag_id:?} on");
|
||||
if let Some(tag) = state.tags.iter_mut().find(|tag| tag.name == tag_id) {
|
||||
tag.active = !tag.active;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -194,13 +204,33 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
Msg::SwitchToTag { tag_id } => {
|
||||
OutputState::with(self.focus_state.focused_output.as_ref().unwrap(), |state| {
|
||||
state.tags.clear();
|
||||
state.tags.insert(tag_id.clone());
|
||||
tracing::debug!("focused tags: {:?}", state.tags);
|
||||
if !state.tags.iter().any(|tag| tag.name == tag_id) {
|
||||
// TODO: notify error
|
||||
return;
|
||||
}
|
||||
for tag in state.tags.iter_mut() {
|
||||
tag.active = false;
|
||||
}
|
||||
|
||||
let Some(tag) = state.tags.iter_mut().find(|tag| tag.name == tag_id) else {
|
||||
unreachable!()
|
||||
};
|
||||
tag.active = true;
|
||||
|
||||
tracing::debug!(
|
||||
"focused tags: {:?}",
|
||||
state
|
||||
.tags
|
||||
.iter()
|
||||
.filter(|tag| tag.active)
|
||||
.map(|tag| &tag.name)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
});
|
||||
|
||||
self.re_layout();
|
||||
}
|
||||
// TODO: add output
|
||||
Msg::AddTags { tags } => {
|
||||
if let Some(output) = self
|
||||
.focus_state
|
||||
|
@ -208,15 +238,21 @@ impl<B: Backend> State<B> {
|
|||
.as_ref()
|
||||
.or_else(|| self.space.outputs().next())
|
||||
{
|
||||
self.tag_state.tags.extend(tags.into_iter().map(|tag| Tag {
|
||||
id: tag,
|
||||
windows: vec![],
|
||||
output: output.clone(),
|
||||
}));
|
||||
OutputState::with(output, |state| {
|
||||
state.tags.extend(
|
||||
tags.clone()
|
||||
.into_iter()
|
||||
.map(|name| Tag::new(name, output.clone())),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Msg::RemoveTags { tags } => {
|
||||
self.tag_state.tags.retain(|tag| !tags.contains(&tag.id));
|
||||
for output in self.space.outputs() {
|
||||
OutputState::with(output, |state| {
|
||||
state.tags.retain(|tag| !tags.contains(&tag.name));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Msg::Quit => {
|
||||
|
@ -475,8 +511,11 @@ impl<B: Backend> State<B> {
|
|||
OutputState::with(output, |state| {
|
||||
for window in self.space.elements().cloned().collect::<Vec<_>>() {
|
||||
let should_render = WindowState::with(&window, |win_state| {
|
||||
if win_state.floating.is_floating() {
|
||||
return true;
|
||||
}
|
||||
for tag_id in win_state.tags.iter() {
|
||||
if state.tags.get(tag_id).is_some() {
|
||||
if state.focused_tags().any(|tag| &tag.id == tag_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -487,21 +526,9 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut tags = self
|
||||
.tag_state
|
||||
.tags
|
||||
.iter_mut()
|
||||
.filter(|tg| state.tags.contains(&tg.id));
|
||||
|
||||
if let Some(first) = tags.next() {
|
||||
let mut layout = first.windows.as_master_stack();
|
||||
|
||||
for tg in tags {
|
||||
layout = layout.chain_with(&mut tg.windows);
|
||||
}
|
||||
|
||||
layout.layout(&self.space, output);
|
||||
}
|
||||
self.windows
|
||||
.to_master_stack(state.focused_tags().map(|tag| tag.id.clone()).collect())
|
||||
.layout(&self.space, output);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -644,7 +671,6 @@ impl<B: Backend> State<B> {
|
|||
input_state: InputState::new(),
|
||||
api_state: ApiState::new(),
|
||||
focus_state: FocusState::new(),
|
||||
tag_state: TagState::new(),
|
||||
|
||||
seat,
|
||||
|
||||
|
|
40
src/tag.rs
40
src/tag.rs
|
@ -4,26 +4,44 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use smithay::{desktop::Window, output::Output};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use smithay::output::Output;
|
||||
|
||||
static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TagId(String);
|
||||
pub struct TagId(u32);
|
||||
|
||||
impl TagId {
|
||||
fn next() -> Self {
|
||||
Self(TAG_ID_COUNTER.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tag {
|
||||
/// The internal id of this tag.
|
||||
pub id: TagId,
|
||||
pub windows: Vec<Window>,
|
||||
/// The name of this tag.
|
||||
pub name: String,
|
||||
/// The output that this tag should be on.
|
||||
pub output: Output,
|
||||
/// Whether this tag is active or not.
|
||||
pub active: bool,
|
||||
// TODO: layout
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TagState {
|
||||
pub tags: Vec<Tag>,
|
||||
}
|
||||
|
||||
impl TagState {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
impl Tag {
|
||||
pub fn new(name: String, output: Output) -> Self {
|
||||
Self {
|
||||
id: TagId::next(),
|
||||
name,
|
||||
output,
|
||||
active: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use smithay::{
|
|||
|
||||
use crate::{backend::Backend, state::State};
|
||||
|
||||
use self::window_state::{Float, WindowId, WindowState};
|
||||
use self::window_state::{CommitState, Float, WindowId, WindowState};
|
||||
|
||||
pub mod window_state;
|
||||
|
||||
|
@ -86,7 +86,12 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
|||
});
|
||||
|
||||
state.re_layout();
|
||||
state.space.raise_element(window, true);
|
||||
|
||||
// FIXME: every window gets mapped after this raise in `commit`, making this useless
|
||||
WindowState::with(window, |state| {
|
||||
state.needs_raise = CommitState::RequestReceived(window.toplevel().send_configure());
|
||||
});
|
||||
// state.space.raise_element(window, true);
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
|
|
@ -14,7 +14,7 @@ use smithay::{
|
|||
utils::{Logical, Point, Serial, Size},
|
||||
};
|
||||
|
||||
use crate::tag::{Tag, TagId, TagState};
|
||||
use crate::tag::TagId;
|
||||
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WindowId(u32);
|
||||
|
@ -37,15 +37,15 @@ pub struct WindowState {
|
|||
pub resize_state: WindowResizeState,
|
||||
/// What tags the window is currently on.
|
||||
pub tags: Vec<TagId>,
|
||||
|
||||
// FIXME: this is a bandaid to get floating working again, figure out an actual solution
|
||||
pub needs_raise: CommitState,
|
||||
}
|
||||
|
||||
/// Returns a vec of references to all the tags the window is on.
|
||||
pub fn tags<'a>(tag_state: &'a TagState, window: &Window) -> Vec<&'a Tag> {
|
||||
tag_state
|
||||
.tags
|
||||
.iter()
|
||||
.filter(|&tag| WindowState::with(window, |state| state.tags.contains(&tag.id)))
|
||||
.collect()
|
||||
pub enum CommitState {
|
||||
Idle,
|
||||
RequestReceived(Serial),
|
||||
Acked,
|
||||
}
|
||||
|
||||
/// The state of a window's resize operation.
|
||||
|
@ -142,6 +142,7 @@ impl Default for WindowState {
|
|||
floating: Float::Tiled(None),
|
||||
resize_state: WindowResizeState::Idle,
|
||||
tags: vec![],
|
||||
needs_raise: CommitState::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue