egui_charts/ui/dialogs/indicator_dialog/data.rs
1//! Study browser entries and saved favorites.
2//!
3//! Use [`IndicatorPreferences`] to retain favorite factory names and
4//! [`IndicatorInfo`] to describe studies in a picker.
5
6use crate::studies::IndicatorFactory;
7
8/// Host-owned indicator browser settings. Save these alongside the user's other settings
9/// after [`IndicatorDialogAction::ToggleFavorite`](super::IndicatorDialogAction::ToggleFavorite),
10/// and restore them with [`IndicatorDialog::set_preferences`](super::IndicatorDialog::set_preferences).
11/// Unknown study names are retained so temporarily unavailable host studies keep their stars.
12#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
13pub struct IndicatorPreferences {
14 /// Favourite factory names, sorted for stable saved documents.
15 #[serde(default)]
16 pub favorites: std::collections::BTreeSet<String>,
17}
18
19/// Indicator information
20#[derive(Debug, Clone)]
21pub struct IndicatorInfo {
22 /// The name the dialog's [`IndicatorFactory`] creates the study under
23 pub id: String,
24 /// Display name.
25 pub name: String,
26 /// Description displayed to the user.
27 pub desc: String,
28 /// Category label used to group the entry in the browser.
29 pub category: String,
30 /// Whether the study draws on the price pane by default.
31 pub is_overlay: bool,
32 /// Display flag for a premium entry; access control belongs to the host.
33 pub is_premium: bool,
34}
35
36impl IndicatorInfo {
37 /// Create a new indicator info entry with the given ID, name, and description
38 pub fn new(id: impl Into<String>, name: impl Into<String>, desc: impl Into<String>) -> Self {
39 Self {
40 id: id.into(),
41 name: name.into(),
42 desc: desc.into(),
43 category: "Technical Analysis".to_string(),
44 is_overlay: false,
45 is_premium: false,
46 }
47 }
48
49 /// Mark this indicator as an overlay (renders on the price chart)
50 pub fn overlay(mut self) -> Self {
51 self.is_overlay = true;
52 self
53 }
54
55 /// Mark this entry as premium for display. Access control belongs to the host.
56 pub fn premium(mut self) -> Self {
57 self.is_premium = true;
58 self
59 }
60
61 /// Every study `factory` creates, sorted by display name, with its description, category
62 /// and overlay flag
63 pub(super) fn from_factory(factory: &IndicatorFactory) -> Vec<IndicatorInfo> {
64 let mut infos: Vec<IndicatorInfo> = factory
65 .list()
66 .into_iter()
67 .filter_map(|id| {
68 let study = factory.create(&id)?;
69 // A description reads "Full name - what it measures", as `Indicator::desc`
70 // shows; one without a full name keeps the registered name.
71 let (name, desc) = study
72 .desc()
73 .split_once(" - ")
74 .unwrap_or((id.as_str(), study.desc()));
75 let mut info = IndicatorInfo::new(id.clone(), name, desc);
76 info.category = factory.category(&id).unwrap_or_default().to_string();
77 info.is_overlay = study.is_overlay();
78 Some(info)
79 })
80 .collect();
81 infos.sort_by_key(|info| info.name.to_lowercase());
82 infos
83 }
84}