From 03ac1322afb4460b6255203386aef12ef901422d Mon Sep 17 00:00:00 2001 From: starlord Date: Thu, 27 Apr 2023 12:35:40 +0400 Subject: [PATCH] Node click api (#17) * Node click api * 0.0.21 --- Cargo.lock | 6 ++--- Cargo.toml | 2 +- examples/basic/Cargo.toml | 2 +- examples/interactive/Cargo.toml | 2 +- examples/interactive/src/main.rs | 38 +++++++++++++++++--------------- src/changes.rs | 35 ++++++++--------------------- src/elements.rs | 4 +++- src/graph_view.rs | 25 ++++++++++++++++----- src/settings.rs | 19 +++++----------- 9 files changed, 64 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8023931..09678e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,7 +339,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "basic" -version = "0.0.20" +version = "0.0.21" dependencies = [ "eframe", "egui", @@ -738,7 +738,7 @@ dependencies = [ [[package]] name = "egui_graphs" -version = "0.0.20" +version = "0.0.21" dependencies = [ "egui", ] @@ -1130,7 +1130,7 @@ dependencies = [ [[package]] name = "interactive" -version = "0.0.20" +version = "0.0.21" dependencies = [ "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index c4fa52f..7ff23a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egui_graphs" -version = "0.0.20" +version = "0.0.21" authors = ["Dmitrii Samsonov "] license = "MIT" homepage = "https://github.com/blitzarx1/egui_graphs" diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index 9381b6a..01bd7f6 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "basic" -version = "0.0.20" +version = "0.0.21" authors = ["Dmitrii Samsonov "] license = "MIT" edition = "2021" diff --git a/examples/interactive/Cargo.toml b/examples/interactive/Cargo.toml index 5e08fdb..89658ea 100644 --- a/examples/interactive/Cargo.toml +++ b/examples/interactive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interactive" -version = "0.0.20" +version = "0.0.21" authors = ["Dmitrii Samsonov "] license = "MIT" edition = "2021" diff --git a/examples/interactive/src/main.rs b/examples/interactive/src/main.rs index 48eec4d..debd30a 100644 --- a/examples/interactive/src/main.rs +++ b/examples/interactive/src/main.rs @@ -48,17 +48,17 @@ impl InteractiveApp { simulation, elements, - settings_interaction: Default::default(), - settings_navigation: Default::default(), - changes_receiver, changes_sender, + settings_interaction: Default::default(), + settings_navigation: Default::default(), + selected_nodes: Default::default(), selected_edges: Default::default(), - + simulation_stopped: false, - + fps: 0., fps_history: Default::default(), last_update_time: Instant::now(), @@ -186,9 +186,10 @@ impl App for InteractiveApp { ui.add_space(5.); ui.add_enabled_ui(!self.settings_navigation.fit_to_screen, |ui| { - ui.checkbox(&mut self.settings_navigation.zoom_and_pan, "pan & zoom") - .on_disabled_hover_text("disabled autofit to enable pan & zoom"); - ui.label("Enable pan and zoom. To pan use LMB + drag and to zoom use Ctrl + Mouse Wheel."); + ui.vertical(|ui| { + ui.checkbox(&mut self.settings_navigation.zoom_and_pan, "pan & zoom"); + ui.label("Enable pan and zoom. To pan use LMB + drag and to zoom use Ctrl + Mouse Wheel."); + }).response.on_disabled_hover_text("disabled autofit to enable pan & zoom"); }); ui.add_space(10.); @@ -200,19 +201,20 @@ impl App for InteractiveApp { ui.label("Enable drag. To drag use LMB + drag on a node."); ui.add_space(5.); - - if ui.checkbox(&mut self.settings_interaction.node_select, "select").changed() && !self.settings_interaction.node_select { - self.settings_interaction.node_multiselect = false; - }; - ui.label("Enable select to select nodes with LMB click. If node is selected clicking on it again will deselect it."); + + ui.add_enabled_ui(!self.settings_interaction.node_multiselect, |ui| { + ui.vertical(|ui| { + ui.checkbox(&mut self.settings_interaction.node_select, "select").on_disabled_hover_text("multiselect enables select"); + ui.label("Enable select to select nodes with LMB click. If node is selected clicking on it again will deselect it."); + }).response.on_disabled_hover_text("multiselect enables select"); + }); ui.add_space(5.); - ui.add_enabled_ui(self.settings_interaction.node_select, |ui| { - ui.checkbox(&mut self.settings_interaction.node_multiselect, "multiselect") - .on_disabled_hover_text("enable select to enable multiselect"); - ui.label("Enable multiselect to select multiple nodes."); - }); + if ui.checkbox(&mut self.settings_interaction.node_multiselect, "multiselect").changed() { + self.settings_interaction.node_select = true; + } + ui.label("Enable multiselect to select multiple nodes."); ui.add_space(5.); diff --git a/src/changes.rs b/src/changes.rs index b96f715..a460294 100644 --- a/src/changes.rs +++ b/src/changes.rs @@ -23,12 +23,12 @@ impl Changes { }; } - pub(crate) fn scale_node(&mut self, idx: &usize, n: &Node, factor: f32) { + pub(crate) fn click_node(&mut self, idx: &usize) { match self.nodes.get_mut(idx) { - Some(changes_node) => changes_node.scale(n, factor), + Some(changes_node) => changes_node.click(), None => { let mut changes_node = ChangesNode::default(); - changes_node.scale(n, factor); + changes_node.click(); self.nodes.insert(*idx, changes_node); } }; @@ -99,18 +99,6 @@ impl Changes { } }; } - - pub(crate) fn scale_edge(&mut self, e: &Edge, factor: f32) { - let key = e.id(); - match self.edges.get_mut(&key) { - Some(changes_edge) => changes_edge.scale(e, factor), - None => { - let mut changes_edge = ChangesEdge::default(); - changes_edge.scale(e, factor); - self.edges.insert(key, changes_edge); - } - }; - } } /// Stores changes to the node properties @@ -121,6 +109,7 @@ pub struct ChangesNode { pub radius: Option, pub selected: Option, pub dragged: Option, + pub clicked: Option, } impl ChangesNode { @@ -153,6 +142,11 @@ impl ChangesNode { let dragged = self.dragged.get_or_insert(n.dragged); *dragged = false; } + + fn click(&mut self) { + let clicked = self.clicked.get_or_insert(true); + *clicked = true; + } } /// Stores changes to the edge properties @@ -166,17 +160,6 @@ pub struct ChangesEdge { } impl ChangesEdge { - fn scale(&mut self, n: &Edge, factor: f32) { - let width = self.width.get_or_insert(n.width); - *width *= factor; - - let tip_size = self.tip_size.get_or_insert(n.tip_size); - *tip_size *= factor; - - let curve_size = self.curve_size.get_or_insert(n.curve_size); - *curve_size *= factor; - } - fn select(&mut self, n: &Edge) { let selected = self.selected.get_or_insert(n.selected); *selected = true; diff --git a/src/elements.rs b/src/elements.rs index 1c3d21a..843c461 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -4,8 +4,10 @@ use egui::{Color32, Vec2}; use crate::{Changes, ChangesEdge, ChangesNode}; -/// Struct `Elements` represents the collection of all nodes and edges in a graph. +/// Used to store the state of the graph, i.e. the location of the nodes. /// It is passed to the GraphView widget and is used to draw the graph. +/// +/// Changes to elements should be applied using the `apply_changes` method. pub struct Elements { nodes: HashMap, edges: HashMap<(usize, usize), Vec>, diff --git a/src/graph_view.rs b/src/graph_view.rs index d77d5f3..a960789 100644 --- a/src/graph_view.rs +++ b/src/graph_view.rs @@ -37,7 +37,7 @@ impl<'a> Widget for GraphView<'a> { let state = self.draw_and_sync(&painter, &mut metadata); self.handle_drags(&response, &state, &mut metadata); - self.handle_clicks(&response, &state, &mut metadata); + self.handle_click(&response, &state, &mut metadata); self.handle_navigation(ui, &response, &state, &mut metadata); metadata.store(ui); @@ -113,17 +113,21 @@ impl<'a> GraphView<'a> { } } - fn handle_clicks(&self, response: &Response, state: &FrameState, metadata: &mut Metadata) { - if !self.setings_interaction.node_select { + fn handle_click(&self, response: &Response, state: &FrameState, metadata: &mut Metadata) { + if !response.clicked() { return; } - if !response.clicked() { + + if !(self.setings_interaction.node_click + || self.setings_interaction.node_select + || self.setings_interaction.node_multiselect) + { return; } // click on empty space let node = self.node_by_pos(metadata, response.hover_pos().unwrap()); - if node.is_none() { + if node.is_none() && self.setings_interaction.node_select { self.deselect_all_nodes(state); self.deselect_all_edges(state); return; @@ -134,6 +138,11 @@ impl<'a> GraphView<'a> { } fn handle_node_click(&self, idx: &usize, state: &FrameState) { + if !self.setings_interaction.node_select { + self.click_node(idx); + return; + } + let n = self.elements.get_node(idx).unwrap(); if n.selected { self.deselect_node(idx, n); @@ -148,6 +157,12 @@ impl<'a> GraphView<'a> { self.select_node(idx, n); } + fn click_node(&self, idx: &usize) { + let mut changes = Changes::default(); + changes.click_node(idx); + self.send_changes(changes); + } + fn select_node(&self, idx: &usize, node: &Node) { let mut changes = Changes::default(); changes.select_node(idx, node); diff --git a/src/settings.rs b/src/settings.rs index 0721c4a..955c25b 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,25 +1,18 @@ -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct SettingsInteraction { /// Node dragging pub node_drag: bool, - /// Node selection + /// Allows clicking on nodes + pub node_click: bool, + + /// Selects clicked node, enables node_click pub node_select: bool, - /// Multiselection for nodes + /// Multiselection for nodes, enables node_click pub node_multiselect: bool, } -impl Default for SettingsInteraction { - fn default() -> Self { - Self { - node_drag: false, - node_select: false, - node_multiselect: false, - } - } -} - #[derive(Debug, Clone)] pub struct SettingsNavigation { /// Fit graph to the screen