Skip to content

Chart View

The navi-chart crate provides ChartView<P, T> — a chart component that manages multiple Navi indicators, handles user interaction, and renders through a platform-agnostic drawing interface. The provider P determines how scripts are executed and where data comes from.

LocalChartProviderCustom ChartProvider
Script executionLocal — runs the VM in-processRemote — your backend streams events
Provider traitChartProvider (Script = String)ChartProvider (Script = your type)
DependenciesRequires the local feature (pulls in navi-vm)No VM dependency — always available
Use caseDesktop apps, CLI tools, playgroundWeb frontends, server-side execution

Cargo Features

toml
[dependencies]
# Remote/custom provider only (no VM):
navi-chart = { git = "https://github.com/longbridge/navi.git" }

# With LocalChartProvider (in-process VM):
navi-chart = { git = "https://github.com/longbridge/navi.git", features = ["local"] }
FeatureWhat it enables
(default — none)ChartView + ChartProvider trait, no VM.
localAdds LocalChartProvider and pulls in navi-vm.

Shared Concepts

Both usage patterns share the same rendering, interaction, and configuration APIs.

Platform

The Platform trait decouples the chart engine from the host runtime. Pass an implementation when constructing ChartView — the engine uses it for async task scheduling, periodic timers, the current time, and optional clipboard access.

MethodDefaultDescription
spawn(fut)Spawn a fire-and-forget future on the host runtime.
interval(interval_ms)Start a periodic timer that yields () every interval_ms ms. Returns an IntervalStream; dropping the stream cancels the timer (RAII).
now_ms()Return the current time in milliseconds. The epoch is host-defined; the engine only uses differences.
double_click_threshold_ms()500.0Maximum gap (ms) between two clicks to be recognised as a double-click. Override to read the OS preference.
write_clipboard(text)no-opWrite text to the system clipboard asynchronously.
read_clipboard()NoneRead text from the system clipboard asynchronously. Returns None if access is denied or the clipboard is empty.

The three required methods are spawn, interval, and now_ms. All others have defaults.

Reference implementation: The web playground's WebPlatform uses wasm_bindgen_futures::spawn_local, setInterval, and js_sys::Date::now().

DrawingContext

Implement the DrawingContext trait for your rendering platform (Canvas 2D, Skia, Cairo, etc.). All coordinates and sizes are in logical/CSS pixels.

Reference implementation: See the Canvas 2D backend used in the web playground.

Canvas

MethodWhat it should do
clear()Erase all pixels to fully transparent (or the platform background). Called once at the start of each frame.

Colors & Styles

These methods set persistent state that applies to subsequent draw calls.

MethodWhat it should do
set_fill_color(color)Set the fill paint to a solid RGBA color. Used before fill, fill_rect, fill_text.
set_stroke_color(color)Set the stroke paint to a solid RGBA color. Used before stroke, stroke_rect.
set_line_width(width)Set the stroke line width in pixels.
set_line_dash(pattern)Set the dash pattern as alternating drawn/gap lengths (e.g., &[6.0, 3.0] = 6 px on, 3 px off). Pass an empty slice to return to a solid line.
set_fill_linear_gradient(x0,y0,x1,y1,stops)Set the fill paint to a linear gradient from (x0,y0) to (x1,y1). stops is a list of (offset, Color) where offset ∈ [0.0, 1.0]. Subsequent fill or fill_rect calls use this gradient.

Text

MethodWhat it should do
set_font(size, formatting, family)Set the active font. size is in CSS pixels. formatting is TextFormatting::Normal, Bold, or Italic. family is FontFamily::Default (sans-serif) or FontFamily::Monospace.
set_text_align(align)Set horizontal text alignment relative to the x coordinate passed to fill_text. Left = x is the left edge, Center = x is the midpoint, Right = x is the right edge.
set_text_baseline(baseline)Set vertical text baseline relative to the y coordinate passed to fill_text. Top = y is the top of the em box, Middle = y is the vertical center, Bottom = y is the bottom, Alphabetic = y is the standard alphabetic baseline.
fill_text(text, x, y)Draw text at pixel position (x, y) using the current font, fill color, alignment, and baseline.
measure_text(text) -> f64Return the rendered width of text in pixels using the current font settings. Must be accurate — the chart uses this for label placement and overlap detection.
font_metrics() -> FontMetricsReturn the vertical metrics (ascent and descent in pixels from the alphabetic baseline) for the current font. Used for precise multi-line text layout. The default implementation returns zeros; backends that can query real font metrics should override it. Canvas 2D backends should call measureText("M") and read fontBoundingBoxAscent / fontBoundingBoxDescent.

