graphview: Do not crash when the position of a node not on the graph is requested

While the position of a node not on the graph should never be requested, this seems to occur sometimes,
so instead of panicking, we only log an error now, or ignore that node if it wasn't
important.
This commit is contained in:
Tom A. Wagner
2021-09-11 15:03:03 +02:00
parent da5da90352
commit 497da8b953

View File

@@ -47,27 +47,32 @@ mod imp {
drag_controller.connect_drag_begin( drag_controller.connect_drag_begin(
clone!(@strong drag_state => move |drag_controller, x, y| { clone!(@strong drag_state => move |drag_controller, x, y| {
let mut drag_state = drag_state.borrow_mut(); let mut drag_state = drag_state.borrow_mut();
let widget = drag_controller let widget = drag_controller
.widget() .widget()
.expect("drag-begin event has no widget") .expect("drag-begin event has no widget")
.dynamic_cast::<Self::Type>() .dynamic_cast::<Self::Type>()
.expect("drag-begin event is not on the GraphView"); .expect("drag-begin event is not on the GraphView");
// pick() should at least return the widget itself. // pick() should at least return the widget itself.
let target = widget.pick(x, y, gtk::PickFlags::DEFAULT).expect("drag-begin pick() did not return a widget"); let target = widget.pick(x, y, gtk::PickFlags::DEFAULT).expect("drag-begin pick() did not return a widget");
*drag_state = if target.ancestor(Port::static_type()).is_some() { *drag_state = if target.ancestor(Port::static_type()).is_some() {
// The user targeted a port, so the dragging should be handled by the Port // The user targeted a port, so the dragging should be handled by the Port
// component instead of here. // component instead of here.
None None
} else if let Some(target) = target.ancestor(Node::static_type()) { } else if let Some(target) = target.ancestor(Node::static_type()) {
// The user targeted a Node without targeting a specific Port. // The user targeted a Node without targeting a specific Port.
// Drag the Node around the screen. // Drag the Node around the screen.
let (x, y) = widget.get_node_position(&target); if let Some((x, y)) = widget.get_node_position(&target) {
Some((target, x, y)) Some((target, x, y))
} else { } else {
None error!("Failed to obtain position of dragged node, drag aborted.");
None
}
} else {
None
}
} }
})); ));
drag_controller.connect_drag_update( drag_controller.connect_drag_update(
clone!(@strong drag_state => move |drag_controller, x, y| { clone!(@strong drag_state => move |drag_controller, x, y| {
let widget = drag_controller let widget = drag_controller
@@ -259,16 +264,16 @@ impl GraphView {
.nodes .nodes
.borrow() .borrow()
.values() .values()
.map(|node| { .filter_map(|node| {
//Map nodes to locations // Map nodes to locations, discard nodes without location
self.get_node_position(&node.clone().upcast()) self.get_node_position(&node.clone().upcast())
}) })
.filter(|&(x2, _y)| { .filter(|(x2, _)| {
//Only look in our column // Only look for other nodes that have a similar x coordinate
(x - x2).abs() < 50.0 (x - x2).abs() < 50.0
}) })
.max_by(|y1, y2| { .max_by(|y1, y2| {
//Get max in column // Get max in column
y1.partial_cmp(y2).unwrap_or(Ordering::Equal) y1.partial_cmp(y2).unwrap_or(Ordering::Equal)
}) })
.map_or(20_f32, |(_x, y)| y + 100.0); .map_or(20_f32, |(_x, y)| y + 100.0);
@@ -333,7 +338,10 @@ impl GraphView {
self.queue_draw(); self.queue_draw();
} }
pub(super) fn get_node_position(&self, node: &gtk::Widget) -> (f32, f32) { /// Get the position of the specified node inside the graphview.
///
/// Returns `None` if the node is not in the graphview.
pub(super) fn get_node_position(&self, node: &gtk::Widget) -> Option<(f32, f32)> {
let layout_manager = self let layout_manager = self
.layout_manager() .layout_manager()
.expect("Failed to get layout manager") .expect("Failed to get layout manager")
@@ -341,12 +349,13 @@ impl GraphView {
.expect("Failed to cast to FixedLayout"); .expect("Failed to cast to FixedLayout");
let node = layout_manager let node = layout_manager
.layout_child(node) .layout_child(node)?
.expect("Could not get layout child")
.dynamic_cast::<gtk::FixedLayoutChild>() .dynamic_cast::<gtk::FixedLayoutChild>()
.expect("Could not cast to FixedLayoutChild"); .expect("Could not cast to FixedLayoutChild");
let transform = node.transform().unwrap_or_default(); let transform = node
transform.to_translate() .transform()
.expect("Failed to obtain transform from layout child");
Some(transform.to_translate())
} }
pub(super) fn move_node(&self, node: &gtk::Widget, x: f32, y: f32) { pub(super) fn move_node(&self, node: &gtk::Widget, x: f32, y: f32) {