Annotations
The AnnotationManagement trait provides programmatic CRUD for annotations — trend lines, shapes, text labels, Fibonacci tools, and more — on any chart view. Both ChartView and ChartView implement this trait, so annotation management code is reusable across both.
Import
use navi_chart::{
ChartView, ChartView, AnnotationManagement, SystemAnnotationManagement,
AnnotationSpec, AnnotationFlags, AnnotationKind, AnnotationPane, ControlPoint,
Stroke, Color, LineStyle, ExtendMode,
};Usage
use navi_chart::{AnnotationManagement, AnnotationSpec, AnnotationId};
fn add_trend_line<C>(cv: &C, t1: i64, p1: f64, t2: i64, p2: f64) -> AnnotationId
where
C: AnnotationManagement,
{
cv.add_annotation(AnnotationSpec {
kind: AnnotationKind::TrendLine {
extend: ExtendMode::None,
stroke: Stroke {
color: Color::from_rgba(41, 98, 255, 255),
width: 2.0,
style: LineStyle::Solid,
},
text: String::new(),
},
points: vec![
ControlPoint { time: t1, price: p1 },
ControlPoint { time: t2, price: p2 },
],
pane: AnnotationPane::Main,
flags: AnnotationFlags::default(), // VISIBLE | SELECTABLE, not locked
})
}
// Works with either view type:
let id = add_trend_line(&local_cv, t1, 150.0, t2, 165.0);
let id = add_trend_line(&remote_cv, t1, 150.0, t2, 165.0);Method Reference
| Method | Description |
|---|---|
add_annotation(spec) | Add a new annotation. Immediately re-renders and pushes AnnotationCreated. Returns a stable, non-zero AnnotationId. |
update_annotation(id, spec) | Replace the full spec of an existing annotation. Re-renders and pushes AnnotationUpdated. |
remove_annotation(id) | Remove an annotation by id. Re-renders and pushes AnnotationDeleted. |
clear_annotations() | Remove all annotations. Pushes AnnotationDeleted for each. |
annotation(id) | Return a reference to an annotation by id, or None if not found. |
annotations() | Return all annotations in z-order (back to front). |
annotation_properties(id) | Return the property descriptors for an annotation, localised to the chart's current locale. Use these to build a style toolbar or properties panel. Returns None if the annotation id is not found. |
get_annotation_property(id, name) | Read a single property by its path name (e.g. "stroke.color", "stroke.width"). Returns None if the id is unknown. |
set_annotation_property(id, name, value) | Write a single property. On VIRTUAL properties (e.g. "colorAll" on a ParallelChannel) the write fans out to all underlying fields. Re-renders immediately. Returns Err(ChartPropertyError) if the id, property name, or value type is invalid — see errors below. |
delete_selected_annotation() | Delete the currently selected annotation. No-op if nothing is selected. Pushes AnnotationDeleted. |
bring_selected_annotation_to_front() | Move the selected annotation to the top of the z-order. No-op if nothing is selected. |
send_selected_annotation_to_back() | Move the selected annotation to the bottom of the z-order. No-op if nothing is selected. |
toggle_selected_annotation_lock() | Toggle the LOCKED flag on the selected annotation (AnnotationFlags::LOCKED). Locked annotations cannot be moved or edited by the user. No-op if nothing is selected. |
copy_selected_annotation() | Copy the selected annotation to the chart's internal clipboard. No-op if nothing is selected. |
cut_selected_annotation() | Copy the selected annotation to the clipboard and then delete it. No-op if nothing is selected. |
paste_annotation() | Paste the clipboard annotation as a new annotation, slightly offset from the original. Pushes AnnotationCreated. No-op if the clipboard is empty. |
has_clipboard_annotation() | Return true if the clipboard contains a copied annotation. |
selected_annotation_bounds() | Return the bounding box of the selected annotation in canvas pixels. Useful for positioning a floating toolbar. Returns None if nothing is selected. |
annotation_bounds(id) | Return the pixel-space AABB of the user annotation with the given id. Uses per-kind accurate geometry (e.g. full pane width for horizontal lines, rendered rect for images, TextBounds for text annotations). Returns None if the id is unknown or the projection is degenerate. |
selected_annotation_is_highlighter() | Return true if the selected annotation is a Highlighter brush stroke. Useful for showing a highlighting-specific toolbar. |
dispatch_context_menu_action(action) | Execute a context menu action string on the selected annotation (e.g. "delete", "lock", "bring-to-front", "send-to-back", "copy", "cut", "paste"). Call this after the user selects an item from the menu populated by context_menu_items(pos). |
annotation_control_point_flags(id) | Return the ControlPointFlags for each control point of annotation id, in order, or None if not found. Use these flags to know which coordinate axes are locked when building a Coordinates editor tab. |
set_annotation_default(tool_id, kind) | Save a per-tool style default for the given drawing tool. The next annotation of that tool created via the drawing flow will use these properties instead of the factory defaults. |
reset_annotation_to_default(id) | Reset an annotation's style properties to factory defaults and clear any saved per-tool default for that tool. Pushes AnnotationUpdated and re-renders. |
Notes on selection-based methods
delete_selected_annotation, bring_selected_annotation_to_front, send_selected_annotation_to_back, toggle_selected_annotation_lock, copy_selected_annotation, cut_selected_annotation, and selected_annotation_bounds all operate on the currently selected annotation. They are no-ops when no annotation is selected — safe to call from toolbar buttons unconditionally.
Notes on annotation_bounds
annotation_bounds(id) and system_annotation_bounds(id) accept an explicit id and return per-kind accurate bounds (not just a control-point AABB). For example, a HorizontalLine returns the full pane width; a Text annotation returns its rendered text box; an Image returns its scaled destination rect.
set_annotation_property errors
set_annotation_property returns Err(ChartPropertyError) when:
- The annotation id is not found.
- The property name does not exist for that annotation kind.
- The value type does not match the property kind.
Example: Properties Toolbar
use navi_chart::{AnnotationManagement, ChartInteraction, PropertyValue, Color};
fn apply_color_from_toolbar<C>(cv: &C, color: Color)
where
C: AnnotationManagement + ChartInteraction,
{
if let Some(ChartElement::Annotation(id)) = cv.selection() {
// Try the virtual property first (fans out to all strokes)
let _ = cv.set_annotation_property(id, "colorAll", PropertyValue::Color(color))
.or_else(|_| cv.set_annotation_property(id, "stroke.color", PropertyValue::Color(color)));
}
}Example: Persistence Round-trip
use navi_chart::{AnnotationManagement, Annotation, AnnotationSpec};
fn save_annotations<C>(cv: &C) -> String
where
C: AnnotationManagement,
{
let annotations: Vec<Annotation> = cv.annotations();
serde_json::to_string(&annotations).unwrap()
}
fn restore_annotations<C>(cv: &C, json: &str)
where
C: AnnotationManagement,
{
let saved: Vec<Annotation> = serde_json::from_str(json).unwrap();
cv.clear_annotations();
for ann in saved {
cv.add_annotation(ann.spec);
}
}AnnotationFlags
AnnotationSpec.flags is a bitflags field that replaces the old visible: bool and locked: bool fields:
| Flag | Default | Description |
|---|---|---|
VISIBLE | ✓ | The annotation is rendered. Clear to hide without deleting. |
LOCKED | ✗ | Control points are not draggable. The annotation is still selectable so the user can unlock it. |
SELECTABLE | ✓ | Clicking the annotation updates the selection and shows handles. When cleared, AnnotationClicked still fires but no selection change occurs. |
AnnotationFlags::default() gives VISIBLE | SELECTABLE (the standard interactive annotation). Construct custom flags with the bitwise operators:
use navi_chart::annotation::AnnotationFlags;
// Hidden, not selectable — a purely decorative overlay
let decorative = AnnotationFlags::VISIBLE;
// Visible, locked, selectable (user can click but not drag)
let read_only = AnnotationFlags::VISIBLE | AnnotationFlags::LOCKED | AnnotationFlags::SELECTABLE;ControlPointFlags
annotation_control_point_flags(id) returns one ControlPointFlags per control point of the annotation. Use these flags when building a Coordinates editor to decide which axis inputs to show or hide.
| Flag | Bit | Effect |
|---|---|---|
LOCK_BAR_INDEX | 0b0001 | The X axis (bar index / time) is fixed for this point. Hide the bar-index input. Set for HorizontalLine (spans the full pane width; anchor bar is irrelevant). |
LOCK_PRICE | 0b0010 | The Y axis (price) is fixed for this point. Hide the price input. |
FOLLOW_FIRST_BAR_INDEX | 0b0100 | During drawing, this point inherits the X value from the first point. |
FOLLOW_FIRST_PRICE | 0b1000 | During drawing, this point inherits the Y value from the first point. |
use navi_chart::{AnnotationManagement, annotation::ControlPointFlags};
fn build_coordinates_tab<C: AnnotationManagement>(cv: &C, id: AnnotationId) {
let annotation = cv.annotation(id).unwrap();
let flags_per_point = cv.annotation_control_point_flags(id).unwrap_or_default();
for (i, point) in annotation.spec.points.iter().enumerate() {
let flags = flags_per_point.get(i).copied().unwrap_or_default();
if !flags.contains(ControlPointFlags::LOCK_BAR_INDEX) {
render_bar_index_input(i, point.time);
}
if !flags.contains(ControlPointFlags::LOCK_PRICE) {
render_price_input(i, point.price);
}
}
}System Annotation Layer
The SystemAnnotationManagement trait provides a parallel API for programmatic overlays (order lines, signals, etc.) that render below user-drawn annotations:
- Excluded from undo/redo,
annotations(), and save snapshots. - Fires
AnnotationCreated/AnnotationUpdated/AnnotationDeleted(same as user annotations). - Dragging a system annotation fires
AnnotationUpdatedbut skips the undo stack. - Keyboard Delete does not affect system annotations — remove them via
remove_system_annotation.
use navi_chart::{SystemAnnotationManagement, AnnotationSpec, AnnotationFlags, AnnotationKind, AnnotationPane, ControlPoint};
// Add an order line
let id = cv.add_system_annotation(AnnotationSpec {
kind: AnnotationKind::HorizontalLine(Default::default()),
points: vec![ControlPoint { time: bar_time, price: order_price }],
pane: AnnotationPane::Main,
flags: AnnotationFlags::VISIBLE | AnnotationFlags::SELECTABLE,
..Default::default()
});
// Update when the order price changes
cv.update_system_annotation(id, updated_spec);
// Remove when the order is filled
cv.remove_system_annotation(id);
// Clear all system annotations
cv.clear_system_annotations();SystemAnnotationManagement method reference
| Method | Description |
|---|---|
add_system_annotation(spec) | Add a system annotation. Fires AnnotationCreated. Returns the allocated AnnotationId. |
update_system_annotation(id, spec) | Replace the spec. Fires AnnotationUpdated. |
remove_system_annotation(id) | Remove by id. Fires AnnotationDeleted. |
clear_system_annotations() | Remove all system annotations. |
system_annotations() | Return all system annotations in z-order. |
system_annotation_bounds(id) | Return the pixel-space AABB of the system annotation with the given id. Same per-kind accuracy as annotation_bounds. Returns None if not found or projection is degenerate. |
For the full AnnotationKind variant list, AnnotationSpec structure, PropertyDescriptor fields, and style sub-structs, see ChartView.