Skip to main content

appcui/ui/graphview/
graphview.rs

1use std::any::TypeId;
2
3use super::events::*;
4use super::graph::Graph;
5use super::initialization_flags::*;
6use super::RenderingOptions;
7use crate::{prelude::*, ui::graphview::GraphNode};
8
9struct NodeInfo {
10    id: usize,
11    top_left: Point,
12    origin: Point,
13}
14enum Drag {
15    None,
16    View(Point),
17    Node(NodeInfo),
18}
19
20#[CustomControl(overwrite=OnPaint+OnKeyPressed+OnMouseEvent+OnResize+OnFocus, internal=true)]
21pub struct GraphView<T>
22where
23    T: GraphNode + 'static,
24{
25    graph: Graph<T>,
26    origin_point: Point,
27    background: Option<Character>,
28    flags: Flags,
29    drag: Drag,
30    comp: ListScrollBars,
31    arrange_method: ArrangeMethod,
32    rendering_options: RenderingOptions,
33}
34impl<T> GraphView<T>
35where
36    T: GraphNode,
37{
38    /// Creates a new GraphView control with the specified layout and flags.
39    /// 
40    /// # Parameters
41    /// - `layout`: The layout configuration for positioning and sizing the GraphView
42    /// - `flags`: Combination of initialization flags that control the GraphView behavior:
43    ///   - `Flags::ScrollBars`: Enables scroll bars for navigating large graphs
44    ///   - `Flags::SearchBar`: Enables a search bar for finding nodes
45    /// 
46    /// # Example
47    /// ```rust, no_run
48    /// use appcui::prelude::*;
49    /// 
50    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
51    /// 
52    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
53    ///     layout!("x:1,y:1,w:50,h:30"),
54    ///     graphview::Flags::ScrollBars | graphview::Flags::SearchBar
55    /// );
56    /// ```
57    pub fn new(layout: Layout, flags: Flags) -> Self {
58        Self {
59            base: ControlBase::with_status_flags(
60                layout,
61                (StatusFlags::Visible | StatusFlags::Enabled | StatusFlags::AcceptInput)
62                    | if flags.contains_one(Flags::ScrollBars | Flags::SearchBar) {
63                        StatusFlags::IncreaseBottomMarginOnFocus | StatusFlags::IncreaseRightMarginOnFocus
64                    } else {
65                        StatusFlags::None
66                    },
67            ),
68            flags,
69            origin_point: Point::ORIGIN,
70            background: None,
71            drag: Drag::None,
72            graph: Graph::default(),
73            arrange_method: ArrangeMethod::GridPacked,
74            rendering_options: RenderingOptions::new(),
75            comp: ListScrollBars::new(flags.contains(Flags::ScrollBars), flags.contains(Flags::SearchBar)),
76        }
77    }
78
79    /// Sets the background of the GraphView to the specified character.
80    /// 
81    /// This method allows you to customize the background appearance of the GraphView.
82    /// The background character will be used to fill the entire control area before
83    /// drawing the graph nodes and edges.
84    /// 
85    /// # Parameters
86    /// - `backgroud_char`: The character to use as background, including its color and formatting
87    /// 
88    /// # Example
89    /// ```rust, no_run
90    /// use appcui::prelude::*;
91    /// 
92    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
93    /// 
94    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
95    ///     layout!("x:1,y:1,w:30,h:10"), 
96    ///     graphview::Flags::ScrollBars
97    /// );
98    /// graph_view.set_background(Character::new('ยท', Color::Gray, Color::Black, CharFlags::None));
99    /// ```
100    pub fn set_background(&mut self, backgroud_char: Character) {
101        self.background = Some(backgroud_char);
102    }
103
104    /// Clears the background character of the GraphView.
105    /// 
106    /// This method removes any previously set background character and resets the GraphView
107    /// to use transparent foreground and background colors, allowing the default theme
108    /// background to show through.
109    /// 
110    /// # Example
111    /// ```rust, no_run
112    /// use appcui::prelude::*;
113    /// 
114    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
115    /// 
116    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
117    ///     layout!("x:1,y:1,w:30,h:10"), 
118    ///     graphview::Flags::None
119    /// );
120    /// graph_view.set_background(Character::new('*', Color::White, Color::Black, CharFlags::None));
121    /// graph_view.clear_background(); // Removes the background character
122    /// ```
123    pub fn clear_background(&mut self) {
124        self.background = None;
125    }
126
127    /// Sets the graph data to be displayed in the GraphView.
128    /// 
129    /// This method replaces the current graph with a new one, updates the rendering options,
130    /// and automatically arranges the nodes using the current arrangement method.
131    /// 
132    /// # Parameters
133    /// - `graph`: The graph containing nodes and edges to be displayed
134    /// 
135    /// # Example
136    /// ```rust, no_run
137    /// use appcui::prelude::*;
138    /// 
139    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
140    /// 
141    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
142    ///     layout!("x:1,y:1,w:50,h:30"), 
143    ///     graphview::Flags::ScrollBars
144    /// );
145    /// 
146    /// let nodes = &["A", "B", "C", "D", "E"];
147    /// let edges = &[(0, 1), (0, 2), (1, 3), (2, 4)];
148    /// let graph = graphview::Graph::with_slices(nodes, edges, true);
149    /// graph_view.set_graph(graph);
150    /// ```
151    pub fn set_graph(&mut self, graph: Graph<T>) {
152        self.graph = graph;
153        self.graph.update_rendering_options(&self.rendering_options, &self.base);
154        self.arrange_nodes(self.arrange_method);
155    }
156
157    /// Sets the edge routing method for drawing connections between nodes.
158    /// 
159    /// This method determines how edges are drawn between nodes in the graph.
160    /// 
161    /// # Parameters
162    /// - `routing`: The routing method to use:
163    ///   - `EdgeRouting::Direct`: Draw edges as straight lines between nodes
164    ///   - `EdgeRouting::Orthogonal`: Draw edges as orthogonal (right-angled) lines
165    /// 
166    /// # Example
167    /// ```rust, no_run
168    /// use appcui::prelude::*;
169    /// 
170    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
171    /// 
172    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
173    ///     layout!("x:1,y:1,w:50,h:30"), 
174    ///     graphview::Flags::ScrollBars
175    /// );
176    /// graph_view.set_edge_routing(graphview::EdgeRouting::Orthogonal);
177    /// ```
178    pub fn set_edge_routing(&mut self, routing: EdgeRouting) {
179        self.rendering_options.edge_routing = routing;
180        self.graph.update_rendering_options(&self.rendering_options, &self.base);
181    }
182
183    /// Sets the line type used for drawing edges between nodes.
184    /// 
185    /// This method controls the visual style of the lines used to draw edges.
186    /// Different line types provide different visual appearances.
187    /// 
188    /// # Parameters
189    /// - `line_type`: The line style to use:
190    ///   - `LineType::Single`: Single lines with Unicode box-drawing characters
191    ///   - `LineType::Double`: Double lines with Unicode box-drawing characters
192    ///   - `LineType::SingleThick`: Thick single lines
193    ///   - `LineType::Border`: Border-style thick lines
194    ///   - `LineType::Ascii`: ASCII characters ('+', '-', '|')
195    ///   - `LineType::AsciiRound`: ASCII with rounded corners
196    ///   - `LineType::SingleRound`: Unicode single lines with rounded corners
197    ///   - `LineType::Braille`: Double lines using Braille characters
198    /// 
199    /// # Example
200    /// ```rust, no_run
201    /// use appcui::prelude::*;
202    /// 
203    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
204    /// 
205    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
206    ///     layout!("x:1,y:1,w:50,h:30"), 
207    ///     graphview::Flags::ScrollBars
208    /// );
209    /// graph_view.set_edge_line_type(LineType::Double);
210    /// ```
211    pub fn set_edge_line_type(&mut self, line_type: LineType) {
212        self.rendering_options.edge_line_type = line_type;
213        self.graph.update_rendering_options(&self.rendering_options, &self.base);
214    }
215
216    /// Enables or disables edge highlighting for the currently selected node.
217    /// 
218    /// When edge highlighting is enabled, edges connected to the currently selected
219    /// node will be visually highlighted to make the connections more apparent.
220    /// 
221    /// # Parameters
222    /// - `incoming`: Whether to highlight edges pointing to the current node
223    /// - `outgoing`: Whether to highlight edges originating from the current node
224    /// 
225    /// # Example
226    /// ```rust, no_run
227    /// use appcui::prelude::*;
228    /// 
229    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
230    /// 
231    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
232    ///     layout!("x:1,y:1,w:50,h:30"), 
233    ///     graphview::Flags::ScrollBars
234    /// );
235    /// // Highlight both incoming and outgoing edges
236    /// graph_view.enable_edge_highlighting(true, true);
237    /// 
238    /// // Only highlight outgoing edges
239    /// graph_view.enable_edge_highlighting(false, true);
240    /// ```
241    pub fn enable_edge_highlighting(&mut self, incoming: bool, outgoing: bool) {
242        self.rendering_options.highlight_edges_in = incoming;
243        self.rendering_options.highlight_edges_out = outgoing;
244        self.graph.update_rendering_options(&self.rendering_options, &self.base);
245    }
246
247    /// Enables or disables arrow heads on directed edges.
248    /// 
249    /// When enabled, directed edges will display arrow heads to indicate
250    /// the direction of the connection between nodes.
251    /// 
252    /// # Parameters
253    /// - `enabled`: Whether to show arrow heads on directed edges
254    /// 
255    /// # Example
256    /// ```rust, no_run
257    /// use appcui::prelude::*;
258    /// 
259    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
260    /// 
261    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
262    ///     layout!("x:1,y:1,w:50,h:30"), 
263    ///     graphview::Flags::ScrollBars
264    /// );
265    /// graph_view.enable_arrow_heads(true);
266    /// ```
267    pub fn enable_arrow_heads(&mut self, enabled: bool) {
268        self.rendering_options.show_arrow_heads = enabled;
269        self.graph.update_rendering_options(&self.rendering_options, &self.base);
270    }
271
272    /// Arranges the nodes in the graph using the specified layout algorithm.
273    /// 
274    /// This method automatically positions all nodes in the graph according to
275    /// the chosen arrangement algorithm. The graph will be resized and repainted
276    /// after the arrangement is complete.
277    /// 
278    /// # Parameters
279    /// - `method`: The arrangement algorithm to use:
280    ///   - `ArrangeMethod::None`: No automatic arrangement (manual positioning)
281    ///   - `ArrangeMethod::Grid`: Arrange nodes in a regular grid with spacing
282    ///   - `ArrangeMethod::GridPacked`: Arrange nodes in a compact grid
283    ///   - `ArrangeMethod::Circular`: Arrange nodes in a circular pattern
284    ///   - `ArrangeMethod::Hierarchical`: Arrange nodes in hierarchical layers with spacing
285    ///   - `ArrangeMethod::HierarchicalPacked`: Arrange nodes in compact hierarchical layers
286    ///   - `ArrangeMethod::ForceDirected`: Use force-directed algorithm for natural positioning
287    /// 
288    /// # Example
289    /// ```rust, no_run
290    /// use appcui::prelude::*;
291    /// 
292    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
293    /// 
294    /// let mut graph_view: GraphView<MyNode> = GraphView::new(
295    ///     layout!("x:1,y:1,w:50,h:30"), 
296    ///     graphview::Flags::ScrollBars
297    /// );
298    /// 
299    /// // Arrange nodes in a hierarchical layout
300    /// graph_view.arrange_nodes(graphview::ArrangeMethod::Hierarchical);
301    /// 
302    /// // Use force-directed layout for organic appearance
303    /// graph_view.arrange_nodes(graphview::ArrangeMethod::ForceDirected);
304    /// ```
305    pub fn arrange_nodes(&mut self, method: ArrangeMethod) {
306        match method {
307            ArrangeMethod::None => { /* do nothing */ }
308            ArrangeMethod::Grid => super::node_layout::grid::rearange(&mut self.graph, 2),
309            ArrangeMethod::GridPacked => super::node_layout::grid::rearange(&mut self.graph, 1),
310            ArrangeMethod::Circular => super::node_layout::circular::rearange(&mut self.graph),
311            ArrangeMethod::Hierarchical => super::node_layout::hierarchical::rearange(&mut self.graph, 2),
312            ArrangeMethod::HierarchicalPacked => super::node_layout::hierarchical::rearange(&mut self.graph, 1),
313            ArrangeMethod::ForceDirected => super::node_layout::force_directed::rearange(&mut self.graph),
314        }
315        self.arrange_method = method;
316        self.graph.resize_graph(true);
317        self.graph.repaint(&self.base);
318    }
319
320    /// Returns an immutable reference to the underlying graph data.
321    /// 
322    /// This method provides read-only access to the graph structure,
323    /// allowing you to query nodes, edges, and other graph properties.
324    /// 
325    /// # Returns
326    /// A reference to the `Graph<T>` containing all nodes and edges
327    /// 
328    /// # Example
329    /// ```rust, no_run
330    /// use appcui::prelude::*;
331    /// 
332    /// type MyNode = &'static str; // or any other type that implements the GraphNode trait
333    /// 
334    /// let graph_view: GraphView<MyNode> = GraphView::new(
335    ///     layout!("x:1,y:1,w:50,h:30"), 
336    ///     graphview::Flags::ScrollBars
337    /// );
338    /// 
339    /// let graph = graph_view.graph();
340    /// if let Some(node) = graph.current_node() {
341    ///     // do something with the current node
342    /// }
343    /// ```
344    pub fn graph(&self) -> &Graph<T> {
345        &self.graph
346    }
347
348    fn move_scroll_to(&mut self, x: i32, y: i32) {
349        let sz = self.size();
350        let surface_size = self.graph.size();
351        self.origin_point.x = if surface_size.width <= sz.width {
352            0
353        } else {
354            x.max((sz.width as i32) - (surface_size.width as i32))
355        };
356        self.origin_point.y = if surface_size.height <= sz.height {
357            0
358        } else {
359            y.max((sz.height as i32) - (surface_size.height as i32))
360        };
361        self.origin_point.x = self.origin_point.x.min(0);
362        self.origin_point.y = self.origin_point.y.min(0);
363        self.comp.set_indexes((-self.origin_point.x) as u64, (-self.origin_point.y) as u64);
364    }
365    fn update_scroll_pos_from_scrollbars(&mut self) {
366        let h = -(self.comp.horizontal_index() as i32);
367        let v = -(self.comp.vertical_index() as i32);
368        self.move_scroll_to(h, v);
369    }
370    fn update_scroll_bars(&mut self) {
371        let paint_sz = self.graph.size();
372        let sz = self.size();
373        self.comp.resize(paint_sz.width as u64, paint_sz.height as u64, &self.base, sz);
374        self.move_scroll_to(self.origin_point.x, self.origin_point.y);
375    }
376    fn ensure_node_is_visible(&mut self, node_id: usize) {
377        if let Some(node) = self.graph.nodes.get(node_id) {
378            let node_rect = node.rect;
379            let sz = self.size();
380            let view_rect = Rect::with_point_and_size(Point::new(-self.origin_point.x, -self.origin_point.y), sz);
381
382            if !view_rect.contains_rect(node_rect) {
383                let mut adx = 0;
384                let mut ady = 0;
385
386                if node_rect.right() > view_rect.right() {
387                    adx = node_rect.right() - view_rect.right();
388                }
389                if node_rect.left() < view_rect.left() {
390                    adx = node_rect.left() - view_rect.left();
391                }
392                if node_rect.bottom() > view_rect.bottom() {
393                    ady = node_rect.bottom() - view_rect.bottom();
394                }
395                if node_rect.top() < view_rect.top() {
396                    ady = node_rect.top() - view_rect.top();
397                }
398                self.move_scroll_to(self.origin_point.x - adx, self.origin_point.y - ady);
399            }
400        }
401    }
402    fn ensure_current_node_is_visible(&mut self) {
403        if let Some(id) = self.graph.current_node_id() {
404            self.ensure_node_is_visible(id);
405        }
406    }
407    fn search_text(&mut self) {
408        let txt = self.comp.search_text();
409        let count = self.graph.filter(txt, &self.base);
410        self.ensure_current_node_is_visible();
411        if count == self.graph.nodes.len() {
412            self.comp.clear_match_count();
413        } else {
414            self.comp.set_match_count(count);
415        }
416    }
417    fn goto_next_match(&mut self) {
418        self.graph.goto_next_match(&self.base);
419        self.ensure_current_node_is_visible();
420    }
421    fn goto_previous_match(&mut self) {
422        self.graph.goto_previous_match(&self.base);
423        self.ensure_current_node_is_visible();
424    }
425    fn raise_current_node_changed(&mut self, old_id: Option<usize>) {
426        let new_id = self.graph.current_node_id();
427        if (old_id != new_id) && new_id.is_some() {
428            self.raise_event(ControlEvent {
429                emitter: self.handle,
430                receiver: self.event_processor,
431                data: ControlEventData::GraphView(EventData {
432                    event_type: GraphViewEventTypes::CurrentNodeChanged,
433                    type_id: TypeId::of::<T>(),
434                }),
435            });
436        }
437    }
438    fn raise_action_on_node(&mut self, id: usize) {
439        self.raise_event(ControlEvent {
440            emitter: self.handle,
441            receiver: self.event_processor,
442            data: ControlEventData::GraphView(EventData {
443                event_type: GraphViewEventTypes::NodeAction(id),
444                type_id: TypeId::of::<T>(),
445            }),
446        });
447    }
448}
449impl<T> OnResize for GraphView<T>
450where
451    T: GraphNode,
452{
453    fn on_resize(&mut self, _: Size, _: Size) {
454        self.update_scroll_bars();
455    }
456}
457impl<T> OnPaint for GraphView<T>
458where
459    T: GraphNode,
460{
461    fn on_paint(&self, surface: &mut Surface, theme: &Theme) {
462        if (self.has_focus()) && (self.flags.contains_one(Flags::ScrollBars | Flags::SearchBar)) {
463            self.comp.paint(surface, theme, self);
464            surface.reduce_clip_by(0, 0, 1, 1);
465        }
466        if let Some(back) = self.background {
467            surface.clear(back);
468        }
469        surface.draw_surface(self.origin_point.x, self.origin_point.y, self.graph.surface());
470        // let sz = format!("Size {:?}", self.size());
471        // surface.write_string(0, 0, &sz, charattr!("w,black"), false);
472    }
473}
474impl<T> OnKeyPressed for GraphView<T>
475where
476    T: GraphNode,
477{
478    fn on_key_pressed(&mut self, key: Key, character: char) -> EventProcessStatus {
479        let nid = self.graph.current_node_id();
480        if self.comp.process_key_pressed(key, character) {
481            self.search_text();
482            self.raise_current_node_changed(nid);
483            return EventProcessStatus::Processed;
484        }
485        if self.graph.process_key_events(key, &self.base) {
486            self.ensure_current_node_is_visible();
487            self.comp.exit_edit_mode();
488            self.raise_current_node_changed(nid);
489            return EventProcessStatus::Processed;
490        }
491        let result = match key.value() {
492            key!("Alt+Left") => {
493                self.move_scroll_to(self.origin_point.x + 1, self.origin_point.y);
494                EventProcessStatus::Processed
495            }
496            key!("Alt+Right") => {
497                self.move_scroll_to(self.origin_point.x - 1, self.origin_point.y);
498                EventProcessStatus::Processed
499            }
500            key!("Alt+Up") => {
501                self.move_scroll_to(self.origin_point.x, self.origin_point.y + 1);
502                EventProcessStatus::Processed
503            }
504            key!("Alt+Down") => {
505                self.move_scroll_to(self.origin_point.x, self.origin_point.y - 1);
506                EventProcessStatus::Processed
507            }
508            key!("PageUp") => {
509                self.move_scroll_to(self.origin_point.x, self.origin_point.y + self.size().height as i32);
510                EventProcessStatus::Processed
511            }
512            key!("PageDown") => {
513                self.move_scroll_to(self.origin_point.x, self.origin_point.y - self.size().height as i32);
514                EventProcessStatus::Processed
515            }
516            key!("Home") => {
517                self.move_scroll_to(0, 0);
518                EventProcessStatus::Processed
519            }
520            key!("End") => {
521                self.move_scroll_to(i32::MIN, i32::MIN);
522                EventProcessStatus::Processed
523            }
524            key!("Enter") => {
525                if self.comp.is_in_edit_mode() {
526                    self.goto_next_match();
527                    self.raise_current_node_changed(nid);
528                    // exist directly so that we don't exit the edit mode
529                    return EventProcessStatus::Processed;
530                } else {
531                    if let Some(id) = self.graph.current_node_id() {
532                        self.raise_action_on_node(id);
533                        return EventProcessStatus::Processed;
534                    }
535                    EventProcessStatus::Ignored
536                }
537            }
538            key!("Ctrl+Enter") => {
539                if self.comp.is_in_edit_mode() {
540                    self.goto_previous_match();
541                    self.raise_current_node_changed(nid);
542                    // exist directly so that we don't exit the edit mode
543                    return EventProcessStatus::Processed;
544                } else {
545                    EventProcessStatus::Ignored
546                }
547            }
548            _ => EventProcessStatus::Ignored,
549        };
550        if result == EventProcessStatus::Processed {
551            self.comp.exit_edit_mode();
552        }
553        if self.comp.should_repaint() {
554            EventProcessStatus::Processed
555        } else {
556            result
557        }
558    }
559}
560
561impl<T> OnFocus for GraphView<T>
562where
563    T: GraphNode,
564{
565    fn on_focus(&mut self) {
566        self.graph.repaint(&self.base);
567    }
568
569    fn on_lose_focus(&mut self) {
570        self.graph.repaint(&self.base);
571    }
572}
573
574impl<T> OnMouseEvent for GraphView<T>
575where
576    T: GraphNode,
577{
578    fn on_mouse_event(&mut self, event: &MouseEvent) -> EventProcessStatus {
579        if self.comp.process_mouse_event(event) {
580            self.update_scroll_pos_from_scrollbars();
581            return EventProcessStatus::Processed;
582        }
583        match event {
584            MouseEvent::Enter | MouseEvent::Leave => {
585                self.graph.reset_hover(&self.base);
586                self.hide_tooltip();
587                EventProcessStatus::Processed
588            }
589            MouseEvent::Over(point) => {
590                let p = Point::new(point.x - self.origin_point.x, point.y - self.origin_point.y);
591                if !self.graph.process_mouse_over(&self.base, p) {
592                    return EventProcessStatus::Ignored;
593                }
594                if let Some(id) = self.graph.hovered_node_id() {
595                    let r = self.graph.nodes[id].rect + (self.origin_point.x, self.origin_point.y);
596                    if let Some(desc) = self.graph.node_description(id) {
597                        self.base.show_tooltip_on_rect(desc, &r);
598                    } else {
599                        self.hide_tooltip();
600                    }
601                } else {
602                    self.hide_tooltip();
603                }
604                EventProcessStatus::Processed
605            }
606            MouseEvent::Pressed(mouse_data) => {
607                let data = Point::new(mouse_data.x - self.origin_point.x, mouse_data.y - self.origin_point.y);
608                if let Some(id) = self.graph.mouse_pos_to_index(data.x, data.y) {
609                    // click on a node
610                    let nid = self.graph.current_node_id();
611                    self.graph.set_current_node(id, &self.base);
612                    let tl = self.graph.nodes[id].rect.top_left();
613                    self.drag = Drag::Node(NodeInfo {
614                        id,
615                        top_left: tl,
616                        origin: Point::new(data.x, data.y),
617                    });
618                    self.raise_current_node_changed(nid);
619                    return EventProcessStatus::Processed;
620                }
621                if self.flags.contains_one(Flags::ScrollBars) && (self.has_focus()) {
622                    let sz = self.size();
623                    if (data.x == sz.width as i32) || (data.y == sz.height as i32) {
624                        return EventProcessStatus::Ignored;
625                    }
626                }
627                self.drag = Drag::View(Point::new(data.x, data.y));
628                EventProcessStatus::Processed
629            }
630            MouseEvent::Released(mouse_data) => match &self.drag {
631                Drag::None => {
632                    EventProcessStatus::Ignored
633                }
634                Drag::View(p) => {
635                    self.move_scroll_to(self.origin_point.x + mouse_data.x - p.x, self.origin_point.y + mouse_data.y - p.y);
636                    self.drag = Drag::None;
637                    EventProcessStatus::Processed
638                }
639                Drag::Node(node_info) => {
640                    let data = Point::new(mouse_data.x - self.origin_point.x, mouse_data.y - self.origin_point.y);
641                    if self.graph.move_node_to(
642                        node_info.id,
643                        node_info.top_left.x + data.x - node_info.origin.x,
644                        node_info.top_left.y + data.y - node_info.origin.y,
645                        &self.base,
646                    ) {
647                        self.update_scroll_bars();
648                    }
649                    self.drag = Drag::None;
650                    self.ensure_current_node_is_visible();
651                    EventProcessStatus::Processed
652                }
653            },
654            MouseEvent::DoubleClick(mouse_data) => {
655                let data = Point::new(mouse_data.x - self.origin_point.x, mouse_data.y - self.origin_point.y);
656                if let Some(id) = self.graph.mouse_pos_to_index(data.x, data.y) {
657                    self.raise_action_on_node(id);
658                    EventProcessStatus::Processed
659                } else {
660                    EventProcessStatus::Ignored
661                }
662            }
663            MouseEvent::Drag(mouse_data) => match &self.drag {
664                Drag::None => {
665                    EventProcessStatus::Ignored
666                }
667                Drag::View(p) => {
668                    self.move_scroll_to(self.origin_point.x + mouse_data.x - p.x, self.origin_point.y + mouse_data.y - p.y);
669                    self.drag = Drag::View(Point::new(mouse_data.x, mouse_data.y));
670                    EventProcessStatus::Processed
671                }
672                Drag::Node(node_info) => {
673                    let data = Point::new(mouse_data.x - self.origin_point.x, mouse_data.y - self.origin_point.y);
674                    if self.graph.move_node_to(
675                        node_info.id,
676                        node_info.top_left.x + data.x - node_info.origin.x,
677                        node_info.top_left.y + data.y - node_info.origin.y,
678                        &self.base,
679                    ) {
680                        self.update_scroll_bars();
681                    }
682                    self.ensure_current_node_is_visible();
683                    EventProcessStatus::Processed
684                }
685            },
686            MouseEvent::Wheel(dir) => {
687                match dir {
688                    MouseWheelDirection::Left => self.move_scroll_to(self.origin_point.x + 1, self.origin_point.y),
689                    MouseWheelDirection::Right => self.move_scroll_to(self.origin_point.x - 1, self.origin_point.y),
690                    MouseWheelDirection::Up => self.move_scroll_to(self.origin_point.x, self.origin_point.y + 1),
691                    MouseWheelDirection::Down => self.move_scroll_to(self.origin_point.x, self.origin_point.y - 1),
692                };
693                EventProcessStatus::Processed
694            }
695        }
696    }
697}