plotly/layout/annotation.rs
1use plotly_derive::FieldSetter;
2use serde::{Serialize, Serializer};
3
4use crate::color::Color;
5use crate::common::{Anchor, Font, Label};
6use crate::layout::{HAlign, VAlign};
7use crate::private::NumOrString;
8
9#[derive(Serialize, Debug, Clone)]
10#[serde(rename_all = "lowercase")]
11pub enum ArrowSide {
12 End,
13 Start,
14 #[serde(rename = "end+start")]
15 StartEnd,
16 None,
17}
18
19#[derive(Debug, Clone)]
20pub enum ClickToShow {
21 False,
22 OnOff,
23 OnOut,
24}
25
26impl Serialize for ClickToShow {
27 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28 where
29 S: Serializer,
30 {
31 match *self {
32 Self::False => serializer.serialize_bool(false),
33 Self::OnOff => serializer.serialize_str("onoff"),
34 Self::OnOut => serializer.serialize_str("onout"),
35 }
36 }
37}
38
39#[serde_with::skip_serializing_none]
40#[derive(Serialize, Debug, Clone, FieldSetter)]
41pub struct Annotation {
42 /// Determines whether or not this annotation is visible.
43 visible: Option<bool>,
44 /// Sets the text associated with this annotation. Plotly uses a subset of
45 /// HTML tags to do things like newline (<br>), bold (<b></b>), italics
46 /// (<i></i>), hyperlinks (<a href='...'></a>). Tags <em></em>, <sup></sup>,
47 /// <sub></sub> <span></span> are also supported.
48 text: Option<String>,
49 /// Sets the angle at which the `text` is drawn with respect to the
50 /// horizontal.
51 #[serde(rename = "textangle")]
52 text_angle: Option<f64>,
53 /// Sets the annotation text font.
54 font: Option<Font>,
55 /// Sets an explicit width for the text box. null (default) lets the text
56 /// set the box width. Wider text will be clipped. There is no automatic
57 /// wrapping; use <br> to start a new line.
58 width: Option<f64>,
59 /// Sets an explicit height for the text box. null (default) lets the text
60 /// set the box height. Taller text will be clipped.
61 height: Option<f64>,
62 /// Sets the opacity of the annotation (text + arrow).
63 opacity: Option<f64>,
64 /// Sets the horizontal alignment of the `text` within the box. Has an
65 /// effect only if `text` spans two or more lines (i.e. `text` contains
66 /// one or more <br> HTML tags) or if an explicit width is set to
67 /// override the text width.
68 align: Option<HAlign>,
69 /// Sets the vertical alignment of the `text` within the box. Has an effect
70 /// only if an explicit height is set to override the text height.
71 valign: Option<VAlign>,
72 /// Sets the background color of the annotation.
73 #[serde(rename = "bgcolor")]
74 background_color: Option<Box<dyn Color>>,
75 /// Sets the color of the border enclosing the annotation `text`.
76 #[serde(rename = "bordercolor")]
77 border_color: Option<Box<dyn Color>>,
78 /// Sets the padding (in px) between the `text` and the enclosing border.
79 #[serde(rename = "borderpad")]
80 border_pad: Option<f64>,
81 /// Sets the width (in px) of the border enclosing the annotation `text`.
82 #[serde(rename = "borderwidth")]
83 border_width: Option<f64>,
84 /// Determines whether or not the annotation is drawn with an arrow. If
85 /// "True", `text` is placed near the arrow's tail. If "False", `text`
86 /// lines up with the `x` and `y` provided.
87 #[serde(rename = "showarrow")]
88 show_arrow: Option<bool>,
89 /// Sets the color of the annotation arrow.
90 #[serde(rename = "arrowcolor")]
91 arrow_color: Option<Box<dyn Color>>,
92 /// Sets the end annotation arrow head style. Integer between or equal to 0
93 /// and 8.
94 #[serde(rename = "arrowhead")]
95 arrow_head: Option<u8>,
96 /// Sets the start annotation arrow head style. Integer between or equal to
97 /// 0 and 8.
98 #[serde(rename = "startarrowhead")]
99 start_arrow_head: Option<u8>,
100 /// Sets the annotation arrow head position.
101 #[serde(rename = "arrowside")]
102 arrow_side: Option<ArrowSide>,
103 /// Sets the size of the end annotation arrow head, relative to
104 /// `arrowwidth`. A value of 1 (default) gives a head about 3x as wide
105 /// as the line.
106 #[serde(rename = "arrowsize")]
107 arrow_size: Option<f64>,
108 /// Sets the size of the start annotation arrow head, relative to
109 /// `arrowwidth`. A value of 1 (default) gives a head about 3x as wide
110 /// as the line.
111 #[serde(rename = "startarrowsize")]
112 start_arrow_size: Option<f64>,
113 /// Sets the width (in px) of annotation arrow line.
114 #[serde(rename = "arrowwidth")]
115 arrow_width: Option<f64>,
116 /// Sets a distance, in pixels, to move the end arrowhead away from the
117 /// position it is pointing at, for example to point at the edge of a
118 /// marker independent of zoom. Note that this shortens the arrow from
119 /// the `ax` / `ay` vector, in contrast to `xshift` / `yshift` which
120 /// moves everything by this amount.
121 #[serde(rename = "standoff")]
122 stand_off: Option<f64>,
123 /// Sets a distance, in pixels, to move the start arrowhead away from the
124 /// position it is pointing at, for example to point at the edge of a
125 /// marker independent of zoom. Note that this shortens the arrow from
126 /// the `ax` / `ay` vector, in contrast to `xshift` / `yshift`
127 /// which moves everything by this amount.
128 #[serde(rename = "startstandoff")]
129 start_stand_off: Option<f64>,
130 /// Sets the x component of the arrow tail about the arrow head. If `axref`
131 /// is `pixel`, a positive (negative) component corresponds to an arrow
132 /// pointing from right to left (left to right). If `axref` is an axis,
133 /// this is an absolute value on that axis, like `x`, NOT a
134 /// relative value.
135 ax: Option<NumOrString>,
136 /// Sets the y component of the arrow tail about the arrow head. If `ayref`
137 /// is `pixel`, a positive (negative) component corresponds to an arrow
138 /// pointing from bottom to top (top to bottom). If `ayref` is an axis,
139 /// this is an absolute value on that axis, like `y`, NOT a
140 /// relative value.
141 ay: Option<NumOrString>,
142 /// Indicates in what terms the tail of the annotation (ax,ay) is specified.
143 /// If `pixel`, `ax` is a relative offset in pixels from `x`. If set to
144 /// an x axis id (e.g. "x" or "x2"), `ax` is specified in the same terms
145 /// as that axis. This is useful for trendline annotations which
146 /// should continue to indicate the correct trend when zoomed.
147 #[serde(rename = "axref")]
148 ax_ref: Option<String>,
149 /// Indicates in what terms the tail of the annotation (ax,ay) is specified.
150 /// If `pixel`, `ay` is a relative offset in pixels from `y`. If set to
151 /// a y axis id (e.g. "y" or "y2"), `ay` is specified in the same terms
152 /// as that axis. This is useful for trendline annotations which
153 /// should continue to indicate the correct trend when zoomed.
154 #[serde(rename = "ayref")]
155 ay_ref: Option<String>,
156 /// Sets the annotation's x coordinate axis. If set to an x axis id (e.g.
157 /// "x" or "x2"), the `x` position refers to an x coordinate If set to
158 /// "paper", the `x` position refers to the distance from the left side
159 /// of the plotting area in normalized coordinates where 0 (1)
160 /// corresponds to the left (right) side.
161 #[serde(rename = "xref")]
162 x_ref: Option<String>,
163 /// Sets the annotation's x position. If the axis `type` is "log", then you
164 /// must take the log of your desired range. If the axis `type` is
165 /// "date", it should be date strings, like date data, though Date
166 /// objects and unix milliseconds will be accepted and converted to strings.
167 /// If the axis `type` is "category", it should be numbers, using the scale
168 /// where each category is assigned a serial number from zero in the
169 /// order it appears.
170 x: Option<NumOrString>,
171 /// Sets the text box's horizontal position anchor This anchor binds the `x`
172 /// position to the "left", "center" or "right" of the annotation. For
173 /// example, if `x` is set to 1, `xref` to "paper" and `xanchor` to
174 /// "right" then the right-most portion of the annotation lines up with
175 /// the right-most edge of the plotting area. If "auto", the anchor is
176 /// equivalent to "center" for data-referenced annotations or if there
177 /// is an arrow, whereas for paper-referenced with no arrow, the anchor
178 /// picked corresponds to the closest side.
179 #[serde(rename = "xanchor")]
180 x_anchor: Option<Anchor>,
181 /// Shifts the position of the whole annotation and arrow to the right
182 /// (positive) or left (negative) by this many pixels.
183 #[serde(rename = "xshift")]
184 x_shift: Option<f64>,
185 /// Sets the annotation's y coordinate axis. If set to an y axis id (e.g.
186 /// "y" or "y2"), the `y` position refers to an y coordinate If set to
187 /// "paper", the `y` position refers to the distance from the bottom of
188 /// the plotting area in normalized coordinates where 0 (1) corresponds
189 /// to the bottom (top).
190 #[serde(rename = "yref")]
191 y_ref: Option<String>,
192 /// Sets the annotation's y position. If the axis `type` is "log", then you
193 /// must take the log of your desired range. If the axis `type` is
194 /// "date", it should be date strings, like date data, though Date
195 /// objects and unix milliseconds will be accepted and converted to strings.
196 /// If the axis `type` is "category", it should be numbers, using the
197 /// scale where each category is assigned a serial number from zero in
198 /// the order it appears.
199 y: Option<NumOrString>,
200 /// Sets the text box's vertical position anchor This anchor binds the `y`
201 /// position to the "top", "middle" or "bottom" of the annotation. For
202 /// example, if `y` is set to 1, `yref` to "paper" and `yanchor` to
203 /// "top" then the top-most portion of the annotation lines up with the
204 /// top-most edge of the plotting area. If "auto", the anchor is equivalent
205 /// to "middle" for data-referenced annotations or if there is an arrow,
206 /// whereas for paper-referenced with no arrow, the anchor picked
207 /// corresponds to the closest side.
208 #[serde(rename = "yanchor")]
209 y_anchor: Option<Anchor>,
210 /// Shifts the position of the whole annotation and arrow up (positive) or
211 /// down (negative) by this many pixels.
212 #[serde(rename = "yshift")]
213 y_shift: Option<f64>,
214 /// Makes this annotation respond to clicks on the plot. If you click a data
215 /// point that exactly matches the `x` and `y` values of this
216 /// annotation, and it is hidden (visible: false), it will appear. In
217 /// "onoff" mode, you must click the same point again to make it disappear,
218 /// so if you click multiple points, you can show multiple annotations.
219 /// In "onout" mode, a click anywhere else in the plot (on another data
220 /// point or not) will hide this annotation. If you need to show/hide
221 /// this annotation in response to different `x` or `y` values, you can set
222 /// `xclick` and/or `yclick`. This is useful for example to label the side
223 /// of a bar. To label markers though, `standoff` is preferred over
224 /// `xclick` and `yclick`.
225 #[serde(rename = "clicktoshow")]
226 click_to_show: Option<ClickToShow>,
227 /// Toggle this annotation when clicking a data point whose `x` value is
228 /// `xclick` rather than the annotation's `x` value.
229 #[serde(rename = "xclick")]
230 x_click: Option<NumOrString>,
231 /// Toggle this annotation when clicking a data point whose `y` value is
232 /// `yclick` rather than the annotation's `y` value.
233 #[serde(rename = "yclick")]
234 y_click: Option<NumOrString>,
235 /// Sets text to appear when hovering over this annotation. If omitted or
236 /// blank, no hover label will appear.
237 #[serde(rename = "hovertext")]
238 hover_text: Option<String>,
239 /// Label displayed on mouse hover.
240 #[serde(rename = "hoverlabel")]
241 hover_label: Option<Label>,
242 /// Determines whether the annotation text box captures mouse move and click
243 /// events, or allows those events to pass through to data points in the
244 /// plot that may be behind the annotation. By default `captureevents`
245 /// is "false" unless `hovertext` is provided. If you use the event
246 /// `plotly_clickannotation` without `hovertext` you must explicitly enable
247 /// `captureevents`.
248 #[serde(rename = "captureevents")]
249 capture_events: Option<bool>,
250 /// When used in a template, named items are created in the output figure in
251 /// addition to any items the figure already has in this array. You can
252 /// modify these items in the output figure by making your own item with
253 /// `templateitemname` matching this `name` alongside your modifications
254 /// (including `visible: false` or `enabled: false` to hide it). Has no
255 /// effect outside of a template.
256 name: Option<String>,
257 /// Used to refer to a named item in this array in the template. Named items
258 /// from the template will be created even without a matching item in
259 /// the input figure, but you can modify one by making an item with
260 /// `templateitemname` matching its `name`, alongside your modifications
261 /// (including `visible: false` or `enabled: false` to hide it). If there is
262 /// no template or no matching item, this item will be hidden unless you
263 /// explicitly show it with `visible: true`.
264 #[serde(rename = "templateitemname")]
265 template_item_name: Option<String>,
266}
267
268impl Annotation {
269 pub fn new() -> Self {
270 Default::default()
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use serde_json::{json, to_value};
277
278 use super::*;
279 use crate::common::{Anchor, Font, Label};
280
281 #[test]
282 fn serialize_click_to_show() {
283 assert_eq!(to_value(ClickToShow::False).unwrap(), json!(false));
284 assert_eq!(to_value(ClickToShow::OnOff).unwrap(), json!("onoff"));
285 assert_eq!(to_value(ClickToShow::OnOut).unwrap(), json!("onout"));
286 }
287
288 #[test]
289 fn serialize_arrow_side() {
290 assert_eq!(to_value(ArrowSide::End).unwrap(), json!("end"));
291 assert_eq!(to_value(ArrowSide::Start).unwrap(), json!("start"));
292 assert_eq!(to_value(ArrowSide::StartEnd).unwrap(), json!("end+start"));
293 assert_eq!(to_value(ArrowSide::None).unwrap(), json!("none"));
294 }
295
296 #[test]
297 fn serialize_annotation() {
298 let annotation = Annotation::new()
299 .align(HAlign::Center)
300 .arrow_color("#464646")
301 .arrow_head(2)
302 .arrow_size(123.4)
303 .arrow_side(ArrowSide::End)
304 .arrow_width(111.1)
305 .ax("ax")
306 .ax_ref("axref")
307 .ay("ay")
308 .ay_ref("ayref")
309 .background_color("#123456")
310 .border_color("#456789")
311 .border_pad(500.)
312 .border_width(1000.)
313 .capture_events(false)
314 .click_to_show(ClickToShow::OnOff)
315 .font(Font::new())
316 .height(6.)
317 .hover_label(Label::new())
318 .hover_text("hovertext")
319 .name("name")
320 .opacity(0.01)
321 .show_arrow(false)
322 .stand_off(999.9)
323 .start_arrow_head(0)
324 .start_stand_off(8.8)
325 .start_arrow_size(456.7)
326 .template_item_name("templateitemname")
327 .text("text")
328 .text_angle(5.)
329 .valign(VAlign::Middle)
330 .visible(true)
331 .width(4.)
332 .x_ref("xref")
333 .x("x")
334 .x_anchor(Anchor::Auto)
335 .x_click("xclick")
336 .x_shift(4.0)
337 .y_ref("yref")
338 .y("y")
339 .y_anchor(Anchor::Bottom)
340 .y_click("yclick")
341 .y_shift(6.3);
342
343 let expected = json!({
344 "visible": true,
345 "text": "text",
346 "textangle": 5.0,
347 "font": {},
348 "width": 4.0,
349 "height": 6.0,
350 "opacity": 0.01,
351 "align": "center",
352 "valign": "middle",
353 "bgcolor": "#123456",
354 "bordercolor": "#456789",
355 "borderpad": 500.0,
356 "borderwidth": 1000.0,
357 "showarrow": false,
358 "arrowcolor": "#464646",
359 "arrowhead": 2,
360 "startarrowhead": 0,
361 "arrowside": "end",
362 "arrowsize": 123.4,
363 "startarrowsize": 456.7,
364 "arrowwidth": 111.1,
365 "standoff": 999.9,
366 "startstandoff": 8.8,
367 "ax": "ax",
368 "ay": "ay",
369 "x": "x",
370 "y": "y",
371 "axref": "axref",
372 "ayref": "ayref",
373 "xref": "xref",
374 "yref": "yref",
375 "xanchor": "auto",
376 "yanchor": "bottom",
377 "xshift": 4.0,
378 "yshift": 6.3,
379 "clicktoshow": "onoff",
380 "xclick": "xclick",
381 "yclick": "yclick",
382 "hovertext": "hovertext",
383 "hoverlabel": {},
384 "captureevents": false,
385 "name": "name",
386 "templateitemname": "templateitemname",
387 });
388
389 assert_eq!(to_value(annotation).unwrap(), expected);
390 }
391}