Skip to main content

egui_map/map/
objects.rs

1//! Data types consumed by the [`Map`](super::Map) widget.
2//!
3//! This module contains the geometry primitives ([`RawPoint`], [`RawLine`]),
4//! the map content types ([`MapPoint`], [`MapLine`], [`MapLabel`]) and the
5//! customization points of the widget: [`MapSettings`], [`MapStyle`],
6//! [`VisibilitySetting`], [`ContextMenuManager`] and [`NodeTemplate`].
7
8use egui::{Align2, Color32, FontFamily, FontId, Pos2, Stroke, Ui};
9use std::convert::{From, Into};
10use std::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
11use std::time::Instant;
12
13/// A point (or vector) in 2D map coordinates.
14///
15/// `RawPoint` supports component-wise arithmetic: [`Mul`], [`Div`],
16/// [`MulAssign`] and [`DivAssign`] with `f32` and the common integer types, and
17/// [`Add`]/[`Sub`] with other points (by value or by reference). It also
18/// converts from and to `[f32; 2]`, integer arrays and [`egui::Pos2`].
19///
20/// # Examples
21///
22/// ```
23/// use egui_map::map::objects::RawPoint;
24///
25/// let a = RawPoint::new(1.0, 2.0);
26/// let b = RawPoint::new(3.0, -1.0);
27///
28/// assert_eq!((a + b).components, [4.0, 1.0]);
29/// assert_eq!((a * 2.0f32).components, [2.0, 4.0]);
30/// ```
31#[derive(Copy, Clone, Debug)]
32pub struct RawPoint {
33    /// The `x` and `y` components of the point.
34    pub components: [f32; 2],
35}
36
37impl RawPoint {
38    /// Creates a point from its `x` and `y` coordinates.
39    pub fn new(x: f32, y: f32) -> Self {
40        Self { components: [x, y] }
41    }
42}
43
44impl Default for RawPoint {
45    fn default() -> Self {
46        Self::new(0.00, 0.00)
47    }
48}
49
50impl Mul<i64> for RawPoint {
51    type Output = Self;
52
53    fn mul(self, rhs: i64) -> Self::Output {
54        Self {
55            components: [
56                self.components[0] * rhs as f32,
57                self.components[1] * rhs as f32,
58            ],
59        }
60    }
61}
62
63impl Mul<i32> for RawPoint {
64    type Output = Self;
65
66    fn mul(self, rhs: i32) -> Self::Output {
67        Self {
68            components: [
69                self.components[0] * rhs as f32,
70                self.components[1] * rhs as f32,
71            ],
72        }
73    }
74}
75
76impl Mul<u64> for RawPoint {
77    type Output = Self;
78
79    fn mul(self, rhs: u64) -> Self::Output {
80        Self {
81            components: [
82                self.components[0] * rhs as f32,
83                self.components[1] * rhs as f32,
84            ],
85        }
86    }
87}
88
89impl Mul<u32> for RawPoint {
90    type Output = Self;
91
92    fn mul(self, rhs: u32) -> Self::Output {
93        Self {
94            components: [
95                self.components[0] * rhs as f32,
96                self.components[1] * rhs as f32,
97            ],
98        }
99    }
100}
101
102impl Mul<f32> for RawPoint {
103    type Output = Self;
104
105    fn mul(self, rhs: f32) -> Self::Output {
106        Self {
107            components: [self.components[0] * rhs, self.components[1] * rhs],
108        }
109    }
110}
111
112impl MulAssign<i64> for RawPoint {
113    fn mul_assign(&mut self, rhs: i64) {
114        self.components[0] = self.components[0] * rhs as f32;
115        self.components[1] = self.components[1] * rhs as f32;
116    }
117}
118
119impl MulAssign<i32> for RawPoint {
120    fn mul_assign(&mut self, rhs: i32) {
121        self.components[0] = self.components[0] * rhs as f32;
122        self.components[1] = self.components[1] * rhs as f32;
123    }
124}
125
126impl MulAssign<u64> for RawPoint {
127    fn mul_assign(&mut self, rhs: u64) {
128        self.components[0] = self.components[0] * rhs as f32;
129        self.components[1] = self.components[1] * rhs as f32;
130    }
131}
132
133impl MulAssign<u32> for RawPoint {
134    fn mul_assign(&mut self, rhs: u32) {
135        self.components[0] = self.components[0] * rhs as f32;
136        self.components[1] = self.components[1] * rhs as f32;
137    }
138}
139
140impl MulAssign<f32> for RawPoint {
141    fn mul_assign(&mut self, rhs: f32) {
142        self.components[0] = self.components[0] * rhs;
143        self.components[1] = self.components[1] * rhs;
144    }
145}
146
147impl Div<i64> for RawPoint {
148    type Output = Self;
149
150    fn div(self, rhs: i64) -> Self::Output {
151        Self {
152            components: [
153                self.components[0] / rhs as f32,
154                self.components[1] / rhs as f32,
155            ],
156        }
157    }
158}
159
160impl Div<i32> for RawPoint {
161    type Output = Self;
162
163    fn div(self, rhs: i32) -> Self::Output {
164        Self {
165            components: [
166                self.components[0] / rhs as f32,
167                self.components[1] / rhs as f32,
168            ],
169        }
170    }
171}
172
173impl Div<u64> for RawPoint {
174    type Output = Self;
175
176    fn div(self, rhs: u64) -> Self::Output {
177        Self {
178            components: [
179                self.components[0] / rhs as f32,
180                self.components[1] / rhs as f32,
181            ],
182        }
183    }
184}
185
186impl Div<u32> for RawPoint {
187    type Output = Self;
188
189    fn div(self, rhs: u32) -> Self::Output {
190        Self {
191            components: [
192                self.components[0] / rhs as f32,
193                self.components[1] / rhs as f32,
194            ],
195        }
196    }
197}
198
199impl Div<f32> for RawPoint {
200    type Output = Self;
201
202    fn div(self, rhs: f32) -> Self::Output {
203        Self {
204            components: [self.components[0] / rhs, self.components[1] / rhs],
205        }
206    }
207}
208
209impl DivAssign<i64> for RawPoint {
210    fn div_assign(&mut self, rhs: i64) {
211        self.components[0] = self.components[0] / rhs as f32;
212        self.components[1] = self.components[1] / rhs as f32;
213    }
214}
215
216impl DivAssign<i32> for RawPoint {
217    fn div_assign(&mut self, rhs: i32) {
218        self.components[0] = self.components[0] / rhs as f32;
219        self.components[1] = self.components[1] / rhs as f32;
220    }
221}
222
223impl DivAssign<u64> for RawPoint {
224    fn div_assign(&mut self, rhs: u64) {
225        self.components[0] = self.components[0] / rhs as f32;
226        self.components[1] = self.components[1] / rhs as f32;
227    }
228}
229
230impl DivAssign<u32> for RawPoint {
231    fn div_assign(&mut self, rhs: u32) {
232        self.components[0] = self.components[0] / rhs as f32;
233        self.components[1] = self.components[1] / rhs as f32;
234    }
235}
236
237impl DivAssign<f32> for RawPoint {
238    fn div_assign(&mut self, rhs: f32) {
239        self.components[0] = self.components[0] / rhs;
240        self.components[1] = self.components[1] / rhs;
241    }
242}
243
244impl Add<RawPoint> for RawPoint {
245    type Output = RawPoint;
246    fn add(self, rhs: RawPoint) -> Self::Output {
247        Self {
248            components: [
249                self.components[0] + rhs.components[0],
250                self.components[1] + rhs.components[1],
251            ],
252        }
253    }
254}
255
256impl Sub<RawPoint> for RawPoint {
257    type Output = RawPoint;
258    fn sub(self, rhs: RawPoint) -> Self::Output {
259        Self {
260            components: [
261                self.components[0] - rhs.components[0],
262                self.components[1] - rhs.components[1],
263            ],
264        }
265    }
266}
267
268impl Add<&RawPoint> for RawPoint {
269    type Output = RawPoint;
270    fn add(self, rhs: &RawPoint) -> Self::Output {
271        Self {
272            components: [
273                self.components[0] + rhs.components[0],
274                self.components[1] + rhs.components[1],
275            ],
276        }
277    }
278}
279
280impl Sub<&RawPoint> for RawPoint {
281    type Output = RawPoint;
282    fn sub(self, rhs: &RawPoint) -> Self::Output {
283        Self {
284            components: [
285                self.components[0] - rhs.components[0],
286                self.components[1] - rhs.components[1],
287            ],
288        }
289    }
290}
291
292impl From<[f32; 2]> for RawPoint {
293    fn from(value: [f32; 2]) -> Self {
294        Self { components: value }
295    }
296}
297
298impl From<Pos2> for RawPoint {
299    fn from(value: Pos2) -> Self {
300        Self {
301            components: [value.x, value.y],
302        }
303    }
304}
305
306impl From<[i64; 2]> for RawPoint {
307    fn from(value: [i64; 2]) -> Self {
308        Self {
309            components: [value[0] as f32, value[1] as f32],
310        }
311    }
312}
313
314impl From<[i32; 2]> for RawPoint {
315    fn from(value: [i32; 2]) -> Self {
316        Self {
317            components: [value[0] as f32, value[1] as f32],
318        }
319    }
320}
321
322impl From<[i16; 2]> for RawPoint {
323    fn from(value: [i16; 2]) -> Self {
324        Self {
325            components: [value[0] as f32, value[1] as f32],
326        }
327    }
328}
329
330impl From<[i8; 2]> for RawPoint {
331    fn from(value: [i8; 2]) -> Self {
332        Self {
333            components: [value[0] as f32, value[1] as f32],
334        }
335    }
336}
337
338impl From<RawPoint> for [f32; 2] {
339    fn from(val: RawPoint) -> Self {
340        [val.components[0], val.components[1]]
341    }
342}
343
344impl From<RawPoint> for Pos2 {
345    fn from(val: RawPoint) -> Self {
346        Pos2::from(val.components)
347    }
348}
349
350/// A straight line segment between two [`RawPoint`]s.
351#[derive(Copy, Clone, Debug)]
352pub struct RawLine {
353    /// The two end points of the segment.
354    pub points: [RawPoint; 2],
355}
356
357impl RawLine {
358    /// Creates a segment between `a` and `b`.
359    pub fn new(a: RawPoint, b: RawPoint) -> Self {
360        Self { points: [a, b] }
361    }
362
363    /// Returns the Euclidean distance between the two end points.
364    pub fn distance(self) -> f32 {
365        let x = self.points[0].components[0] - self.points[1].components[0];
366        let y = self.points[0].components[1] - self.points[1].components[1];
367        (x.powi(2) + y.powi(2)).sqrt()
368    }
369
370    /// Returns the point halfway between the two end points.
371    pub fn midpoint(self) -> RawPoint {
372        let x = (self.points[0].components[0] + self.points[1].components[0]) / 2.0;
373        let y = (self.points[0].components[1] + self.points[1].components[1]) / 2.0;
374        RawPoint::new(x, y)
375    }
376}
377
378impl From<RawLine> for [Pos2; 2] {
379    fn from(val: RawLine) -> Self {
380        let position1 = val.points[0].into();
381        let position2 = val.points[1].into();
382        [position1, position2]
383    }
384}
385
386impl From<[[i64; 2]; 2]> for RawLine {
387    fn from(value: [[i64; 2]; 2]) -> Self {
388        Self {
389            points: [RawPoint::from(value[0]), RawPoint::from(value[1])],
390        }
391    }
392}
393
394/// Visual style used to paint the map under a given theme.
395///
396/// Multiplying or dividing a `MapStyle` by a number scales the stroke widths
397/// and the font size, leaving colors untouched; the widget uses this to scale
398/// the active style with the current zoom factor. Those operators panic if
399/// `border`, `line` or `font` are `None`, so they must only be applied to
400/// fully populated styles such as the ones in [`MapSettings::default()`].
401#[derive(Clone, Debug)]
402pub struct MapStyle {
403    /// Stroke used for the widget border.
404    pub border: Option<Stroke>,
405    /// Stroke used for the connection lines between nodes.
406    pub line: Option<Stroke>,
407    /// Color used to fill node shapes.
408    pub fill_color: Color32,
409    /// Color used for text.
410    pub text_color: Color32,
411    /// Font used for map labels.
412    pub font: Option<FontId>,
413    /// Background color of the map canvas.
414    pub background_color: Color32,
415    /// Color used for notification pulse animations.
416    pub alert_color: Color32,
417}
418
419impl MapStyle {
420    /// Creates a fully transparent style with no border, line or font.
421    pub fn new() -> Self {
422        MapStyle {
423            border: None,
424            line: None,
425            fill_color: Color32::TRANSPARENT,
426            text_color: Color32::TRANSPARENT,
427            font: None,
428            background_color: Color32::TRANSPARENT,
429            alert_color: Color32::TRANSPARENT,
430        }
431    }
432}
433
434impl Default for MapStyle {
435    fn default() -> Self {
436        MapStyle::new()
437    }
438}
439
440impl Mul<i64> for MapStyle {
441    // The multiplication of rational numbers is a closed operation.
442    type Output = Self;
443
444    fn mul(mut self, rhs: i64) -> Self::Output {
445        self.border.as_mut().unwrap().width *= rhs as f32;
446        self.line.as_mut().unwrap().width *= rhs as f32;
447        self.font.as_mut().unwrap().size *= rhs as f32;
448        self
449    }
450}
451
452impl Mul<i32> for MapStyle {
453    // The multiplication of rational numbers is a closed operation.
454    type Output = Self;
455
456    fn mul(mut self, rhs: i32) -> Self::Output {
457        self.border.as_mut().unwrap().width *= rhs as f32;
458        self.line.as_mut().unwrap().width *= rhs as f32;
459        self.font.as_mut().unwrap().size *= rhs as f32;
460        self
461    }
462}
463
464impl Mul<f32> for MapStyle {
465    // The multiplication of rational numbers is a closed operation.
466    type Output = Self;
467
468    fn mul(mut self, rhs: f32) -> Self::Output {
469        self.border.as_mut().unwrap().width *= rhs;
470        self.line.as_mut().unwrap().width *= rhs;
471        self.font.as_mut().unwrap().size *= rhs;
472        self
473    }
474}
475
476impl Mul<f64> for MapStyle {
477    // The multiplication of rational numbers is a closed operation.
478    type Output = Self;
479
480    fn mul(mut self, rhs: f64) -> Self::Output {
481        self.border.as_mut().unwrap().width *= rhs as f32;
482        self.line.as_mut().unwrap().width *= rhs as f32;
483        self.font.as_mut().unwrap().size *= rhs as f32;
484        self
485    }
486}
487
488impl Div<i64> for MapStyle {
489    // The multiplication of rational numbers is a closed operation.
490    type Output = Self;
491
492    fn div(mut self, rhs: i64) -> Self::Output {
493        self.border.as_mut().unwrap().width /= rhs as f32;
494        self.line.as_mut().unwrap().width /= rhs as f32;
495        self.font.as_mut().unwrap().size /= rhs as f32;
496        self
497    }
498}
499
500impl Div<i32> for MapStyle {
501    // The multiplication of rational numbers is a closed operation.
502    type Output = Self;
503
504    fn div(mut self, rhs: i32) -> Self::Output {
505        self.border.as_mut().unwrap().width /= rhs as f32;
506        self.line.as_mut().unwrap().width /= rhs as f32;
507        self.font.as_mut().unwrap().size /= rhs as f32;
508        self
509    }
510}
511
512impl Div<f32> for MapStyle {
513    // The multiplication of rational numbers is a closed operation.
514    type Output = Self;
515
516    fn div(mut self, rhs: f32) -> Self::Output {
517        self.border.as_mut().unwrap().width /= rhs;
518        self.line.as_mut().unwrap().width /= rhs;
519        self.font.as_mut().unwrap().size /= rhs;
520        self
521    }
522}
523
524impl Div<f64> for MapStyle {
525    // The multiplication of rational numbers is a closed operation.
526    type Output = Self;
527
528    fn div(mut self, rhs: f64) -> Self::Output {
529        self.border.as_mut().unwrap().width /= rhs as f32;
530        self.line.as_mut().unwrap().width /= rhs as f32;
531        self.font.as_mut().unwrap().size /= rhs as f32;
532        self
533    }
534}
535
536/// A free-floating text label drawn on the map.
537///
538/// Labels are installed with [`Map::add_labels`](super::Map::add_labels).
539#[derive(Clone, Debug)]
540pub struct MapLabel {
541    /// The text to display.
542    pub text: String,
543    /// The position of the label's center.
544    pub center: Pos2,
545}
546
547impl Default for MapLabel {
548    fn default() -> Self {
549        MapLabel::new()
550    }
551}
552
553impl MapLabel {
554    /// Creates an empty label centered at the origin.
555    pub fn new() -> Self {
556        MapLabel {
557            text: String::new(),
558            center: Pos2::new(0.00, 0.00),
559        }
560    }
561}
562
563/// A connection line between two points on the map.
564///
565/// Lines are installed with [`Map::add_lines`](super::Map::add_lines), keyed by
566/// an id that nodes reference through [`MapPoint::connections`].
567#[derive(Clone, Debug)]
568pub struct MapLine {
569    /// Optional identifier of the line.
570    pub id: Option<String>,
571    /// The segment geometry, in map coordinates.
572    pub raw_line: RawLine,
573}
574
575impl MapLine {
576    /// Creates a line between `point1` and `point2` with no id.
577    pub fn new(point1: RawPoint, point2: RawPoint) -> Self {
578        MapLine {
579            id: None,
580            raw_line: RawLine::new(point1, point2),
581        }
582    }
583}
584
585/// A node on the map: an id, a position and an optional display name.
586///
587/// Nodes are loaded into the widget through
588/// [`Map::add_hashmap_points`](super::Map::add_hashmap_points), keyed by their
589/// id.
590#[derive(Clone, Debug)]
591pub struct MapPoint {
592    /// Position of the node, in map coordinates.
593    pub raw_point: RawPoint,
594    /// Ids of the lines connecting this node with others.
595    ///
596    /// Each entry must match a key of the map passed to
597    /// [`Map::add_lines`](super::Map::add_lines). The usual pattern is to push
598    /// the same line id into the `connections` of **both** endpoint nodes; a
599    /// line is drawn as soon as any node referencing it becomes visible.
600    pub connections: Vec<String>,
601    /// Node identifier, used for lookups, notifications and markers.
602    id: usize,
603    /// Display name shown next to the node.
604    name: String,
605}
606
607impl MapPoint {
608    /// Creates a node with the given `id` at the given map coordinates.
609    pub fn new(id: usize, coords: RawPoint) -> MapPoint {
610        MapPoint {
611            raw_point: coords,
612            id,
613            connections: Vec::new(),
614            name: String::new(),
615        }
616    }
617
618    /// Returns the node identifier.
619    pub fn get_id(&self) -> usize {
620        self.id
621    }
622
623    /// Returns the node display name (empty if it was never set).
624    pub fn get_name(&self) -> String {
625        self.name.clone()
626    }
627
628    /// Sets the node display name.
629    pub fn set_name(&mut self, value: String) {
630        self.name = value;
631    }
632}
633
634impl From<std::collections::hash_map::OccupiedEntry<'_, usize, MapPoint>> for MapPoint {
635    fn from(value: std::collections::hash_map::OccupiedEntry<'_, usize, MapPoint>) -> Self {
636        let k = value.get();
637        k.clone()
638    }
639}
640
641#[derive(Clone)]
642pub(crate) struct MapBounds {
643    pub min: RawPoint,
644    pub max: RawPoint,
645    pub pos: RawPoint,
646    pub dist: f32,
647}
648
649impl MapBounds {
650    pub fn new() -> Self {
651        MapBounds {
652            min: RawPoint::default(),
653            max: RawPoint::default(),
654            pos: RawPoint::default(),
655            dist: 0.0,
656        }
657    }
658}
659
660impl Default for MapBounds {
661    fn default() -> Self {
662        MapBounds::new()
663    }
664}
665
666pub(crate) struct TextSettings {
667    pub position: RawPoint,
668    pub anchor: Align2,
669    pub text: String,
670    pub size: f32,
671    pub family: FontFamily,
672    pub text_color: Color32,
673}
674
675/// Configuration of a [`Map`](super::Map) widget.
676///
677/// [`MapSettings::default()`] provides sensible zoom limits plus a light and a
678/// dark theme; the widget picks the style to apply based on
679/// [`egui::Visuals::dark_mode`], using `styles[0]` in light mode and
680/// `styles[1]` in dark mode.
681#[derive(Clone, Debug)]
682pub struct MapSettings {
683    /// Maximum zoom factor.
684    pub max_zoom: f32,
685    /// Minimum zoom factor.
686    pub min_zoom: f32,
687    /// Zoom threshold above which connection lines become visible.
688    pub line_visible_zoom: f32,
689    /// Zoom threshold above which node names become visible when
690    /// [`node_text_visibility`](Self::node_text_visibility) is
691    /// [`VisibilitySetting::Allways`].
692    pub label_visible_zoom: f32,
693    /// Controls when node names are displayed.
694    pub node_text_visibility: VisibilitySetting,
695    /// Per-theme styles; index `0` is used in light mode, index `1` in dark
696    /// mode.
697    pub styles: Vec<MapStyle>,
698}
699
700impl MapSettings {
701    /// Creates settings with all zoom thresholds set to `0.0` and a single
702    /// transparent style.
703    ///
704    /// Prefer [`MapSettings::default()`] unless you really need to build the
705    /// configuration from scratch.
706    pub fn new() -> Self {
707        MapSettings {
708            max_zoom: 0.0,
709            min_zoom: 0.0,
710            line_visible_zoom: 0.0,
711            label_visible_zoom: 0.0,
712            node_text_visibility: VisibilitySetting::Allways,
713            styles: vec![MapStyle::new()],
714        }
715    }
716}
717
718impl Default for MapSettings {
719    /// Returns the default configuration: zoom from `0.1` to `2.0`, connection
720    /// lines visible above `0.2`, node names above `0.58`, and built-in light
721    /// and dark themes.
722    fn default() -> Self {
723        let mut obj = MapSettings {
724            max_zoom: 2.0,
725            min_zoom: 0.1,
726            line_visible_zoom: 0.2,
727            label_visible_zoom: 0.58,
728            node_text_visibility: VisibilitySetting::Allways,
729            styles: Vec::new(),
730        };
731
732        // light Theme
733        obj.styles.push(MapStyle {
734            border: Some(egui::Stroke {
735                width: 2.0,
736                color: Color32::from_rgb(216, 142, 58),
737            }),
738            line: Some(egui::Stroke {
739                width: 2.0,
740                color: Color32::DARK_RED,
741            }),
742            fill_color: Color32::from_rgb(216, 142, 58),
743            text_color: Color32::DARK_GREEN,
744            font: Some(FontId::new(12.00, FontFamily::Proportional)),
745            background_color: Color32::WHITE,
746            alert_color: Color32::from_rgb(246, 30, 131),
747        });
748
749        // Dark Theme
750        obj.styles.push(MapStyle {
751            border: Some(egui::Stroke {
752                width: 2.0,
753                color: Color32::GOLD,
754            }),
755            line: Some(egui::Stroke {
756                width: 2.0,
757                color: Color32::LIGHT_RED,
758            }),
759            fill_color: Color32::GOLD,
760            text_color: Color32::LIGHT_GREEN,
761            font: Some(FontId::new(12.00, FontFamily::Proportional)),
762            background_color: Color32::DARK_GRAY,
763            alert_color: Color32::from_rgb(128, 12, 67),
764        });
765        obj
766    }
767}
768
769/// Controls when the name of a node is displayed next to it.
770#[derive(Clone, Debug, PartialEq)]
771pub enum VisibilitySetting {
772    /// Never show node names.
773    Hidden,
774    /// Only show the name of the node closest to the mouse pointer.
775    Hover,
776    /// Always show node names, subject to [`MapSettings::label_visible_zoom`].
777    Allways,
778}
779
780/// Provides the contents of the widget's right-click context menu.
781///
782/// Install an implementation with
783/// [`Map::set_context_manager`](super::Map::set_context_manager).
784///
785/// # Examples
786///
787/// ```
788/// use egui_map::map::objects::ContextMenuManager;
789///
790/// struct MyMenu;
791///
792/// impl ContextMenuManager for MyMenu {
793///     fn ui(&self, ui: &mut egui::Ui) {
794///         ui.label("Hello from the map!");
795///     }
796/// }
797/// ```
798pub trait ContextMenuManager {
799    /// Builds the menu contents; called every frame while the menu is open.
800    fn ui(&self, ui: &mut Ui);
801}
802
803/// Customizes how nodes and their visual effects are rendered.
804///
805/// When a template is installed with
806/// [`Map::set_node_template`](super::Map::set_node_template), the widget
807/// delegates all node painting to it instead of using the built-in shapes and
808/// animations — including the node name labels, so draw the name yourself in
809/// [`NodeTemplate::node_ui`] if you need it.
810///
811/// The positions passed to these methods are in screen coordinates: already
812/// scaled by `zoom` and translated to the viewport origin. Multiply every size
813/// by `zoom` so your shapes scale together with the map.
814///
815/// # Animation idioms
816///
817/// egui only repaints on demand, so any method that animates (a blinking
818/// marker, a fading notification, ...) must call
819/// [`ui.ctx().request_repaint()`](egui::Context::request_repaint) to keep the
820/// frames coming. Time-driven effects are usually computed from
821/// [`Instant::now()`] (see `initial_time` in
822/// [`NodeTemplate::notification_ui`]) or from the system clock.
823///
824/// # Examples
825///
826/// A node drawn as a rounded box with its name inside, plus a notification
827/// animation that expands and fades out over two seconds:
828///
829/// ```
830/// use egui_map::map::objects::{MapPoint, NodeTemplate};
831/// use egui::{Align2, Color32, CornerRadius, FontId, Pos2, Rect, Stroke, Ui, Vec2};
832/// use std::time::Instant;
833///
834/// struct BoxedNodes;
835///
836/// impl NodeTemplate for BoxedNodes {
837///     fn node_ui(&self, ui: &mut Ui, point: Pos2, zoom: f32, system: &MapPoint) {
838///         // Multiply every size by `zoom` so the node scales with the map.
839///         let rect = Rect::from_center_size(point, Vec2::new(90.0 * zoom, 35.0 * zoom));
840///         let rounding = CornerRadius::same((10.0 * zoom) as u8);
841///         let painter = ui.painter();
842///         painter.rect_filled(rect, rounding, ui.visuals().extreme_bg_color);
843///         painter.rect_stroke(
844///             rect,
845///             rounding,
846///             Stroke::new(4.0 * zoom, Color32::WHITE),
847///             egui::StrokeKind::Middle,
848///         );
849///         painter.text(
850///             point,
851///             Align2::CENTER_CENTER,
852///             system.get_name(),
853///             FontId::proportional(12.0 * zoom),
854///             Color32::WHITE,
855///         );
856///     }
857///
858///     fn notification_ui(
859///         &self,
860///         ui: &mut Ui,
861///         point: Pos2,
862///         zoom: f32,
863///         initial_time: Instant,
864///         color: Color32,
865///     ) -> bool {
866///         let secs = Instant::now().duration_since(initial_time).as_secs_f32();
867///         // Expand the stroke and fade the color out over 2 seconds.
868///         let alpha = (1.0 - secs / 2.0).clamp(0.0, 1.0);
869///         let fading =
870///             Color32::from_rgba_unmultiplied(color.r(), color.g(), color.b(), (255.0 * alpha) as u8);
871///         let rect = Rect::from_center_size(point, Vec2::new(90.0 * zoom, 35.0 * zoom));
872///         ui.painter().rect_stroke(
873///             rect,
874///             CornerRadius::same((10.0 * zoom) as u8),
875///             Stroke::new((4.0 + 25.0 * secs) * zoom, fading),
876///             egui::StrokeKind::Middle,
877///         );
878///         // Keep the animation frames coming.
879///         ui.ctx().request_repaint();
880///         // Returning `false` removes the notification.
881///         secs < 2.0
882///     }
883///     # fn selection_ui(&self, ui: &mut Ui, point: Pos2, zoom: f32) {
884///     #     let rect = Rect::from_center_size(point, Vec2::new(94.0 * zoom, 39.0 * zoom));
885///     #     ui.painter().rect_stroke(
886///     #         rect,
887///     #         CornerRadius::same((10.0 * zoom) as u8),
888///     #         Stroke::new(3.0 * zoom, Color32::YELLOW),
889///     #         egui::StrokeKind::Middle,
890///     #     );
891///     # }
892///     # fn marker_ui(&self, ui: &mut Ui, point: Pos2, zoom: f32) {
893///     #     ui.painter().circle_stroke(point, 6.0 * zoom, Stroke::new(2.0 * zoom, Color32::LIGHT_GREEN));
894///     #     ui.ctx().request_repaint();
895///     # }
896/// }
897/// ```
898pub trait NodeTemplate {
899    /// Draws a node, replacing the default filled circle.
900    ///
901    /// Called every frame for each visible node. The widget no longer draws
902    /// the node name once a template is installed, so render it here (e.g.
903    /// with [`Painter::text`](egui::Painter::text)) if you need it.
904    fn node_ui(&self, ui: &mut Ui, _viewport_point: Pos2, _zoom: f32, _system: &MapPoint);
905
906    /// Draws the highlight over the node closest to the mouse pointer.
907    ///
908    /// The nearest node is only computed while the pointer is over the map and
909    /// [`MapSettings::node_text_visibility`] is [`VisibilitySetting::Hover`].
910    fn selection_ui(&self, ui: &mut Ui, _viewport_point: Pos2, _zoom: f32);
911
912    /// Draws the notification effect of a node notified at `initial_time`.
913    ///
914    /// Called every frame for each node passed to
915    /// [`Map::notify`](super::Map::notify). Should return `true` while the
916    /// animation is still playing — remember to call
917    /// [`ui.ctx().request_repaint()`](egui::Context::request_repaint) —; once
918    /// it returns `false` the notification is discarded.
919    fn notification_ui(
920        &self,
921        ui: &mut Ui,
922        _viewport_point: Pos2,
923        _zoom: f32,
924        initial_time: Instant,
925        color: Color32,
926    ) -> bool;
927
928    /// Draws a marker over the given node.
929    ///
930    /// Called every frame for each marker registered with
931    /// [`Map::update_marker`](super::Map::update_marker). For animated markers
932    /// (e.g. a blinking light), drive the effect from the system clock and
933    /// call [`ui.ctx().request_repaint()`](egui::Context::request_repaint).
934    fn marker_ui(&self, ui: &mut Ui, _viewport_point: Pos2, _zoom: f32);
935}
936
937#[cfg(test)]
938mod tests {
939    use super::*;
940    use std::collections::HashMap;
941
942    // ---------- RawPoint ----------
943
944    #[test]
945    fn raw_point_new() {
946        let p = RawPoint::new(3.5, -2.0);
947        assert_eq!(p.components, [3.5, -2.0]);
948    }
949
950    #[test]
951    fn raw_point_default() {
952        let p = RawPoint::default();
953        assert_eq!(p.components, [0.0, 0.0]);
954    }
955
956    #[test]
957    fn raw_point_mul_i64() {
958        let p = RawPoint::new(2.0, -3.0) * 3i64;
959        assert_eq!(p.components, [6.0, -9.0]);
960    }
961
962    #[test]
963    fn raw_point_mul_i32() {
964        let p = RawPoint::new(2.0, -3.0) * 3i32;
965        assert_eq!(p.components, [6.0, -9.0]);
966    }
967
968    #[test]
969    fn raw_point_mul_u64() {
970        let p = RawPoint::new(2.0, -3.0) * 3u64;
971        assert_eq!(p.components, [6.0, -9.0]);
972    }
973
974    #[test]
975    fn raw_point_mul_u32() {
976        let p = RawPoint::new(2.0, -3.0) * 3u32;
977        assert_eq!(p.components, [6.0, -9.0]);
978    }
979
980    #[test]
981    fn raw_point_mul_f32() {
982        let p = RawPoint::new(2.0, -3.0) * 0.5f32;
983        assert_eq!(p.components, [1.0, -1.5]);
984    }
985
986    #[test]
987    fn raw_point_mul_assign_i64() {
988        let mut p = RawPoint::new(2.0, -3.0);
989        p *= 3i64;
990        assert_eq!(p.components, [6.0, -9.0]);
991    }
992
993    #[test]
994    fn raw_point_mul_assign_i32() {
995        let mut p = RawPoint::new(2.0, -3.0);
996        p *= 3i32;
997        assert_eq!(p.components, [6.0, -9.0]);
998    }
999
1000    #[test]
1001    fn raw_point_mul_assign_u64() {
1002        let mut p = RawPoint::new(2.0, -3.0);
1003        p *= 3u64;
1004        assert_eq!(p.components, [6.0, -9.0]);
1005    }
1006
1007    #[test]
1008    fn raw_point_mul_assign_u32() {
1009        let mut p = RawPoint::new(2.0, -3.0);
1010        p *= 3u32;
1011        assert_eq!(p.components, [6.0, -9.0]);
1012    }
1013
1014    #[test]
1015    fn raw_point_mul_assign_f32() {
1016        let mut p = RawPoint::new(2.0, -3.0);
1017        p *= 0.5f32;
1018        assert_eq!(p.components, [1.0, -1.5]);
1019    }
1020
1021    #[test]
1022    fn raw_point_div_i64() {
1023        let p = RawPoint::new(6.0, -9.0) / 3i64;
1024        assert_eq!(p.components, [2.0, -3.0]);
1025    }
1026
1027    #[test]
1028    fn raw_point_div_i32() {
1029        let p = RawPoint::new(6.0, -9.0) / 3i32;
1030        assert_eq!(p.components, [2.0, -3.0]);
1031    }
1032
1033    #[test]
1034    fn raw_point_div_u64() {
1035        let p = RawPoint::new(6.0, -9.0) / 3u64;
1036        assert_eq!(p.components, [2.0, -3.0]);
1037    }
1038
1039    #[test]
1040    fn raw_point_div_u32() {
1041        let p = RawPoint::new(6.0, -9.0) / 3u32;
1042        assert_eq!(p.components, [2.0, -3.0]);
1043    }
1044
1045    #[test]
1046    fn raw_point_div_f32() {
1047        let p = RawPoint::new(1.0, -1.5) / 0.5f32;
1048        assert_eq!(p.components, [2.0, -3.0]);
1049    }
1050
1051    #[test]
1052    fn raw_point_div_assign_i64() {
1053        let mut p = RawPoint::new(6.0, -9.0);
1054        p /= 3i64;
1055        assert_eq!(p.components, [2.0, -3.0]);
1056    }
1057
1058    #[test]
1059    fn raw_point_div_assign_i32() {
1060        let mut p = RawPoint::new(6.0, -9.0);
1061        p /= 3i32;
1062        assert_eq!(p.components, [2.0, -3.0]);
1063    }
1064
1065    #[test]
1066    fn raw_point_div_assign_u64() {
1067        let mut p = RawPoint::new(6.0, -9.0);
1068        p /= 3u64;
1069        assert_eq!(p.components, [2.0, -3.0]);
1070    }
1071
1072    #[test]
1073    fn raw_point_div_assign_u32() {
1074        let mut p = RawPoint::new(6.0, -9.0);
1075        p /= 3u32;
1076        assert_eq!(p.components, [2.0, -3.0]);
1077    }
1078
1079    #[test]
1080    fn raw_point_div_assign_f32() {
1081        let mut p = RawPoint::new(1.0, -1.5);
1082        p /= 0.5f32;
1083        assert_eq!(p.components, [2.0, -3.0]);
1084    }
1085
1086    #[test]
1087    fn raw_point_add() {
1088        let a = RawPoint::new(1.0, 2.0);
1089        let b = RawPoint::new(3.0, -4.0);
1090        let c = a + b;
1091        assert_eq!(c.components, [4.0, -2.0]);
1092    }
1093
1094    #[test]
1095    fn raw_point_sub() {
1096        let a = RawPoint::new(1.0, 2.0);
1097        let b = RawPoint::new(3.0, -4.0);
1098        let c = a - b;
1099        assert_eq!(c.components, [-2.0, 6.0]);
1100    }
1101
1102    #[test]
1103    fn raw_point_add_ref() {
1104        let a = RawPoint::new(1.0, 2.0);
1105        let b = RawPoint::new(3.0, -4.0);
1106        let c = a + &b;
1107        assert_eq!(c.components, [4.0, -2.0]);
1108        // b sigue siendo usable tras la suma por referencia
1109        assert_eq!(b.components, [3.0, -4.0]);
1110    }
1111
1112    #[test]
1113    fn raw_point_sub_ref() {
1114        let a = RawPoint::new(1.0, 2.0);
1115        let b = RawPoint::new(3.0, -4.0);
1116        let c = a - &b;
1117        assert_eq!(c.components, [-2.0, 6.0]);
1118        assert_eq!(b.components, [3.0, -4.0]);
1119    }
1120
1121    #[test]
1122    fn raw_point_from_f32_array() {
1123        let p = RawPoint::from([1.5f32, -2.5f32]);
1124        assert_eq!(p.components, [1.5, -2.5]);
1125    }
1126
1127    #[test]
1128    fn raw_point_from_i64_array() {
1129        let p = RawPoint::from([3i64, -4i64]);
1130        assert_eq!(p.components, [3.0, -4.0]);
1131    }
1132
1133    #[test]
1134    fn raw_point_from_i32_array() {
1135        let p = RawPoint::from([3i32, -4i32]);
1136        assert_eq!(p.components, [3.0, -4.0]);
1137    }
1138
1139    #[test]
1140    fn raw_point_from_i16_array() {
1141        let p = RawPoint::from([3i16, -4i16]);
1142        assert_eq!(p.components, [3.0, -4.0]);
1143    }
1144
1145    #[test]
1146    fn raw_point_from_i8_array() {
1147        let p = RawPoint::from([3i8, -4i8]);
1148        assert_eq!(p.components, [3.0, -4.0]);
1149    }
1150
1151    #[test]
1152    fn raw_point_from_pos2() {
1153        let p = RawPoint::from(Pos2::new(7.0, 8.0));
1154        assert_eq!(p.components, [7.0, 8.0]);
1155    }
1156
1157    #[test]
1158    fn raw_point_into_f32_array() {
1159        let arr: [f32; 2] = RawPoint::new(7.0, 8.0).into();
1160        assert_eq!(arr, [7.0, 8.0]);
1161    }
1162
1163    #[test]
1164    fn raw_point_into_pos2() {
1165        let pos: Pos2 = RawPoint::new(7.0, 8.0).into();
1166        assert_eq!(pos, Pos2::new(7.0, 8.0));
1167    }
1168
1169    // ---------- RawLine ----------
1170
1171    #[test]
1172    fn raw_line_new() {
1173        let a = RawPoint::new(1.0, 2.0);
1174        let b = RawPoint::new(3.0, 4.0);
1175        let line = RawLine::new(a, b);
1176        assert_eq!(line.points[0].components, [1.0, 2.0]);
1177        assert_eq!(line.points[1].components, [3.0, 4.0]);
1178    }
1179
1180    #[test]
1181    fn raw_line_distance() {
1182        // triángulo 3-4-5
1183        let line = RawLine::new(RawPoint::new(0.0, 0.0), RawPoint::new(3.0, 4.0));
1184        assert_eq!(line.distance(), 5.0);
1185    }
1186
1187    #[test]
1188    fn raw_line_distance_zero() {
1189        let line = RawLine::new(RawPoint::new(2.0, 2.0), RawPoint::new(2.0, 2.0));
1190        assert_eq!(line.distance(), 0.0);
1191    }
1192
1193    #[test]
1194    fn raw_line_midpoint() {
1195        let line = RawLine::new(RawPoint::new(0.0, 0.0), RawPoint::new(4.0, 6.0));
1196        let mid = line.midpoint();
1197        assert_eq!(mid.components, [2.0, 3.0]);
1198    }
1199
1200    #[test]
1201    fn raw_line_into_pos2_array() {
1202        let line = RawLine::new(RawPoint::new(1.0, 2.0), RawPoint::new(3.0, 4.0));
1203        let arr: [Pos2; 2] = line.into();
1204        assert_eq!(arr, [Pos2::new(1.0, 2.0), Pos2::new(3.0, 4.0)]);
1205    }
1206
1207    #[test]
1208    fn raw_line_from_i64_arrays() {
1209        let line = RawLine::from([[1i64, 2i64], [3i64, 4i64]]);
1210        assert_eq!(line.points[0].components, [1.0, 2.0]);
1211        assert_eq!(line.points[1].components, [3.0, 4.0]);
1212    }
1213
1214    // ---------- MapStyle ----------
1215
1216    fn full_style() -> MapStyle {
1217        MapStyle {
1218            border: Some(Stroke::new(2.0, Color32::RED)),
1219            line: Some(Stroke::new(4.0, Color32::BLUE)),
1220            fill_color: Color32::GREEN,
1221            text_color: Color32::WHITE,
1222            font: Some(FontId::new(10.0, FontFamily::Proportional)),
1223            background_color: Color32::BLACK,
1224            alert_color: Color32::YELLOW,
1225        }
1226    }
1227
1228    #[test]
1229    fn map_style_new() {
1230        let s = MapStyle::new();
1231        assert!(s.border.is_none());
1232        assert!(s.line.is_none());
1233        assert!(s.font.is_none());
1234        assert_eq!(s.fill_color, Color32::TRANSPARENT);
1235        assert_eq!(s.text_color, Color32::TRANSPARENT);
1236        assert_eq!(s.background_color, Color32::TRANSPARENT);
1237        assert_eq!(s.alert_color, Color32::TRANSPARENT);
1238    }
1239
1240    #[test]
1241    fn map_style_default_equals_new() {
1242        let s = MapStyle::default();
1243        assert!(s.border.is_none());
1244        assert!(s.line.is_none());
1245        assert!(s.font.is_none());
1246    }
1247
1248    #[test]
1249    fn map_style_mul_i64() {
1250        let s = full_style() * 2i64;
1251        assert_eq!(s.border.unwrap().width, 4.0);
1252        assert_eq!(s.line.unwrap().width, 8.0);
1253        assert_eq!(s.font.unwrap().size, 20.0);
1254    }
1255
1256    #[test]
1257    fn map_style_mul_i32() {
1258        let s = full_style() * 2i32;
1259        assert_eq!(s.border.unwrap().width, 4.0);
1260        assert_eq!(s.line.unwrap().width, 8.0);
1261        assert_eq!(s.font.unwrap().size, 20.0);
1262    }
1263
1264    #[test]
1265    fn map_style_mul_f32() {
1266        let s = full_style() * 0.5f32;
1267        assert_eq!(s.border.unwrap().width, 1.0);
1268        assert_eq!(s.line.unwrap().width, 2.0);
1269        assert_eq!(s.font.unwrap().size, 5.0);
1270    }
1271
1272    #[test]
1273    fn map_style_mul_f64() {
1274        let s = full_style() * 0.5f64;
1275        assert_eq!(s.border.unwrap().width, 1.0);
1276        assert_eq!(s.line.unwrap().width, 2.0);
1277        assert_eq!(s.font.unwrap().size, 5.0);
1278    }
1279
1280    #[test]
1281    fn map_style_div_i64() {
1282        let s = full_style() / 2i64;
1283        assert_eq!(s.border.unwrap().width, 1.0);
1284        assert_eq!(s.line.unwrap().width, 2.0);
1285        assert_eq!(s.font.unwrap().size, 5.0);
1286    }
1287
1288    #[test]
1289    fn map_style_div_i32() {
1290        let s = full_style() / 2i32;
1291        assert_eq!(s.border.unwrap().width, 1.0);
1292        assert_eq!(s.line.unwrap().width, 2.0);
1293        assert_eq!(s.font.unwrap().size, 5.0);
1294    }
1295
1296    #[test]
1297    fn map_style_div_f32() {
1298        let s = full_style() / 0.5f32;
1299        assert_eq!(s.border.unwrap().width, 4.0);
1300        assert_eq!(s.line.unwrap().width, 8.0);
1301        assert_eq!(s.font.unwrap().size, 20.0);
1302    }
1303
1304    #[test]
1305    fn map_style_div_f64() {
1306        let s = full_style() / 0.5f64;
1307        assert_eq!(s.border.unwrap().width, 4.0);
1308        assert_eq!(s.line.unwrap().width, 8.0);
1309        assert_eq!(s.font.unwrap().size, 20.0);
1310    }
1311
1312    // ---------- MapLabel ----------
1313
1314    #[test]
1315    fn map_label_new() {
1316        let l = MapLabel::new();
1317        assert_eq!(l.text, String::new());
1318        assert_eq!(l.center, Pos2::new(0.0, 0.0));
1319    }
1320
1321    #[test]
1322    fn map_label_default_equals_new() {
1323        let l = MapLabel::default();
1324        assert_eq!(l.text, String::new());
1325        assert_eq!(l.center, Pos2::new(0.0, 0.0));
1326    }
1327
1328    // ---------- MapLine ----------
1329
1330    #[test]
1331    fn map_line_new() {
1332        let a = RawPoint::new(1.0, 2.0);
1333        let b = RawPoint::new(3.0, 4.0);
1334        let line = MapLine::new(a, b);
1335        assert!(line.id.is_none());
1336        assert_eq!(line.raw_line.points[0].components, [1.0, 2.0]);
1337        assert_eq!(line.raw_line.points[1].components, [3.0, 4.0]);
1338    }
1339
1340    // ---------- MapPoint ----------
1341
1342    #[test]
1343    fn map_point_new() {
1344        let p = MapPoint::new(42, RawPoint::new(1.0, 2.0));
1345        assert_eq!(p.get_id(), 42);
1346        assert_eq!(p.raw_point.components, [1.0, 2.0]);
1347        assert!(p.connections.is_empty());
1348        assert_eq!(p.get_name(), String::new());
1349    }
1350
1351    #[test]
1352    fn map_point_set_and_get_name() {
1353        let mut p = MapPoint::new(1, RawPoint::default());
1354        p.set_name("Jita".to_string());
1355        assert_eq!(p.get_name(), "Jita");
1356    }
1357
1358    #[test]
1359    fn map_point_from_occupied_entry() {
1360        let mut map: HashMap<usize, MapPoint> = HashMap::new();
1361        let mut original = MapPoint::new(7, RawPoint::new(5.0, 6.0));
1362        original.set_name("Amarr".to_string());
1363        map.insert(7, original);
1364
1365        use std::collections::hash_map::Entry;
1366        if let Entry::Occupied(entry) = map.entry(7) {
1367            let cloned = MapPoint::from(entry);
1368            assert_eq!(cloned.get_id(), 7);
1369            assert_eq!(cloned.get_name(), "Amarr");
1370            assert_eq!(cloned.raw_point.components, [5.0, 6.0]);
1371        } else {
1372            panic!("se esperaba una entrada ocupada");
1373        }
1374    }
1375
1376    // ---------- MapBounds ----------
1377
1378    #[test]
1379    fn map_bounds_new() {
1380        let b = MapBounds::new();
1381        assert_eq!(b.min.components, [0.0, 0.0]);
1382        assert_eq!(b.max.components, [0.0, 0.0]);
1383        assert_eq!(b.pos.components, [0.0, 0.0]);
1384        assert_eq!(b.dist, 0.0);
1385    }
1386
1387    #[test]
1388    fn map_bounds_default_equals_new() {
1389        let b = MapBounds::default();
1390        assert_eq!(b.dist, 0.0);
1391        assert_eq!(b.pos.components, [0.0, 0.0]);
1392    }
1393
1394    // ---------- MapSettings ----------
1395
1396    #[test]
1397    fn map_settings_new() {
1398        let s = MapSettings::new();
1399        assert_eq!(s.max_zoom, 0.0);
1400        assert_eq!(s.min_zoom, 0.0);
1401        assert_eq!(s.line_visible_zoom, 0.0);
1402        assert_eq!(s.label_visible_zoom, 0.0);
1403        assert_eq!(s.node_text_visibility, VisibilitySetting::Allways);
1404        assert_eq!(s.styles.len(), 1);
1405    }
1406
1407    #[test]
1408    fn map_settings_default() {
1409        let s = MapSettings::default();
1410        assert_eq!(s.max_zoom, 2.0);
1411        assert_eq!(s.min_zoom, 0.1);
1412        assert_eq!(s.line_visible_zoom, 0.2);
1413        assert_eq!(s.label_visible_zoom, 0.58);
1414        assert_eq!(s.node_text_visibility, VisibilitySetting::Allways);
1415        // light + dark themes
1416        assert_eq!(s.styles.len(), 2);
1417        // light theme
1418        assert_eq!(s.styles[0].background_color, Color32::WHITE);
1419        assert!(s.styles[0].border.is_some());
1420        assert!(s.styles[0].line.is_some());
1421        assert!(s.styles[0].font.is_some());
1422        // dark theme
1423        assert_eq!(s.styles[1].background_color, Color32::DARK_GRAY);
1424        assert!(s.styles[1].border.is_some());
1425        assert!(s.styles[1].line.is_some());
1426        assert!(s.styles[1].font.is_some());
1427    }
1428
1429    // ---------- VisibilitySetting ----------
1430
1431    #[test]
1432    fn visibility_setting_equality() {
1433        assert_eq!(VisibilitySetting::Hidden, VisibilitySetting::Hidden);
1434        assert_eq!(VisibilitySetting::Hover, VisibilitySetting::Hover);
1435        assert_eq!(VisibilitySetting::Allways, VisibilitySetting::Allways);
1436        assert_ne!(VisibilitySetting::Hidden, VisibilitySetting::Hover);
1437        assert_ne!(VisibilitySetting::Hover, VisibilitySetting::Allways);
1438        assert_ne!(VisibilitySetting::Hidden, VisibilitySetting::Allways);
1439    }
1440}