This commit is contained in:
2026-09-09 11:30:58 +02:00
parent 78631065e2
commit 1367b03b97
8 changed files with 315 additions and 35 deletions
+72 -10
View File
@@ -99,32 +99,87 @@ fn live_sockets() -> Vec<PathBuf> {
found
}
/// Every view in the tree, in tree order (the same traversal the jq filter did,
/// so the grid keeps the ordering the muscle memory expects).
pub fn windows(conn: &mut Connection) -> Result<Vec<Target>, swayipc::Error> {
/// How to order windows in the grid.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Order {
/// Most-Recently-Used (MRU) focus order: current window first, then previous, etc.
#[default]
Mru,
/// Traversal of sway's layout tree (workspace by workspace).
Tree,
}
impl Order {
pub fn parse(s: &str) -> Result<Self, String> {
match s.trim() {
"mru" => Ok(Order::Mru),
"tree" => Ok(Order::Tree),
other => Err(format!("{other:?} is not mru or tree")),
}
}
}
/// Views in the tree, either in MRU focus order or tree layout order.
pub fn windows(conn: &mut Connection, order: Order) -> Result<Vec<Target>, swayipc::Error> {
let mut out = Vec::new();
collect(&conn.get_tree()?, &mut out);
let tree = conn.get_tree()?;
match order {
Order::Mru => {
collect_mru(&tree, &mut out);
// Ensure the currently focused window is at index 0
if let Some(pos) = out.iter().position(|t| t.focused) {
if pos > 0 {
let focused = out.remove(pos);
out.insert(0, focused);
}
}
}
Order::Tree => collect_tree(&tree, &mut out),
}
Ok(out)
}
fn collect(node: &Node, out: &mut Vec<Target>) {
fn collect_target(node: &Node) -> Option<Target> {
let is_con = matches!(node.node_type, NodeType::Con | NodeType::FloatingCon);
let class = node
.window_properties
.as_ref()
.and_then(|p| p.class.clone());
if is_con && (node.app_id.is_some() || class.is_some()) {
// A view with no identifier can't be captured, but it still belongs in
// the list: it gets a tile with no thumbnail.
out.push(Target::window(
Some(Target::window(
node.id,
node.foreign_toplevel_identifier.clone().unwrap_or_default(),
node.app_id.clone().or(class).unwrap_or_default(),
node.name.clone().unwrap_or_default(),
));
node.focused,
))
} else {
None
}
}
fn collect_tree(node: &Node, out: &mut Vec<Target>) {
if let Some(target) = collect_target(node) {
out.push(target);
}
for child in node.nodes.iter().chain(node.floating_nodes.iter()) {
collect(child, out);
collect_tree(child, out);
}
}
fn collect_mru(node: &Node, out: &mut Vec<Target>) {
if let Some(target) = collect_target(node) {
out.push(target);
}
let mut children: Vec<&Node> = node.nodes.iter().chain(node.floating_nodes.iter()).collect();
children.sort_by_key(|child| {
node.focus
.iter()
.position(|&id| id == child.id)
.unwrap_or(usize::MAX)
});
for child in children {
collect_mru(child, out);
}
}
@@ -184,4 +239,11 @@ mod tests {
assert_eq!(socket_pid("sway-ipc.1000..sock"), None);
assert_eq!(socket_pid("sway-ipc.1000.notapid.sock"), None);
}
#[test]
fn order_parses() {
assert_eq!(super::Order::parse("mru"), Ok(super::Order::Mru));
assert_eq!(super::Order::parse("tree"), Ok(super::Order::Tree));
assert!(super::Order::parse("invalid").is_err());
}
}