diff --git a/src/graphmanager/graphview.rs b/src/graphmanager/graphview.rs index 6943d3f..df97ba6 100644 --- a/src/graphmanager/graphview.rs +++ b/src/graphmanager/graphview.rs @@ -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 { 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 { 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 { - 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 { - 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::() - .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::() - .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::() - .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::() - .expect("Unable to cast to Node"); - let to_node = to_port - .ancestor(Node::static_type()) - .expect("Unable to reach parent") - .dynamic_cast::() - .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> { - 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::() + .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::() + .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::() + .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 { + 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> { + 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::() + .expect("Unable to cast to Node"); + let to_node = to_port + .ancestor(Node::static_type()) + .expect("Unable to reach parent") + .dynamic_cast::() + .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 { diff --git a/src/graphmanager/link.rs b/src/graphmanager/link.rs index b58645a..3ec9d5a 100644 --- a/src/graphmanager/link.rs +++ b/src/graphmanager/link.rs @@ -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() } } diff --git a/src/graphmanager/mod.rs b/src/graphmanager/mod.rs index c352fbb..f9604d4 100644 --- a/src/graphmanager/mod.rs +++ b/src/graphmanager/mod.rs @@ -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; diff --git a/src/graphmanager/node.rs b/src/graphmanager/node.rs index 1505b11..8548fe8 100644 --- a/src/graphmanager/node.rs +++ b/src/graphmanager/node.rs @@ -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> { let private = imp::Node::from_instance(self); private.ports.borrow() } + /// Retrieves all ports with given direction + /// pub fn all_ports(&self, direction: PortDirection) -> Vec { 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 { 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) { for (key, value) in new_node_properties { self.add_property(key.clone(), value.clone()); } } + /// Retrieves node properties. + /// pub fn properties(&self) -> Ref> { let private = imp::Node::from_instance(self); private.properties.borrow() } + /// Retrieves node property with the name. + /// pub fn property(&self, name: &str) -> Option { 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() - } } diff --git a/src/graphmanager/port.rs b/src/graphmanager/port.rs index ad10e50..fb1e71b 100644 --- a/src/graphmanager/port.rs +++ b/src/graphmanager/port.rs @@ -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() } diff --git a/src/graphmanager/selection.rs b/src/graphmanager/selection.rs new file mode 100644 index 0000000..85f6b4c --- /dev/null +++ b/src/graphmanager/selection.rs @@ -0,0 +1,32 @@ +// selection.rs +// +// Copyright 2022 Stéphane Cerveau +// +// 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 . +// +// 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; +}