Add initial project.

This commit is contained in:
Tom A. Wagner
2020-11-11 12:44:17 +01:00
commit 264ccf0982
7 changed files with 891 additions and 0 deletions

61
src/main.rs Normal file
View File

@@ -0,0 +1,61 @@
mod view;
use glib::clone;
use gio::prelude::*;
use gtk::prelude::*;
use std::rc::Rc;
pub struct PipewireLink {
pub node_from: u32,
pub port_from: u32,
pub node_to: u32,
pub port_to: u32
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
gtk::init()?;
let mut graphview = view::GraphView::new();
// For UI Testing purposes
let mut node = view::PipewireNode::new("Test Node");
node.add_ingoing_port(10, gtk::Button::with_label("Ingoing Port"));
node.add_outgoing_port(11, gtk::Button::with_label("Outgoing Port"));
node.add_outgoing_port(12, gtk::Button::with_label("Outgoing Port 2"));
let mut node2 = view::PipewireNode::new("Test Node 2");
node2.add_ingoing_port(13, gtk::Button::with_label("Ingoing Port"));
node2.add_outgoing_port(14, gtk::Button::with_label("Outgoing Port"));
node2.add_outgoing_port(15, gtk::Button::with_label("Outgoing Port 2"));
graphview.add_node(0, node);
graphview.add_node(1, node2);
graphview.add_link(2, PipewireLink {
node_from: 0,
port_from: 12,
node_to: 1,
port_to: 13
});
// End UI Testing
let graphview = Rc::new(graphview);
let app = gtk::Application::new(
Some("org.freedesktop.pipewire.graphui"),
gio::ApplicationFlags::FLAGS_NONE,
)
.expect("Application creation failed");
app.connect_activate(clone!(@strong graphview => move |app| {
let window = gtk::ApplicationWindow::new(app);
window.set_default_size(800, 600);
window.set_title("Pipewire Graph Editor");
window.add(&graphview.widget);
window.show_all();
}));
app.run(&std::env::args().collect::<Vec<_>>());
Ok(())
}

96
src/view/graph_view.rs Normal file
View File

@@ -0,0 +1,96 @@
use super::PipewireNode;
use cairo::Context;
use glib::clone;
use gtk::{prelude::*, LayoutExt};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
pub struct GraphView {
pub(crate) widget: gtk::Layout,
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
}
impl GraphView {
pub fn new() -> Self {
let result = Self {
widget: gtk::Layout::new::<gtk::Adjustment, gtk::Adjustment>(None, None),
nodes: Rc::new(RefCell::new(HashMap::new())),
links: Rc::new(RefCell::new(HashMap::new())),
};
result.widget.connect_draw(clone!(
@weak result.nodes as nodes, @weak result.links as links => @default-panic,
move |_, cr| {
draw(nodes, links, cr);
Inhibit(false)
}));
result
}
pub fn add_node(&mut self, id: u32, node: PipewireNode) {
// TODO: Find a free position to put the widget at.
self.widget.put(
&node.widget,
(self.nodes.borrow().len() % 4 * 400) as i32,
(self.nodes.borrow().len() / 4 * 100) as i32,
);
self.nodes.borrow_mut().insert(id, node);
}
/// Add a link to the graph.
///
/// `add_link` takes three arguments: `link_id` is the id of the link as assigned by the pipewire server,
/// `from` and `to` are the id's of the ingoing and outgoing port, respectively.
pub fn add_link(&mut self, link_id: u32, link: crate::PipewireLink) {
self.links.borrow_mut().insert(link_id, link);
self.widget.queue_draw();
}
}
fn draw(
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
links: Rc<RefCell<HashMap<u32, crate::PipewireLink>>>,
cr: &Context,
) {
cr.set_line_width(2.0);
cr.set_source_rgb(255.0, 255.0, 255.0);
cr.paint();
cr.set_source_rgb(0.0, 0.0, 0.0);
for link in links.borrow().values() {
let (from_alloc, to_alloc) = get_allocs(nodes.clone(), link);
cr.move_to(
(from_alloc.x + from_alloc.width).into(),
(from_alloc.y + (from_alloc.height / 2)).into()
);
cr.line_to(
to_alloc.x.into(),
(to_alloc.y + (to_alloc.height / 2)).into()
);
cr.stroke();
}
}
fn get_allocs(
nodes: Rc<RefCell<HashMap<u32, PipewireNode>>>,
link: &crate::PipewireLink,
) -> (gtk::Allocation, gtk::Allocation) {
let from_alloc = &nodes
.borrow()
.get(&link.node_from)
.unwrap()
.get_outgoing_port(link.port_from)
.unwrap()
.get_allocation();
let to_alloc = &nodes
.borrow()
.get(&link.node_to)
.unwrap()
.get_ingoing_port(link.port_to)
.unwrap()
.get_allocation();
(from_alloc.to_owned(), to_alloc.to_owned())
}

5
src/view/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod graph_view;
mod pipewire_node;
pub use pipewire_node::PipewireNode;
pub use graph_view::GraphView;

43
src/view/pipewire_node.rs Normal file
View File

@@ -0,0 +1,43 @@
use gtk::GridExt;
use std::collections::HashMap;
pub struct PipewireNode {
pub(super) widget: gtk::Grid,
_label: gtk::Label,
ingoing_ports: HashMap<u32, gtk::Button>,
outgoing_ports: HashMap<u32, gtk::Button>,
}
impl PipewireNode {
pub fn new(name: &str) -> Self {
let widget = gtk::Grid::new();
let label = gtk::Label::new(Some(name));
widget.attach(&label, 0, 0, 2, 1);
Self {
widget,
_label: label,
ingoing_ports: HashMap::new(),
outgoing_ports: HashMap::new(),
}
}
pub fn add_ingoing_port(&mut self, id: u32, port: gtk::Button) {
self.widget.attach(&port, 0, (self.ingoing_ports.len() + 1) as i32, 1, 1);
self.ingoing_ports.insert(id, port);
}
pub fn add_outgoing_port(&mut self, id: u32, port: gtk::Button) {
self.widget.attach(&port, 1, (self.outgoing_ports.len() + 1) as i32, 1, 1);
self.outgoing_ports.insert(id, port);
}
pub fn get_ingoing_port(&self, id: u32) -> Option<&gtk::Button> {
self.ingoing_ports.get(&id)
}
pub fn get_outgoing_port(&self, id: u32) -> Option<&gtk::Button> {
self.outgoing_ports.get(&id)
}
}