Emit "link-toggled" signal when two ports widgets are connected

This commit is contained in:
Tom A. Wagner
2021-05-06 10:09:11 +02:00
parent 08283bb995
commit b5071c09a0

View File

@@ -1,10 +1,16 @@
use gtk::{glib, prelude::*, subclass::prelude::*}; use gtk::{
gdk,
glib::{self, subclass::Signal},
prelude::*,
subclass::prelude::*,
};
use log::warn;
use pipewire::spa::Direction; use pipewire::spa::Direction;
use crate::controller::MediaType; use crate::controller::MediaType;
mod imp { mod imp {
use once_cell::unsync::OnceCell; use once_cell::{sync::Lazy, unsync::OnceCell};
use pipewire::spa::Direction; use pipewire::spa::Direction;
use super::*; use super::*;
@@ -23,7 +29,22 @@ mod imp {
type ParentType = gtk::Button; type ParentType = gtk::Button;
} }
impl ObjectImpl for Port {} impl ObjectImpl for Port {
fn signals() -> &'static [Signal] {
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
vec![Signal::builder(
"port-toggled",
// Provide id of output port and input port to signal handler.
&[<u32>::static_type().into(), <u32>::static_type().into()],
// signal handler sends back nothing.
<()>::static_type().into(),
)
.build()]
});
SIGNALS.as_ref()
}
}
impl WidgetImpl for Port {} impl WidgetImpl for Port {}
impl ButtonImpl for Port {} impl ButtonImpl for Port {}
} }
@@ -46,6 +67,44 @@ impl Port {
res.set_child(Some(&gtk::Label::new(Some(name)))); res.set_child(Some(&gtk::Label::new(Some(name))));
// Add either a drag source or drop target controller depending on direction,
// they will be responsible for link creation by dragging an output port onto an input port.
//
// FIXME: The type used for dragging is simply a u32.
// This means that anything that provides a u32 could be dragged onto a input port,
// leading to that port trying to create a link to an invalid output port.
// We should use a newtype instead of a plain u32.
// Additionally, this does not protect against e.g. dropping an outgoing audio port on an ingoing video port.
match direction {
Direction::Input => {
let drop_target = gtk::DropTarget::new(u32::static_type(), gdk::DragAction::COPY);
let this = res.clone();
drop_target.connect_drop(move |drop_target, val, _, _| {
if let Some(source_id) = val.downcast_ref::<u32>() {
// Get the callback registered in the widget and call it
let source_id = source_id.get_some();
drop_target
.get_widget()
.expect("Drop target has no widget")
.emit_by_name("port-toggled", &[&source_id, &this.id()])
.expect("Failed to send signal");
} else {
warn!("Invalid type dropped on ingoing port");
}
true
});
res.add_controller(&drop_target);
}
Direction::Output => {
// The port will simply provide its pipewire id to the drag target.
let drag_src = gtk::DragSourceBuilder::new()
.content(&gdk::ContentProvider::new_for_value(&(id.to_value())))
.build();
res.add_controller(&drag_src);
}
}
// Color the port according to its media type. // Color the port according to its media type.
match media_type { match media_type {
Some(MediaType::Video) => res.add_css_class("video"), Some(MediaType::Video) => res.add_css_class("video"),
@@ -57,6 +116,11 @@ impl Port {
res res
} }
pub fn id(&self) -> u32 {
let private = imp::Port::from_instance(self);
private.id.get().copied().expect("Port id is not set")
}
pub fn direction(&self) -> &Direction { pub fn direction(&self) -> &Direction {
let private = imp::Port::from_instance(self); let private = imp::Port::from_instance(self);
private.direction.get().expect("Port direction is not set") private.direction.get().expect("Port direction is not set")