ui: Display node media name in graph view

This commit is contained in:
Denis Drakhnia
2023-10-11 11:17:30 +03:00
committed by Tom Wagner
parent 5d4931b418
commit 96c079d29e
6 changed files with 133 additions and 24 deletions

View File

@@ -59,6 +59,7 @@ mod imp {
move |msg| {
match msg {
PipewireMessage::NodeAdded { id, name, node_type } => imp.add_node(id, name.as_str(), node_type),
PipewireMessage::NodeNameChanged { id, name, media_name } => imp.node_name_changed(id, &name, &media_name),
PipewireMessage::PortAdded { id, node_id, name, direction } => imp.add_port(id, name.as_str(), node_id, direction),
PipewireMessage::PortFormatChanged { id, media_type } => imp.port_media_type_changed(id, media_type),
PipewireMessage::LinkAdded {
@@ -95,6 +96,23 @@ mod imp {
self.obj().graph().add_node(node, node_type);
}
/// Update a node tooltip to the view.
fn node_name_changed(&self, id: u32, node_name: &str, media_name: &str) {
let items = self.items.borrow();
let Some(node) = items.get(&id) else {
log::warn!("Node (id: {id}) for changed name not found in graph manager");
return;
};
let Some(node) = node.dynamic_cast_ref::<graph::Node>() else {
log::warn!("Graph Manager item under node (id: {id}) is not a node");
return;
};
node.set_node_name(node_name);
node.set_media_name(media_name);
}
/// Remove the node with the specified id from the view.
fn remove_node(&self, id: u32) {
log::info!("Removing node from graph: id {}", id);

View File

@@ -39,6 +39,11 @@ pub enum PipewireMessage {
name: String,
node_type: Option<NodeType>,
},
NodeNameChanged {
id: u32,
name: String,
media_name: String,
},
PortAdded {
id: u32,
node_id: u32,

View File

@@ -26,7 +26,9 @@ use std::{
use adw::glib::{self, clone};
use log::{debug, error, info, warn};
use pipewire::{
keys,
link::{Link, LinkChangeMask, LinkInfo, LinkListener, LinkState},
node::{Node, NodeInfo, NodeListener},
port::{Port, PortChangeMask, PortInfo, PortListener},
prelude::*,
properties,
@@ -43,6 +45,10 @@ use crate::{GtkMessage, MediaType, NodeType, PipewireMessage};
use state::{Item, State};
enum ProxyItem {
Node {
_proxy: Node,
_listener: NodeListener,
},
Port {
proxy: Port,
_listener: PortListener,
@@ -149,7 +155,7 @@ pub(super) fn thread_main(
.add_listener_local()
.global(clone!(@strong gtk_sender, @weak registry, @strong proxies, @strong state =>
move |global| match global.type_ {
ObjectType::Node => handle_node(global, &gtk_sender, &state),
ObjectType::Node => handle_node(global, &gtk_sender, &registry, &proxies, &state),
ObjectType::Port => handle_port(global, &gtk_sender, &registry, &proxies, &state),
ObjectType::Link => handle_link(global, &gtk_sender, &registry, &proxies, &state),
_ => {
@@ -180,10 +186,21 @@ pub(super) fn thread_main(
}
}
/// Get the nicest possible name for the node, using a fallback chain of possible name attributes
fn get_node_name(props: &ForeignDict) -> &str {
props
.get(&keys::NODE_DESCRIPTION)
.or_else(|| props.get(&keys::NODE_NICK))
.or_else(|| props.get(&keys::NODE_NAME))
.unwrap_or_default()
}
/// Handle a new node being added
fn handle_node(
node: &GlobalObject<ForeignDict>,
sender: &glib::Sender<PipewireMessage>,
registry: &Rc<Registry>,
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
state: &Rc<RefCell<State>>,
) {
let props = node
@@ -191,15 +208,7 @@ fn handle_node(
.as_ref()
.expect("Node object is missing properties");
// Get the nicest possible name for the node, using a fallback chain of possible name attributes.
let name = String::from(
props
.get("node.description")
.or_else(|| props.get("node.nick"))
.or_else(|| props.get("node.name"))
.unwrap_or_default(),
);
let name = get_node_name(props).to_string();
let media_class = |class: &str| {
if class.contains("Sink") || class.contains("Input") {
Some(NodeType::Input)
@@ -230,6 +239,50 @@ fn handle_node(
node_type,
})
.expect("Failed to send message");
let proxy: Node = registry.bind(node).expect("Failed to bind to node proxy");
let listener = proxy
.add_listener_local()
.info(clone!(@strong sender, @strong proxies => move |info| {
handle_node_info(info, &sender, &proxies);
}))
.register();
proxies.borrow_mut().insert(
node.id,
ProxyItem::Node {
_proxy: proxy,
_listener: listener,
},
);
}
fn handle_node_info(
info: &NodeInfo,
sender: &glib::Sender<PipewireMessage>,
proxies: &Rc<RefCell<HashMap<u32, ProxyItem>>>,
) {
debug!("Received node info: {:?}", info);
let id = info.id();
let proxies = proxies.borrow();
let Some(ProxyItem::Node { .. }) = proxies.get(&id) else {
error!("Received info on unknown node with id {id}");
return;
};
let props = info.props().expect("NodeInfo object is missing properties");
if let Some(media_name) = props.get(&keys::MEDIA_NAME) {
let name = get_node_name(props).to_string();
sender
.send(PipewireMessage::NodeNameChanged {
id,
name,
media_name: media_name.to_string(),
})
.expect("Failed to send message");
}
}
/// Handle a new port being added

View File

@@ -41,7 +41,7 @@ node {
background-color: @headerbar_bg_color;
}
node label.heading {
node .node-title {
padding: 4px 7px;
}

View File

@@ -34,15 +34,26 @@ mod imp {
#[property(get, set, construct_only)]
pub(super) pipewire_id: Cell<u32>,
#[property(
name = "name", type = String,
get = |this: &Self| this.label.text().to_string(),
name = "node-name", type = String,
get = |this: &Self| this.node_name.text().to_string(),
set = |this: &Self, val| {
this.label.set_text(val);
this.label.set_tooltip_text(Some(val));
this.node_name.set_text(val);
this.node_name.set_tooltip_text(Some(val));
}
)]
#[template_child]
pub(super) label: TemplateChild<gtk::Label>,
pub(super) node_name: TemplateChild<gtk::Label>,
#[property(
name = "media-name", type = String,
get = |this: &Self| this.media_name.text().to_string(),
set = |this: &Self, val| {
this.media_name.set_text(val);
this.media_name.set_tooltip_text(Some(val));
this.media_name.set_visible(!val.is_empty());
}
)]
#[template_child]
pub(super) media_name: TemplateChild<gtk::Label>,
#[template_child]
pub(super) separator: TemplateChild<gtk::Separator>,
#[template_child]
@@ -75,7 +86,7 @@ mod imp {
self.parent_constructed();
// Display a grab cursor when the mouse is over the label so the user knows the node can be dragged.
self.label
self.node_name
.set_cursor(gtk::gdk::Cursor::from_name("grab", None).as_ref());
}
@@ -141,7 +152,7 @@ glib::wrapper! {
impl Node {
pub fn new(name: &str, pipewire_id: u32) -> Self {
glib::Object::builder()
.property("name", name)
.property("node-name", name)
.property("pipewire-id", pipewire_id)
.build()
}

View File

@@ -9,14 +9,36 @@
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label">
<object class="GtkBox">
<style>
<class name="heading"></class>
<class name="node-title"></class>
</style>
<property name="wrap">true</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
<property name="lines">2</property>
<property name="max-width-chars">20</property>
<property name="orientation">vertical</property>
<property name="spacing">1</property>
<child>
<object class="GtkLabel" id="node_name">
<style>
<class name="heading"></class>
</style>
<property name="wrap">true</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
<property name="lines">2</property>
<property name="max-width-chars">20</property>
</object>
</child>
<child>
<object class="GtkLabel" id="media_name">
<style>
<class name="dim-label"></class>
<class name="caption"></class>
</style>
<property name="visible">false</property>
<property name="wrap">true</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
<property name="lines">2</property>
<property name="max-width-chars">20</property>
</object>
</child>
</object>
</child>
<child>