Skip to main content

graphplot/
style.rs

1use core::iter::DoubleEndedIterator;
2use std::fmt::Display;
3use std::{collections::BTreeMap, fmt::Debug};
4
5use serde::{Deserialize, Serialize};
6
7mod edgestyle;
8mod graphstyle;
9mod nodestyle;
10
11pub use edgestyle::*;
12pub use graphstyle::*;
13pub use nodestyle::*;
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
16#[serde(rename_all = "kebab-case")]
17pub struct Style {
18    background_color: String,
19    background_opacity: f32,
20    fullscreen: bool, // svg only
21    edge: EdgeStyle,
22    edge_highlighted: EdgeStyle,
23    graph: GraphStyle,
24    node: NodeStyle,
25    node_highlighted: NodeStyle,
26    subgraph: GraphStyle,
27    utils: StyleUtils,
28}
29impl Style {
30    /// Oppretter en [`Theme`] fra fil.
31    pub fn from_file(filename: &str) -> Self {
32        let json = std::fs::read_to_string(filename).expect("Error reading json with theme");
33        serde_json::from_str(&json).expect("Error parsing json to Theme")
34    }
35    /// Returnerer mørkt standard Theme.
36    pub fn dark() -> Self {
37        let json = include_str!("../styles/dark.json");
38        serde_json::from_str(json).expect("Error parsing json to Theme (includstr! dark.json)")
39    }
40    /// Returnerer lyst standard Theme.
41    pub fn light() -> Self {
42        let json = include_str!("../styles/light.json");
43        serde_json::from_str(json).expect("Error parsing json to Theme (includstr! light.json)")
44    }
45
46    // -- getters
47    pub fn get_background_color(&self) -> &str {
48        &self.background_color
49    }
50    pub fn get_background_opacity(&self) -> f32 {
51        self.background_opacity
52    }
53    pub fn get_fullscreen(&self) -> bool {
54        self.fullscreen
55    }
56    pub fn get_edge(&self) -> &EdgeStyle {
57        &self.edge
58    }
59    pub fn get_edge_highlighted(&self) -> &EdgeStyle {
60        &self.edge_highlighted
61    }
62    pub fn get_graph(&self) -> &GraphStyle {
63        &self.graph
64    }
65    pub fn get_node(&self) -> &NodeStyle {
66        &self.node
67    }
68    pub fn get_node_highlighted(&self) -> &NodeStyle {
69        &self.node_highlighted
70    }
71    pub fn get_subgraph(&self) -> &GraphStyle {
72        &self.subgraph
73    }
74    pub fn get_utils(&self) -> &StyleUtils {
75        &self.utils
76    }
77
78    // -- setter
79    /// Adds background-color to the plot.
80    pub fn background_color<S: Display>(mut self, color: S) -> Self {
81        self.background_color = color.to_string();
82        self
83    }
84    pub fn background_opacity(mut self, opacity: f32) -> Self {
85        self.background_opacity = opacity;
86        self
87    }
88    /// Disables SVG fullscreen background color.
89    pub fn disable_fullscreen(mut self) -> Self {
90        self.fullscreen = false;
91        self
92    }
93    /// Adds a section to <defs>.
94    pub fn def<S: Display>(mut self, def: S) -> Self {
95        self.utils.defs.push(def.to_string());
96        self
97    }
98    /// Adds a webfont URL to the SVG. E.g: "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700".
99    pub fn webfont<S: Display>(mut self, font: S) -> Self {
100        self.utils.webfonts.push(font.to_string());
101        self
102    }
103
104    // -- builders
105    pub fn edge(mut self, configure_edge: impl FnOnce(EdgeStyle) -> EdgeStyle) -> Self {
106        self.edge = configure_edge(self.edge);
107        self
108    }
109    pub fn edge_highlighted(mut self, configure_edge_highlighted: impl FnOnce(EdgeStyle) -> EdgeStyle) -> Self {
110        self.edge_highlighted = configure_edge_highlighted(self.edge_highlighted);
111        self
112    }
113    pub fn graph(mut self, configure_graph: impl FnOnce(GraphStyle) -> GraphStyle) -> Self {
114        self.graph = configure_graph(self.graph);
115        self
116    }
117    pub fn node(mut self, configure_node: impl FnOnce(NodeStyle) -> NodeStyle) -> Self {
118        self.node = configure_node(self.node);
119        self
120    }
121    pub fn node_highlighted(mut self, configure_node_highlighted: impl FnOnce(NodeStyle) -> NodeStyle) -> Self {
122        self.node_highlighted = configure_node_highlighted(self.node_highlighted);
123        self
124    }
125    pub fn subgraph(mut self, configure_subgraph: impl FnOnce(GraphStyle) -> GraphStyle) -> Self {
126        self.subgraph = configure_subgraph(self.subgraph);
127        self
128    }
129}
130impl Default for Style {
131    fn default() -> Self {
132        Self::light()
133    }
134}
135
136#[derive(Clone, Debug, Deserialize, Serialize)]
137#[serde(rename_all = "kebab-case")]
138pub struct StyleUtils {
139    defs: Vec<String>,
140    webfonts: Vec<String>,
141}
142impl StyleUtils {
143    // -- getters
144    /// Returns every <defs> -section added to theme.
145    pub fn get_defs(&self) -> &[String] {
146        &self.defs
147    }
148    /// Returns every webfont-URL (Googl Fonts, Adobe Fonts etc.)
149    pub fn get_webfonts(&self) -> &[String] {
150        &self.webfonts
151    }
152}
153
154// -- shared
155#[derive(Clone, Debug, Default, Deserialize, Serialize)]
156#[serde(rename_all = "kebab-case")]
157pub struct FontStyle {
158    color: String,
159    family: String,
160    anchor: TextAnchor,
161    opacity: f32,
162    size: f32,
163    attributes: BTreeMap<String, String>,
164}
165impl FontStyle {
166    // -- getters
167    pub fn get_anchor(&self) -> TextAnchor {
168        self.anchor
169    }
170    pub fn get_color(&self) -> &str {
171        &self.color
172    }
173    pub fn get_family(&self) -> &str {
174        &self.family
175    }
176    pub fn get_opacity(&self) -> f32 {
177        self.opacity
178    }
179    pub fn get_size(&self) -> f32 {
180        self.size
181    }
182    pub fn attrs(&self) -> impl DoubleEndedIterator<Item = (&String, &String)> {
183        self.attributes.iter()
184    }
185
186    // -- setters
187    pub fn anchor(mut self, anchor: TextAnchor) -> Self {
188        self.anchor = anchor;
189        self
190    }
191    pub fn color<S: Display>(mut self, color: S) -> Self {
192        self.color = color.to_string();
193        self
194    }
195    pub fn family<S: Display>(mut self, family: S) -> Self {
196        self.family = family.to_string();
197        self
198    }
199    pub fn opacity<I: Into<f64>>(mut self, new_opacity: I) -> Self {
200        self.opacity = new_opacity.into() as f32;
201        self
202    }
203    pub fn size<I: Into<f64>>(mut self, new_size: I) -> Self {
204        self.size = new_size.into() as f32;
205        self
206    }
207    /// Sets an attribute on the `<text>` element.
208    pub fn set<K: Display, V: Display>(mut self, attr: K, value: V) -> Self {
209        self.attributes.insert(attr.to_string(), value.to_string());
210        self
211    }
212}
213
214#[derive(Clone, Debug, Default, Deserialize, Serialize)]
215#[serde(rename_all = "kebab-case")]
216pub struct FrameStyle {
217    enabled: bool,
218    color: String,
219    opacity: f32,
220    thickness: f32,
221    attributes: BTreeMap<String, String>,
222}
223impl FrameStyle {
224    // -- getters
225    pub fn enabled(&self) -> bool {
226        self.enabled
227    }
228    pub fn get_color(&self) -> &str {
229        &self.color
230    }
231    pub fn get_opacity(&self) -> f32 {
232        self.opacity
233    }
234    pub fn get_thickness(&self) -> f32 {
235        self.thickness
236    }
237    pub fn attrs(&self) -> impl DoubleEndedIterator<Item = (&String, &String)> {
238        self.attributes.iter()
239    }
240
241    // -- setters
242    pub fn disable(mut self) -> Self {
243        self.enabled = false;
244        self
245    }
246    pub fn enable(mut self) -> Self {
247        self.enabled = true;
248        self
249    }
250    pub fn color<S: Display>(mut self, color: S) -> Self {
251        self.color = color.to_string();
252        self
253    }
254    pub fn opacity<I: Into<f64>>(mut self, new_opacity: I) -> Self {
255        self.opacity = new_opacity.into() as f32;
256        self
257    }
258    pub fn thickness<I: Into<f64>>(mut self, new_thickness: I) -> Self {
259        self.thickness = new_thickness.into() as f32;
260        self
261    }
262    /// Sets an attribute on `<rect>` or `<circle>` element.
263    pub fn set<K: Display, V: Display>(mut self, attr: K, value: V) -> Self {
264        self.attributes.insert(attr.to_string(), value.to_string());
265        self
266    }
267}
268
269#[derive(Clone, Debug, Deserialize, Serialize)]
270#[serde(rename_all = "kebab-case")]
271pub struct LineStyle {
272    arrowsize: f32,
273    color: String,
274    opacity: f32,
275    thickness: f32,
276    attributes: BTreeMap<String, String>,
277}
278impl LineStyle {
279    // -- getters
280    pub fn get_arrowsize(&self) -> f32 {
281        self.arrowsize
282    }
283    pub fn get_color(&self) -> &str {
284        &self.color
285    }
286    pub fn get_opacity(&self) -> f32 {
287        self.opacity
288    }
289    pub fn get_thickness(&self) -> f32 {
290        self.thickness
291    }
292    pub fn attrs(&self) -> impl DoubleEndedIterator<Item = (&String, &String)> {
293        self.attributes.iter()
294    }
295
296    // -- setters
297    pub fn arrowsize<I: Into<f64>>(mut self, new_arrowsize: I) -> Self {
298        self.arrowsize = new_arrowsize.into() as f32;
299        self
300    }
301    pub fn color<S: Display>(mut self, color: S) -> Self {
302        self.color = color.to_string();
303        self
304    }
305    pub fn opacity<I: Into<f64>>(mut self, new_opacity: I) -> Self {
306        self.opacity = new_opacity.into() as f32;
307        self
308    }
309    pub fn thickness<I: Into<f64>>(mut self, new_thickness: I) -> Self {
310        self.thickness = new_thickness.into() as f32;
311        self
312    }
313    /// Sets an attribute on the `<path>` element.
314    pub fn set<K: Display, V: Display>(mut self, attr: K, value: V) -> Self {
315        self.attributes.insert(attr.to_string(), value.to_string());
316        self
317    }
318}
319
320#[derive(Copy, Clone, Default, Deserialize, Serialize)]
321#[serde(rename_all = "kebab-case")]
322pub enum TextAnchor {
323    #[default]
324    Start,
325    Middle,
326    End,
327}
328impl Debug for TextAnchor {
329    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330        let anchor = match self {
331            Self::Start => "start",
332            Self::Middle => "middle",
333            Self::End => "end",
334        };
335        write!(f, "{anchor}")
336    }
337}
338
339// -- shared: traits
340pub trait RectStyling {
341    // -- getters
342    fn get_background_color(&self) -> &str;
343    fn get_background_opacity(&self) -> f32;
344    fn get_border_radius(&self) -> f32;
345    fn get_padding(&self) -> f32;
346    fn get_margin(&self) -> f32;
347    fn get_height(&self) -> Option<f32>;
348    fn get_width(&self) -> Option<f32>;
349
350    // -- builder
351    fn background_color<S: Display>(self, color: S) -> Self;
352    fn background_opacity<I: Into<f64>>(self, opacity: I) -> Self;
353    fn border_radius<I: Into<f64>>(self, radius: I) -> Self;
354    fn padding<I: Into<f64>>(self, padding: I) -> Self;
355    fn margin<I: Into<f64>>(self, margin: I) -> Self;
356    /// Sets minimal height.
357    fn height<I: Into<f64>>(self, height: I) -> Self;
358    /// Sets minimal width.
359    fn width<I: Into<f64>>(self, width: I) -> Self;
360}