Paths

Paths are built with begin_path / move_to / line_to / close_path / arc / arc_to, then painted with stroke or fill. This is the standard Canvas 2D path model.

MethodWhat it should do
begin_path()Discard any previously accumulated path and start a new empty path.
move_to(x, y)Move the current point to (x, y) without drawing anything. Starts a new sub-path.
line_to(x, y)Append a straight line segment from the current point to (x, y), then update the current point.
close_path()Append a straight line from the current point back to the start of the current sub-path, then close it.
arc(x, y, radius)Append a full circle (0 to 2π) centered at (x, y) with the given radius.
arc_to(x1,y1,x2,y2,radius)Append a circular arc defined by two tangent lines (current point → (x1,y1) and (x1,y1)(x2,y2)) with the given radius. Equivalent to Canvas 2D arcTo. Used for rounded corners.
stroke()Stroke the current path with the current stroke color and line width.
fill()Fill the interior of the current path with the current fill paint (color or gradient).

Shapes

Convenience methods that don't require building a path manually.

MethodWhat it should do
fill_rect(rect)Fill the rectangle rect with the current fill paint. Equivalent to begin_path + rect + fill.
stroke_rect(rect)Stroke the outline of rect with the current stroke color and line width.

Clipping (optional)

MethodWhat it should do
set_clip(rect)Some(rect) restricts all subsequent drawing to the given rectangle. A second set_clip(Some(other)) replaces the previous clip (no intersection). None removes the clip and restores unrestricted drawing. The default implementation is a no-op — override it if your backend supports clipping. Canvas 2D backends should use save/clip/restore internally.

ChartEvent

Action events are accumulated inside the chart view and retrieved with poll_event() after each interaction call. Events carry only a ScriptId or AnnotationId; use helper methods to look up associated data once the borrow is released.

EventDescription
EditScript(ScriptId)User clicked the ✎ edit button on a script's legend
RemoveScript(ScriptId)User clicked the ✕ remove button
ShowError(ScriptId)User clicked the ⚠ error icon — call cv.script_error(id) to retrieve the error
ConfigureScript(ScriptId)User clicked the ⚙ configure button — call cv.script_inputs_effective(id) to retrieve inputs
DoubleClick(ChartElement)User double-clicked a series graph or candlestick bar
SelectionChanged(Option<ChartElement>)The selected element changed or was cleared. ChartElement is either Bar(usize), Series { script_id, graph_id }, or Annotation(AnnotationId).
AnnotationCreated(AnnotationId)A new annotation was added — either by the user completing a drawing tool gesture, or by a direct add_annotation call
AnnotationUpdated(AnnotationId)An annotation's spec was replaced via update_annotation
AnnotationDeleted(AnnotationId)An annotation was removed via remove_annotation, clear_annotations, or the user pressing Delete while one is selected
rust
// After any interaction method, poll and handle pending events:
cv.on_mouse_up(pos);
while let Some(event) = cv.poll_event() {
    match event {
        ChartEvent::EditScript(id) => { /* open editor */ }
        ChartEvent::RemoveScript(id) => { /* confirm and remove */ }
        ChartEvent::ShowError(id) => {
            let error = cv.script_error(id).unwrap();
            // display error
        }
        ChartEvent::ConfigureScript(id) => {
            let inputs = cv.script_inputs_effective(id).unwrap();
            // open config dialog
        }
        ChartEvent::DoubleClick(element) => { /* handle */ }
        ChartEvent::SelectionChanged(sel) => { /* handle */ }
        ChartEvent::AnnotationCreated(id) => { /* persist new annotation */ }
        ChartEvent::AnnotationUpdated(id) => { /* update persisted annotation */ }
        ChartEvent::AnnotationDeleted(id) => { /* remove from persistence */ }
        _ => {}
    }
}

ToggleVisibility is handled internally — the chart toggles visibility and re-renders automatically.

ScriptConfig

ScriptConfig manages script visual overrides and input value overrides:

rust
pub struct ScriptConfig {
    /// Visual overrides per series graph (colors, styles, visibility).
    pub series_overrides: HashMap<SeriesGraphId, SeriesGraphOverride>,
    /// Input value overrides. Key = input index, value = JSON value.
    pub input_values: HashMap<usize, serde_json::Value>,
}

See the ChartView page for the complete configuration UI guide.

ScriptId

ScriptId is an opaque identifier returned when adding a script. Use it to query, configure, or remove scripts.

Rendering & Interaction

All rendering and interaction APIs are documented in the sub-pages:

Released under the MIT License.