1
0
Fork 0
forked from wry/wry

Implement scratchpad window toggling

This commit is contained in:
atagen 2026-05-31 17:23:56 +10:00
parent 4c10713073
commit 290b290fdf
17 changed files with 515 additions and 3 deletions

View file

@ -114,9 +114,11 @@ use {
tree::{
ContainerNode, ContainerSplit, Direction, DisplayNode, FindTreeUsecase, FloatNode,
FoundNode, LatchListener, Node, NodeId, NodeIds, NodeVisitorBase, OutputNode,
PlaceholderNode, TearingMode, TileState, ToplevelData, ToplevelIdentifier,
ToplevelNode, ToplevelNodeBase, Transform, VrrMode, WorkspaceDisplayOrder,
WorkspaceNode, WorkspaceNodeId, WsMoveConfig, generic_node_visitor, move_ws_to_output,
PlaceholderNode, ScratchpadToplevelState, TearingMode, TileState, ToplevelData,
ToplevelIdentifier, ToplevelNode, ToplevelNodeBase, Transform, VrrMode,
WorkspaceDisplayOrder, WorkspaceNode, WorkspaceNodeId, WsMoveConfig,
generic_node_visitor, move_ws_to_output, toplevel_hide_for_scratchpad,
toplevel_restore_from_scratchpad, toplevel_set_workspace,
},
udmabuf::UdmabufHolder,
utils::{
@ -412,6 +414,7 @@ pub struct State {
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
pub virtual_outputs: VirtualOutputs,
pub clean_logs_older_than: Cell<Option<SystemTime>>,
pub scratchpads: RefCell<AHashMap<String, Vec<Rc<ScratchpadEntry>>>>,
}
// impl Drop for State {
@ -459,6 +462,28 @@ pub struct IdleState {
pub in_grace_period: Cell<bool>,
}
pub struct ScratchpadEntry {
node: Weak<dyn ToplevelNode>,
identifier: ToplevelIdentifier,
hidden: Cell<bool>,
restore: RefCell<Option<ScratchpadToplevelState>>,
}
impl ScratchpadEntry {
fn alive(&self) -> bool {
self.node().is_some()
}
fn node(&self) -> Option<Rc<dyn ToplevelNode>> {
let node = self.node.upgrade()?;
if node.tl_data().identifier.get() == self.identifier {
Some(node)
} else {
None
}
}
}
impl IdleState {
pub fn set_timeout(&self, state: &State, timeout: Duration) {
self.timeout.set(timeout);
@ -1023,6 +1048,121 @@ impl State {
float
}
pub fn send_to_scratchpad(self: &Rc<Self>, name: &str, node: Rc<dyn ToplevelNode>) {
if node.node_is_placeholder() {
return;
}
let identifier = node.tl_data().identifier.get();
let entry = Rc::new(ScratchpadEntry {
node: Rc::downgrade(&node),
identifier,
hidden: Cell::new(false),
restore: Default::default(),
});
let Some(restore) = toplevel_hide_for_scratchpad(node) else {
return;
};
*entry.restore.borrow_mut() = Some(restore);
entry.hidden.set(true);
{
let mut scratchpads = self.scratchpads.borrow_mut();
for entries in scratchpads.values_mut() {
entries.retain(|entry| entry.alive() && entry.identifier != identifier);
}
scratchpads
.entry(name.to_string())
.or_default()
.push(entry.clone());
}
self.tree_changed();
}
pub fn toggle_scratchpad(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, name: &str) {
let entry = {
let mut scratchpads = self.scratchpads.borrow_mut();
let Some(entries) = scratchpads.get_mut(name) else {
return;
};
entries.retain(|entry| entry.alive());
entries
.iter()
.rev()
.find(|entry| {
!entry.hidden.get() && entry.node().is_some_and(|node| node.node_visible())
})
.cloned()
.or_else(|| {
entries
.iter()
.rev()
.find(|entry| {
entry.hidden.get()
|| entry.node().is_some_and(|node| !node.node_visible())
})
.cloned()
})
};
let Some(entry) = entry else {
return;
};
if entry.hidden.get() {
self.show_scratchpad_entry(seat, &entry);
} else if entry.node().is_some_and(|node| !node.node_visible()) {
self.move_scratchpad_entry_to_current_workspace(seat, &entry);
} else {
self.hide_scratchpad_entry(&entry);
}
}
fn hide_scratchpad_entry(self: &Rc<Self>, entry: &Rc<ScratchpadEntry>) {
let Some(node) = entry.node() else {
return;
};
if let Some(restore) = toplevel_hide_for_scratchpad(node) {
*entry.restore.borrow_mut() = Some(restore);
entry.hidden.set(true);
self.tree_changed();
}
}
fn show_scratchpad_entry(
self: &Rc<Self>,
seat: &Rc<WlSeatGlobal>,
entry: &Rc<ScratchpadEntry>,
) {
if !entry.hidden.get() {
return;
}
let Some(node) = entry.node() else {
return;
};
let restore = entry.restore.borrow();
let Some(restore) = restore.as_ref() else {
return;
};
let ws = seat.get_fallback_output().ensure_workspace();
toplevel_restore_from_scratchpad(self, node.clone(), &ws, restore);
entry.hidden.set(false);
node.node_do_focus(seat, Direction::Unspecified);
seat.maybe_schedule_warp_mouse_to_focus();
self.tree_changed();
}
fn move_scratchpad_entry_to_current_workspace(
self: &Rc<Self>,
seat: &Rc<WlSeatGlobal>,
entry: &Rc<ScratchpadEntry>,
) {
let Some(node) = entry.node() else {
return;
};
let ws = seat.get_fallback_output().ensure_workspace();
toplevel_set_workspace(self, node.clone(), &ws);
node.node_do_focus(seat, Direction::Unspecified);
seat.maybe_schedule_warp_mouse_to_focus();
self.tree_changed();
}
fn focus_after_map(&self, node: Rc<dyn ToplevelNode>, seat: Option<&Rc<WlSeatGlobal>>) {
if !node.node_visible() {
return;
@ -1298,6 +1438,7 @@ impl State {
self.node_at_tree.borrow_mut().clear();
self.position_hint_requests.clear();
self.pending_warp_mouse_to_focus.clear();
self.scratchpads.borrow_mut().clear();
self.head_managers.clear();
self.head_managers_async.clear();
self.const_40hz_latch.clear();