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:
Stéphane Cerveau 2022-01-18 18:02:18 +01:00
parent ade3f14902
commit 6c31bc3912
6 changed files with 408 additions and 252 deletions

View file

@ -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: &gtk::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: &gtk::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 {

View file

@ -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()
}
}

View file

@ -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;

View file

@ -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()
}
}

View file

@ -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()
}

View 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;
}