mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-06-23 18:10:36 +00:00
graphview: add documentation and refactor the code
Add documentation to public method. Publish method which are public Use SelectionExt trait. Rename add_node_with_port to create_node_with_port
This commit is contained in:
parent
ade3f14902
commit
6c31bc3912
|
@ -25,10 +25,12 @@ use xml::writer::EmitterConfig;
|
|||
use xml::writer::XmlEvent as XMLWEvent;
|
||||
|
||||
use super::{
|
||||
link::Link,
|
||||
link::*,
|
||||
node::{Node, NodeType},
|
||||
port::{Port, PortDirection, PortPresence},
|
||||
selection::SelectionExt,
|
||||
};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
@ -329,7 +331,7 @@ mod imp {
|
|||
}
|
||||
|
||||
impl GraphView {
|
||||
/// Get coordinates for the drawn link to start at and to end at.
|
||||
/// Retrieves coordinates for the drawn link to start at and to end at.
|
||||
///
|
||||
/// # Returns
|
||||
/// `Some((from_x, from_y, to_x, to_y))` if all objects the links refers to exist as widgets.
|
||||
|
@ -379,6 +381,10 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl GraphView {
|
||||
/// Create a new graphview
|
||||
///
|
||||
/// # Returns
|
||||
/// Graphview object
|
||||
pub fn new() -> Self {
|
||||
// Load CSS from the STYLE variable.
|
||||
let provider = gtk::CssProvider::new();
|
||||
|
@ -390,17 +396,17 @@ impl GraphView {
|
|||
);
|
||||
glib::Object::new(&[]).expect("Failed to create GraphView")
|
||||
}
|
||||
|
||||
/// Set graphview id
|
||||
///
|
||||
pub fn set_id(&self, id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private.id.set(id)
|
||||
}
|
||||
|
||||
fn graph_updated(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
self.emit_by_name::<()>("graph-updated", &[&private.id.get()]);
|
||||
}
|
||||
|
||||
pub fn add_node_with_port(&self, id: u32, node: Node, input: u32, output: u32) {
|
||||
/// Add node to the graphview without port
|
||||
///
|
||||
pub fn add_node(&self, node: Node) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
node.set_parent(self);
|
||||
|
||||
|
@ -436,7 +442,22 @@ impl GraphView {
|
|||
|
||||
self.move_node(&node.clone().upcast(), x, y);
|
||||
|
||||
private.nodes.borrow_mut().insert(id, node);
|
||||
private.nodes.borrow_mut().insert(node.id(), node);
|
||||
self.graph_updated();
|
||||
}
|
||||
|
||||
/// Create a new node and add it to the graphview with input/output port number.
|
||||
///
|
||||
pub fn create_node_with_port(
|
||||
&self,
|
||||
id: u32,
|
||||
name: &str,
|
||||
node_type: NodeType,
|
||||
input: u32,
|
||||
output: u32,
|
||||
) {
|
||||
let node = Node::new(id, name, node_type);
|
||||
self.add_node(node);
|
||||
let _i = 0;
|
||||
for _i in 0..input {
|
||||
let port_id = self.next_port_id();
|
||||
|
@ -448,7 +469,6 @@ impl GraphView {
|
|||
PortPresence::Always,
|
||||
);
|
||||
}
|
||||
|
||||
let _i = 0;
|
||||
for _i in 0..output {
|
||||
let port_id = self.next_port_id();
|
||||
|
@ -460,13 +480,10 @@ impl GraphView {
|
|||
PortPresence::Always,
|
||||
);
|
||||
}
|
||||
self.graph_updated();
|
||||
}
|
||||
|
||||
pub fn add_node(&self, id: u32, node: Node) {
|
||||
self.add_node_with_port(id, node, 0, 0);
|
||||
}
|
||||
|
||||
/// Remove node from the graphview
|
||||
///
|
||||
pub fn remove_node(&self, id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut nodes = private.nodes.borrow_mut();
|
||||
|
@ -481,6 +498,10 @@ impl GraphView {
|
|||
}
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
/// Select all nodes according to the NodeType
|
||||
///
|
||||
/// Returns a vector of nodes
|
||||
pub fn all_nodes(&self, node_type: NodeType) -> Vec<Node> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let nodes = private.nodes.borrow();
|
||||
|
@ -493,6 +514,7 @@ impl GraphView {
|
|||
.collect();
|
||||
nodes_list
|
||||
}
|
||||
|
||||
/// Get the node with the specified node id inside the graphview.
|
||||
///
|
||||
/// Returns `None` if the node is not in the graphview.
|
||||
|
@ -501,6 +523,8 @@ impl GraphView {
|
|||
private.nodes.borrow().get(&id).cloned()
|
||||
}
|
||||
|
||||
/// Remove all the nodes from the graphview
|
||||
///
|
||||
pub fn remove_all_nodes(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let nodes_list = self.all_nodes(NodeType::All);
|
||||
|
@ -513,6 +537,9 @@ impl GraphView {
|
|||
self.queue_draw();
|
||||
}
|
||||
|
||||
/// Check if the node is linked
|
||||
///
|
||||
/// Returns Some(link id) or `None` if the node is not linked.
|
||||
pub fn node_is_linked(&self, node_id: u32) -> Option<u32> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for (key, link) in private.links.borrow().iter() {
|
||||
|
@ -523,15 +550,8 @@ impl GraphView {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn unselect_nodes(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for node in private.nodes.borrow_mut().values() {
|
||||
node.set_selected(false);
|
||||
node.unselect_all_ports();
|
||||
}
|
||||
}
|
||||
// Port
|
||||
|
||||
// Port related methods
|
||||
/// Add the port with id from node with id.
|
||||
///
|
||||
pub fn add_port(
|
||||
|
@ -557,6 +577,9 @@ impl GraphView {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the port with id from node with id can be removed.
|
||||
///
|
||||
/// Return true if the port presence is not always.
|
||||
pub fn can_remove_port(&self, node_id: u32, port_id: u32) -> bool {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let nodes = private.nodes.borrow();
|
||||
|
@ -579,6 +602,7 @@ impl GraphView {
|
|||
node.remove_port(port_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the port is linked
|
||||
///
|
||||
/// Returns Some(link id) or `None` if the port is not linked.
|
||||
|
@ -592,23 +616,19 @@ impl GraphView {
|
|||
None
|
||||
}
|
||||
|
||||
// Link related methods
|
||||
pub fn all_links(&self) -> Vec<Link> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let links = private.links.borrow();
|
||||
let links_list: Vec<_> = links.iter().map(|(_, link)| link.clone()).collect();
|
||||
links_list
|
||||
}
|
||||
// Link
|
||||
|
||||
/// Add a link to the graphView
|
||||
///
|
||||
pub fn add_link(&self, link: Link) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if !self.link_exists(&link) {
|
||||
private.links.borrow_mut().insert(link.id, link);
|
||||
self.graph_updated();
|
||||
self.queue_draw();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the link state with ink id and link state (boolean)
|
||||
///
|
||||
pub fn set_link_state(&self, link_id: u32, active: bool) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if let Some(link) = private.links.borrow_mut().get_mut(&link_id) {
|
||||
|
@ -619,43 +639,6 @@ impl GraphView {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove_link(&self, id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut links = private.links.borrow_mut();
|
||||
links.remove(&id);
|
||||
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
pub fn unselect_links(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for link in private.links.borrow_mut().values() {
|
||||
link.set_selected(false);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point_on_link(&self, point: &graphene::Point) -> Option<Link> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
self.unselect_all();
|
||||
for link in private.links.borrow_mut().values() {
|
||||
if let Some((from_x, from_y, to_x, to_y)) = private.link_coordinates(link) {
|
||||
let quad = graphene::Quad::new(
|
||||
&graphene::Point::new(from_x as f32, from_y as f32 - link.thickness as f32),
|
||||
&graphene::Point::new(to_x as f32, to_y as f32 - link.thickness as f32),
|
||||
&graphene::Point::new(to_x as f32, to_y as f32 + link.thickness as f32),
|
||||
&graphene::Point::new(from_x as f32, from_y as f32 + link.thickness as f32),
|
||||
);
|
||||
if quad.contains(point) {
|
||||
link.toggle_selected();
|
||||
self.queue_draw();
|
||||
return Some(link.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.queue_draw();
|
||||
None
|
||||
}
|
||||
|
||||
/// Get the position of the specified node inside the graphview.
|
||||
///
|
||||
/// Returns `None` if the node is not in the graphview.
|
||||
|
@ -676,72 +659,8 @@ impl GraphView {
|
|||
Some(transform.to_translate())
|
||||
}
|
||||
|
||||
pub(super) fn move_node(&self, widget: >k::Widget, x: f32, y: f32) {
|
||||
let node = widget
|
||||
.clone()
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to convert to Node");
|
||||
node.set_position(x, y);
|
||||
let layout_manager = self
|
||||
.layout_manager()
|
||||
.expect("Failed to get layout manager")
|
||||
.dynamic_cast::<gtk::FixedLayout>()
|
||||
.expect("Failed to cast to FixedLayout");
|
||||
|
||||
let transform = gsk::Transform::new()
|
||||
// Nodes should not be able to be dragged out of the view, so we use `max(coordinate, 0.0)` to prevent that.
|
||||
.translate(&graphene::Point::new(f32::max(x, 0.0), f32::max(y, 0.0)))
|
||||
.unwrap();
|
||||
|
||||
layout_manager
|
||||
.layout_child(widget)
|
||||
.dynamic_cast::<gtk::FixedLayoutChild>()
|
||||
.expect("Could not cast to FixedLayoutChild")
|
||||
.set_transform(&transform);
|
||||
|
||||
// FIXME: If links become proper widgets,
|
||||
// we don't need to redraw the full graph everytime.
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
pub(super) fn link_exists(&self, new_link: &Link) -> bool {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
|
||||
for link in private.links.borrow().values() {
|
||||
if (new_link.port_from == link.port_from && new_link.port_to == link.port_to)
|
||||
|| (new_link.port_to == link.port_from && new_link.port_from == link.port_to)
|
||||
{
|
||||
warn!("link already existing");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn ports_compatible(&self, to_port: &Port) -> bool {
|
||||
let current_port = self.selected_port().to_owned();
|
||||
if let Some(from_port) = current_port {
|
||||
let from_node = from_port
|
||||
.ancestor(Node::static_type())
|
||||
.expect("Unable to reach parent")
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to cast to Node");
|
||||
let to_node = to_port
|
||||
.ancestor(Node::static_type())
|
||||
.expect("Unable to reach parent")
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to cast to Node");
|
||||
let res = from_port.id() != to_port.id()
|
||||
&& from_port.direction() != to_port.direction()
|
||||
&& from_node.id() != to_node.id();
|
||||
if !res {
|
||||
warn!("Unable add the following link");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Retrieves the next node unique id.
|
||||
///
|
||||
pub fn next_node_id(&self) -> u32 {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private
|
||||
|
@ -750,13 +669,8 @@ impl GraphView {
|
|||
private.current_node_id.get()
|
||||
}
|
||||
|
||||
pub fn update_current_node_id(&self, node_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if node_id > private.current_node_id.get() {
|
||||
private.current_node_id.set(node_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the next port unique id.
|
||||
///
|
||||
pub fn next_port_id(&self) -> u32 {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private
|
||||
|
@ -765,41 +679,11 @@ impl GraphView {
|
|||
private.current_port_id.get()
|
||||
}
|
||||
|
||||
pub fn update_current_port_id(&self, port_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if port_id > private.current_port_id.get() {
|
||||
private.current_port_id.set(port_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn next_link_id(&self) -> u32 {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private
|
||||
.current_link_id
|
||||
.set(private.current_link_id.get() + 1);
|
||||
private.current_link_id.get()
|
||||
}
|
||||
|
||||
pub fn update_current_link_id(&self, link_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if link_id > private.current_link_id.get() {
|
||||
private.current_link_id.set(link_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_selected_port(&self, port: Option<&Port>) {
|
||||
self.unselect_all();
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
*private.port_selected.borrow_mut() = port.cloned();
|
||||
}
|
||||
|
||||
fn selected_port(&self) -> RefMut<Option<Port>> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private.port_selected.borrow_mut()
|
||||
}
|
||||
|
||||
/// Retrieves the node/port id connected to the input port id
|
||||
///
|
||||
pub fn port_connected_to(&self, port_id: u32) -> Option<(u32, u32)> {
|
||||
for link in self.all_links() {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for (_id, link) in private.links.borrow().iter() {
|
||||
if port_id == link.port_from {
|
||||
return Some((link.port_to, link.node_to));
|
||||
}
|
||||
|
@ -807,12 +691,8 @@ impl GraphView {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn unselect_all(&self) {
|
||||
self.unselect_nodes();
|
||||
self.unselect_links();
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
/// Delete the selected element (link, node, port)
|
||||
///
|
||||
pub fn delete_selected(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut link_id = None;
|
||||
|
@ -833,9 +713,12 @@ impl GraphView {
|
|||
if let Some(id) = node_id {
|
||||
self.remove_node(id);
|
||||
}
|
||||
self.queue_draw();
|
||||
|
||||
self.graph_updated();
|
||||
}
|
||||
|
||||
/// Render the graph in a file with XML format
|
||||
///
|
||||
pub fn render_xml(&self, filename: &str) -> anyhow::Result<()> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut file = File::create(filename).unwrap();
|
||||
|
@ -879,7 +762,7 @@ impl GraphView {
|
|||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
//Get the link and write it.
|
||||
for link in self.all_links() {
|
||||
for (_id, link) in private.links.borrow().iter() {
|
||||
writer.write(
|
||||
XMLWEvent::start_element("Link")
|
||||
.attr("id", &link.id.to_string())
|
||||
|
@ -895,6 +778,8 @@ impl GraphView {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Load the graph from a file with XML format
|
||||
///
|
||||
pub fn load_xml(&self, filename: &str) -> anyhow::Result<()> {
|
||||
let file = File::open(filename)?;
|
||||
let file = BufReader::new(file);
|
||||
|
@ -1025,7 +910,7 @@ impl GraphView {
|
|||
if let Some(node) = current_node {
|
||||
let id = node.id();
|
||||
let position = node.position();
|
||||
self.add_node(id, node);
|
||||
self.add_node(node);
|
||||
if let Some(node) = self.node(id) {
|
||||
self.move_node(&node.upcast(), position.0, position.1);
|
||||
}
|
||||
|
@ -1068,6 +953,171 @@ impl GraphView {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//Private
|
||||
|
||||
fn remove_link(&self, id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut links = private.links.borrow_mut();
|
||||
links.remove(&id);
|
||||
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
fn update_current_link_id(&self, link_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if link_id > private.current_link_id.get() {
|
||||
private.current_link_id.set(link_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn link_exists(&self, new_link: &Link) -> bool {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
|
||||
for link in private.links.borrow().values() {
|
||||
if (new_link.port_from == link.port_from && new_link.port_to == link.port_to)
|
||||
|| (new_link.port_to == link.port_from && new_link.port_from == link.port_to)
|
||||
{
|
||||
warn!("link already existing");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn move_node(&self, widget: >k::Widget, x: f32, y: f32) {
|
||||
let node = widget
|
||||
.clone()
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to convert to Node");
|
||||
node.set_position(x, y);
|
||||
let layout_manager = self
|
||||
.layout_manager()
|
||||
.expect("Failed to get layout manager")
|
||||
.dynamic_cast::<gtk::FixedLayout>()
|
||||
.expect("Failed to cast to FixedLayout");
|
||||
|
||||
let transform = gsk::Transform::new()
|
||||
// Nodes should not be able to be dragged out of the view, so we use `max(coordinate, 0.0)` to prevent that.
|
||||
.translate(&graphene::Point::new(f32::max(x, 0.0), f32::max(y, 0.0)))
|
||||
.unwrap();
|
||||
|
||||
layout_manager
|
||||
.layout_child(widget)
|
||||
.dynamic_cast::<gtk::FixedLayoutChild>()
|
||||
.expect("Could not cast to FixedLayoutChild")
|
||||
.set_transform(&transform);
|
||||
|
||||
// FIXME: If links become proper widgets,
|
||||
// we don't need to redraw the full graph everytime.
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
fn unselect_nodes(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for node in private.nodes.borrow_mut().values() {
|
||||
node.set_selected(false);
|
||||
node.unselect_all_ports();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_current_node_id(&self, node_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if node_id > private.current_node_id.get() {
|
||||
private.current_node_id.set(node_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn unselect_links(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
for link in private.links.borrow_mut().values() {
|
||||
link.set_selected(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn unselect_all(&self) {
|
||||
self.unselect_nodes();
|
||||
self.unselect_links();
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
fn point_on_link(&self, point: &graphene::Point) -> Option<Link> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
self.unselect_all();
|
||||
for link in private.links.borrow_mut().values() {
|
||||
if let Some((from_x, from_y, to_x, to_y)) = private.link_coordinates(link) {
|
||||
let quad = graphene::Quad::new(
|
||||
&graphene::Point::new(from_x as f32, from_y as f32 - link.thickness as f32),
|
||||
&graphene::Point::new(to_x as f32, to_y as f32 - link.thickness as f32),
|
||||
&graphene::Point::new(to_x as f32, to_y as f32 + link.thickness as f32),
|
||||
&graphene::Point::new(from_x as f32, from_y as f32 + link.thickness as f32),
|
||||
);
|
||||
if quad.contains(point) {
|
||||
link.toggle_selected();
|
||||
self.queue_draw();
|
||||
return Some(link.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.queue_draw();
|
||||
None
|
||||
}
|
||||
|
||||
fn graph_updated(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
self.queue_draw();
|
||||
self.emit_by_name::<()>("graph-updated", &[&private.id.get()]);
|
||||
}
|
||||
|
||||
fn next_link_id(&self) -> u32 {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private
|
||||
.current_link_id
|
||||
.set(private.current_link_id.get() + 1);
|
||||
private.current_link_id.get()
|
||||
}
|
||||
|
||||
fn set_selected_port(&self, port: Option<&Port>) {
|
||||
self.unselect_all();
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
*private.port_selected.borrow_mut() = port.cloned();
|
||||
}
|
||||
|
||||
fn selected_port(&self) -> RefMut<Option<Port>> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private.port_selected.borrow_mut()
|
||||
}
|
||||
|
||||
fn ports_compatible(&self, to_port: &Port) -> bool {
|
||||
let current_port = self.selected_port().to_owned();
|
||||
if let Some(from_port) = current_port {
|
||||
let from_node = from_port
|
||||
.ancestor(Node::static_type())
|
||||
.expect("Unable to reach parent")
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to cast to Node");
|
||||
let to_node = to_port
|
||||
.ancestor(Node::static_type())
|
||||
.expect("Unable to reach parent")
|
||||
.dynamic_cast::<Node>()
|
||||
.expect("Unable to cast to Node");
|
||||
let res = from_port.id() != to_port.id()
|
||||
&& from_port.direction() != to_port.direction()
|
||||
&& from_node.id() != to_node.id();
|
||||
if !res {
|
||||
warn!("Unable add the following link");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn update_current_port_id(&self, port_id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
if port_id > private.current_port_id.get() {
|
||||
private.current_port_id.set(port_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GraphView {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use super::SelectionExt;
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -32,8 +33,22 @@ pub struct Link {
|
|||
pub thickness: u32,
|
||||
}
|
||||
|
||||
impl Link {
|
||||
pub fn new(
|
||||
pub trait LinkExt {
|
||||
/// Create a new link
|
||||
///
|
||||
fn new(
|
||||
id: u32,
|
||||
node_from: u32,
|
||||
node_to: u32,
|
||||
port_from: u32,
|
||||
port_to: u32,
|
||||
active: bool,
|
||||
selected: bool,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl LinkExt for Link {
|
||||
fn new(
|
||||
id: u32,
|
||||
node_from: u32,
|
||||
node_to: u32,
|
||||
|
@ -53,16 +68,17 @@ impl Link {
|
|||
thickness: 4,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_selected(&self) {
|
||||
}
|
||||
impl SelectionExt for Link {
|
||||
fn toggle_selected(&self) {
|
||||
self.set_selected(!self.selected.get());
|
||||
}
|
||||
|
||||
pub fn set_selected(&self, selected: bool) {
|
||||
fn set_selected(&self, selected: bool) {
|
||||
self.selected.set(selected);
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> bool {
|
||||
fn selected(&self) -> bool {
|
||||
self.selected.get()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ mod graphview;
|
|||
mod link;
|
||||
mod node;
|
||||
mod port;
|
||||
mod selection;
|
||||
|
||||
pub use graphview::GraphView;
|
||||
pub use link::Link;
|
||||
|
@ -9,3 +10,4 @@ pub use node::Node;
|
|||
pub use node::NodeType;
|
||||
pub use port::Port;
|
||||
pub use port::{PortDirection, PortPresence};
|
||||
pub use selection::SelectionExt;
|
||||
|
|
|
@ -23,6 +23,7 @@ use gtk::subclass::prelude::*;
|
|||
use log::trace;
|
||||
|
||||
use super::Port;
|
||||
use super::SelectionExt;
|
||||
use super::{PortDirection, PortPresence};
|
||||
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
|
@ -171,6 +172,8 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl Node {
|
||||
/// Create a new node
|
||||
///
|
||||
pub fn new(id: u32, name: &str, node_type: NodeType) -> Self {
|
||||
let res: Self = glib::Object::new(&[]).expect("Failed to create Node");
|
||||
let private = imp::Node::from_instance(&res);
|
||||
|
@ -184,32 +187,8 @@ impl Node {
|
|||
res
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
self_.name.set_text(name);
|
||||
}
|
||||
|
||||
fn set_description(&self, description: &str) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
self_.description.set_text(description);
|
||||
trace!("Node description is {}", description);
|
||||
}
|
||||
pub fn hidden_property(&self, name: &str) -> bool {
|
||||
name.starts_with('_')
|
||||
}
|
||||
|
||||
fn update_description(&self) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
let mut description = String::from("");
|
||||
for (name, value) in self_.properties.borrow().iter() {
|
||||
if !self.hidden_property(name) {
|
||||
description.push_str(&format!("{}:{}", name, value));
|
||||
description.push('\n');
|
||||
}
|
||||
}
|
||||
self.set_description(&description);
|
||||
}
|
||||
|
||||
/// Add a new port to the node
|
||||
///
|
||||
pub fn add_port(
|
||||
&mut self,
|
||||
id: u32,
|
||||
|
@ -234,11 +213,15 @@ impl Node {
|
|||
private.ports.borrow_mut().insert(id, port);
|
||||
}
|
||||
|
||||
/// Retrieves all ports as an hashmap
|
||||
///
|
||||
pub fn ports(&self) -> Ref<HashMap<u32, Port>> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.ports.borrow()
|
||||
}
|
||||
|
||||
/// Retrieves all ports with given direction
|
||||
///
|
||||
pub fn all_ports(&self, direction: PortDirection) -> Vec<Port> {
|
||||
let ports_list: Vec<_> = self
|
||||
.ports()
|
||||
|
@ -249,11 +232,15 @@ impl Node {
|
|||
ports_list
|
||||
}
|
||||
|
||||
/// Retrieves the port with id
|
||||
///
|
||||
pub fn port(&self, id: &u32) -> Option<super::port::Port> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.ports.borrow().get(id).cloned()
|
||||
}
|
||||
|
||||
/// Check if we can remove a port dependending on PortPrensence attribute
|
||||
///
|
||||
pub fn can_remove_port(&self, id: u32) -> bool {
|
||||
let private = imp::Node::from_instance(self);
|
||||
if let Some(port) = private.ports.borrow().get(&id) {
|
||||
|
@ -264,6 +251,8 @@ impl Node {
|
|||
false
|
||||
}
|
||||
|
||||
/// Removes a port id from the given node
|
||||
///
|
||||
pub fn remove_port(&self, id: u32) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
if let Some(port) = private.ports.borrow_mut().remove(&id) {
|
||||
|
@ -276,16 +265,22 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Retrieves the node id
|
||||
///
|
||||
pub fn id(&self) -> u32 {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.id.get().copied().expect("Node id is not set")
|
||||
}
|
||||
|
||||
/// Retrieves the node name
|
||||
///
|
||||
pub fn name(&self) -> String {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.name.text().to_string()
|
||||
}
|
||||
|
||||
/// Retrieves the unique name composed with the node name and its id
|
||||
///
|
||||
pub fn unique_name(&self) -> String {
|
||||
let private = imp::Node::from_instance(self);
|
||||
let mut unique_name = private.name.text().to_string();
|
||||
|
@ -293,11 +288,21 @@ impl Node {
|
|||
unique_name
|
||||
}
|
||||
|
||||
/// Retrieves the NodeType
|
||||
///
|
||||
pub fn node_type(&self) -> Option<&NodeType> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.node_type.get()
|
||||
}
|
||||
|
||||
/// Check if the name is an hidden property
|
||||
///
|
||||
pub fn hidden_property(&self, name: &str) -> bool {
|
||||
name.starts_with('_')
|
||||
}
|
||||
|
||||
/// Add a node property with a name and a value.
|
||||
///
|
||||
pub fn add_property(&self, name: String, value: String) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
trace!("property name={} updated with value={}", name, value);
|
||||
|
@ -305,17 +310,23 @@ impl Node {
|
|||
self.update_description();
|
||||
}
|
||||
|
||||
/// Update the node property.
|
||||
///
|
||||
pub fn update_properties(&self, new_node_properties: &HashMap<String, String>) {
|
||||
for (key, value) in new_node_properties {
|
||||
self.add_property(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves node properties.
|
||||
///
|
||||
pub fn properties(&self) -> Ref<HashMap<String, String>> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.properties.borrow()
|
||||
}
|
||||
|
||||
/// Retrieves node property with the name.
|
||||
///
|
||||
pub fn property(&self, name: &str) -> Option<String> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
if let Some(property) = private.properties.borrow().get(name) {
|
||||
|
@ -324,11 +335,61 @@ impl Node {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn toggle_selected(&self) {
|
||||
/// Unselect all the ports of the given node.
|
||||
///
|
||||
pub fn unselect_all_ports(&self) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
for port in private.ports.borrow_mut().values() {
|
||||
port.set_selected(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set coordinates for the drawn node.
|
||||
///
|
||||
pub fn set_position(&self, x: f32, y: f32) {
|
||||
imp::Node::from_instance(self).position.set((x, y));
|
||||
}
|
||||
|
||||
/// Get coordinates for the drawn node.
|
||||
///
|
||||
/// # Returns
|
||||
/// `(x, y)`
|
||||
pub fn position(&self) -> (f32, f32) {
|
||||
imp::Node::from_instance(self).position.get()
|
||||
}
|
||||
|
||||
//Private
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
self_.name.set_text(name);
|
||||
}
|
||||
|
||||
fn set_description(&self, description: &str) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
self_.description.set_text(description);
|
||||
trace!("Node description is {}", description);
|
||||
}
|
||||
|
||||
fn update_description(&self) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
let mut description = String::from("");
|
||||
for (name, value) in self_.properties.borrow().iter() {
|
||||
if !self.hidden_property(name) {
|
||||
description.push_str(&format!("{}:{}", name, value));
|
||||
description.push('\n');
|
||||
}
|
||||
}
|
||||
self.set_description(&description);
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionExt for Node {
|
||||
fn toggle_selected(&self) {
|
||||
self.set_selected(!self.selected());
|
||||
}
|
||||
|
||||
pub fn set_selected(&self, selected: bool) {
|
||||
fn set_selected(&self, selected: bool) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.selected.set(selected);
|
||||
if selected {
|
||||
|
@ -338,27 +399,8 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> bool {
|
||||
fn selected(&self) -> bool {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.selected.get()
|
||||
}
|
||||
|
||||
pub fn unselect_all_ports(&self) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
for port in private.ports.borrow_mut().values() {
|
||||
port.set_selected(false);
|
||||
}
|
||||
}
|
||||
/// Set coordinates for the drawn node.
|
||||
///
|
||||
pub fn set_position(&self, x: f32, y: f32) {
|
||||
imp::Node::from_instance(self).position.set((x, y));
|
||||
}
|
||||
/// Get coordinates for the drawn node.
|
||||
///
|
||||
/// # Returns
|
||||
/// `(x, y)`
|
||||
pub fn position(&self) -> (f32, f32) {
|
||||
imp::Node::from_instance(self).position.get()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ use gtk::{
|
|||
use std::cell::Cell;
|
||||
use std::{borrow::Borrow, fmt};
|
||||
|
||||
use super::SelectionExt;
|
||||
|
||||
#[derive(Debug, Clone, PartialOrd, PartialEq, Copy)]
|
||||
pub enum PortDirection {
|
||||
Input,
|
||||
|
@ -120,6 +122,8 @@ glib::wrapper! {
|
|||
}
|
||||
|
||||
impl Port {
|
||||
/// Create a new port
|
||||
///
|
||||
pub fn new(id: u32, name: &str, direction: PortDirection, presence: PortPresence) -> Self {
|
||||
// Create the widget and initialize needed fields
|
||||
let port: Self = glib::Object::new(&[]).expect("Failed to create Port");
|
||||
|
@ -156,32 +160,42 @@ impl Port {
|
|||
port
|
||||
}
|
||||
|
||||
/// Retrieves the port id
|
||||
///
|
||||
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) -> PortDirection {
|
||||
let private = imp::Port::from_instance(self);
|
||||
*private.direction.get().expect("Port direction is not set")
|
||||
}
|
||||
|
||||
pub fn presence(&self) -> PortPresence {
|
||||
let private = imp::Port::from_instance(self);
|
||||
*private.presence.get().expect("Port presence is not set")
|
||||
}
|
||||
|
||||
/// Retrieves the port name
|
||||
///
|
||||
pub fn name(&self) -> String {
|
||||
let private = imp::Port::from_instance(self);
|
||||
let label = private.label.borrow().get().unwrap();
|
||||
label.text().to_string()
|
||||
}
|
||||
|
||||
pub fn toggle_selected(&self) {
|
||||
/// Retrieves the port direction
|
||||
///
|
||||
pub fn direction(&self) -> PortDirection {
|
||||
let private = imp::Port::from_instance(self);
|
||||
*private.direction.get().expect("Port direction is not set")
|
||||
}
|
||||
|
||||
/// Retrieves the port presence
|
||||
///
|
||||
pub fn presence(&self) -> PortPresence {
|
||||
let private = imp::Port::from_instance(self);
|
||||
*private.presence.get().expect("Port presence is not set")
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionExt for Port {
|
||||
fn toggle_selected(&self) {
|
||||
self.set_selected(!self.selected());
|
||||
}
|
||||
|
||||
pub fn set_selected(&self, selected: bool) {
|
||||
fn set_selected(&self, selected: bool) {
|
||||
let private = imp::Port::from_instance(self);
|
||||
private.selected.set(selected);
|
||||
if selected {
|
||||
|
@ -191,7 +205,7 @@ impl Port {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> bool {
|
||||
fn selected(&self) -> bool {
|
||||
let private = imp::Port::from_instance(self);
|
||||
private.selected.get()
|
||||
}
|
||||
|
|
32
src/graphmanager/selection.rs
Normal file
32
src/graphmanager/selection.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// selection.rs
|
||||
//
|
||||
// Copyright 2022 Stéphane Cerveau <scerveau@collabora.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub trait SelectionExt {
|
||||
/// Toggle selected
|
||||
///
|
||||
fn toggle_selected(&self);
|
||||
|
||||
/// Set selection to selected state
|
||||
///
|
||||
fn set_selected(&self, selected: bool);
|
||||
|
||||
/// Retrieve selection state
|
||||
///
|
||||
fn selected(&self) -> bool;
|
||||
}
|
Loading…
Reference in a new issue