From b5071c09a065e5e21cf9748f99581bd044233ce6 Mon Sep 17 00:00:00 2001 From: "Tom A. Wagner" Date: Thu, 6 May 2021 10:09:11 +0200 Subject: [PATCH] Emit "link-toggled" signal when two ports widgets are connected --- src/view/port.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/view/port.rs b/src/view/port.rs index 07a3b51..3cc3c34 100644 --- a/src/view/port.rs +++ b/src/view/port.rs @@ -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 crate::controller::MediaType; mod imp { - use once_cell::unsync::OnceCell; + use once_cell::{sync::Lazy, unsync::OnceCell}; use pipewire::spa::Direction; use super::*; @@ -23,7 +29,22 @@ mod imp { type ParentType = gtk::Button; } - impl ObjectImpl for Port {} + impl ObjectImpl for Port { + fn signals() -> &'static [Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![Signal::builder( + "port-toggled", + // Provide id of output port and input port to signal handler. + &[::static_type().into(), ::static_type().into()], + // signal handler sends back nothing. + <()>::static_type().into(), + ) + .build()] + }); + + SIGNALS.as_ref() + } + } impl WidgetImpl for Port {} impl ButtonImpl for Port {} } @@ -46,6 +67,44 @@ impl Port { res.set_child(Some(>k::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::() { + // 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. match media_type { Some(MediaType::Video) => res.add_css_class("video"), @@ -57,6 +116,11 @@ impl Port { 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 { let private = imp::Port::from_instance(self); private.direction.get().expect("Port direction is not set")