mirror of
https://gitlab.freedesktop.org/pipewire/helvum
synced 2026-03-15 03:26:10 +08:00
Group nodes into columns by major type
This commit is contained in:
@@ -11,7 +11,7 @@ use pipewire::{channel::Sender, spa::Direction};
|
||||
|
||||
use crate::{
|
||||
view::{self},
|
||||
GtkMessage, MediaType, PipewireLink, PipewireMessage,
|
||||
GtkMessage, MediaType, NodeType, PipewireLink, PipewireMessage,
|
||||
};
|
||||
|
||||
static STYLE: &str = include_str!("style.css");
|
||||
@@ -108,7 +108,7 @@ impl Application {
|
||||
@weak app => @default-return Continue(true),
|
||||
move |msg| {
|
||||
match msg {
|
||||
PipewireMessage::NodeAdded{ id, name } => app.add_node(id, name.as_str()),
|
||||
PipewireMessage::NodeAdded{ id, name, node_type } => app.add_node(id, name.as_str(), node_type),
|
||||
PipewireMessage::PortAdded{ id, node_id, name, direction, media_type } => app.add_port(id, name.as_str(), node_id, direction, media_type),
|
||||
PipewireMessage::LinkAdded{ id, node_from, port_from, node_to, port_to, active} => app.add_link(id, node_from, port_from, node_to, port_to, active),
|
||||
PipewireMessage::LinkStateChanged { id, active } => app.link_state_changed(id, active), // TODO
|
||||
@@ -125,12 +125,14 @@ impl Application {
|
||||
}
|
||||
|
||||
/// Add a new node to the view.
|
||||
fn add_node(&self, id: u32, name: &str) {
|
||||
fn add_node(&self, id: u32, name: &str, node_type: Option<NodeType>) {
|
||||
info!("Adding node to graph: id {}", id);
|
||||
|
||||
imp::Application::from_instance(self)
|
||||
.graphview
|
||||
.add_node(id, view::Node::new(name));
|
||||
imp::Application::from_instance(self).graphview.add_node(
|
||||
id,
|
||||
view::Node::new(name),
|
||||
node_type,
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a new port to the view.
|
||||
|
||||
@@ -23,6 +23,7 @@ enum PipewireMessage {
|
||||
NodeAdded {
|
||||
id: u32,
|
||||
name: String,
|
||||
node_type: Option<NodeType>,
|
||||
},
|
||||
PortAdded {
|
||||
id: u32,
|
||||
@@ -55,6 +56,12 @@ enum PipewireMessage {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NodeType {
|
||||
Input,
|
||||
Output,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum MediaType {
|
||||
Audio,
|
||||
|
||||
@@ -14,7 +14,7 @@ use pipewire::{
|
||||
Context, Core, MainLoop,
|
||||
};
|
||||
|
||||
use crate::{GtkMessage, MediaType, PipewireMessage};
|
||||
use crate::{GtkMessage, MediaType, NodeType, PipewireMessage};
|
||||
use state::{Item, State};
|
||||
|
||||
enum ProxyItem {
|
||||
@@ -112,6 +112,27 @@ fn handle_node(
|
||||
}
|
||||
});
|
||||
|
||||
let media_class = |class: &str| {
|
||||
if class.contains("Sink") || class.contains("Input") {
|
||||
Some(NodeType::Input)
|
||||
} else if class.contains("Source") || class.contains("Output") {
|
||||
Some(NodeType::Output)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let node_type = props
|
||||
.get("media.category")
|
||||
.and_then(|class| {
|
||||
if class.contains("Duplex") {
|
||||
None
|
||||
} else {
|
||||
props.get("media.class").and_then(media_class)
|
||||
}
|
||||
})
|
||||
.or_else(|| props.get("media.class").and_then(media_class));
|
||||
|
||||
state.borrow_mut().insert(
|
||||
node.id,
|
||||
Item::Node {
|
||||
@@ -121,7 +142,11 @@ fn handle_node(
|
||||
);
|
||||
|
||||
sender
|
||||
.send(PipewireMessage::NodeAdded { id: node.id, name })
|
||||
.send(PipewireMessage::NodeAdded {
|
||||
id: node.id,
|
||||
name,
|
||||
node_type,
|
||||
})
|
||||
.expect("Failed to send message");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ use gtk::{
|
||||
};
|
||||
use log::{error, warn};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::{cmp::Ordering, collections::HashMap};
|
||||
|
||||
use crate::NodeType;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
@@ -239,14 +241,37 @@ impl GraphView {
|
||||
glib::Object::new(&[]).expect("Failed to create GraphView")
|
||||
}
|
||||
|
||||
pub fn add_node(&self, id: u32, node: Node) {
|
||||
pub fn add_node(&self, id: u32, node: Node, node_type: Option<NodeType>) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
node.set_parent(self);
|
||||
|
||||
// Place widgets in colums of 4, growing down, then right.
|
||||
// TODO: Make a better positioning algorithm.
|
||||
let x = ((private.nodes.borrow().len() / 4) as f32 * 400.0) + 20.0; // This relies on integer division rounding down.
|
||||
let y = (private.nodes.borrow().len() as f32 % 4.0 * 100.0) + 20.0;
|
||||
// Place widgets in colums of 3, growing down
|
||||
let x = if let Some(node_type) = node_type {
|
||||
match node_type {
|
||||
NodeType::Output => 20.0,
|
||||
NodeType::Input => 820.0,
|
||||
}
|
||||
} else {
|
||||
420.0
|
||||
};
|
||||
|
||||
let y = private
|
||||
.nodes
|
||||
.borrow()
|
||||
.values()
|
||||
.map(|node| {
|
||||
//Map nodes to locations
|
||||
self.get_node_position(&node.clone().upcast())
|
||||
})
|
||||
.filter(|&(x2, _y)| {
|
||||
//Only look in our column
|
||||
(x - x2).abs() < 50.0
|
||||
})
|
||||
.max_by(|y1, y2| {
|
||||
//Get max in column
|
||||
y1.partial_cmp(y2).unwrap_or(Ordering::Equal)
|
||||
})
|
||||
.map_or(20_f32, |(_x, y)| y + 100.0);
|
||||
|
||||
self.move_node(&node.clone().upcast(), x, y);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user