1use crate::core::{Color, Rect};
4use crate::ontology::{
5 AgentAction, AgentCapability, Discoverable, SemanticRole, UiNode, WidgetSchema,
6};
7use crate::paint::Painter;
8use crate::widget::Widget;
9
10pub struct ProgressBar {
12 pub id: String,
13 pub progress: Option<f32>,
15 bg_color: Option<Color>,
16 fg_color: Option<Color>,
17 corner_radius: Option<f32>,
18}
19
20impl ProgressBar {
21 #[must_use]
23 pub fn new(id: impl Into<String>, progress: f32) -> Self {
24 Self {
25 id: id.into(),
26 progress: Some(progress.clamp(0.0, 1.0)),
27 bg_color: None,
28 fg_color: None,
29 corner_radius: None,
30 }
31 }
32
33 #[must_use]
35 pub fn indeterminate(id: impl Into<String>) -> Self {
36 Self {
37 id: id.into(),
38 progress: None,
39 bg_color: None,
40 fg_color: None,
41 corner_radius: None,
42 }
43 }
44
45 #[must_use]
46 pub fn bg(mut self, color: Color) -> Self {
47 self.bg_color = Some(color);
48 self
49 }
50
51 #[must_use]
52 pub fn fg(mut self, color: Color) -> Self {
53 self.fg_color = Some(color);
54 self
55 }
56
57 #[must_use]
58 pub fn rounded(mut self, radius: f32) -> Self {
59 self.corner_radius = Some(radius);
60 self
61 }
62}
63
64impl Widget for ProgressBar {
65 fn draw(&self, painter: &mut dyn Painter, area: Rect) {
66 let radius = self.corner_radius.unwrap_or(3.0);
67 let track_bg = self.bg_color.unwrap_or(Color::rgba(0.2, 0.2, 0.25, 1.0));
69 painter.fill_rect(area, track_bg, radius);
70
71 let fill_width = match self.progress {
73 Some(p) => area.width * p,
74 None => area.width * 0.3, };
76 let fill = Rect::new(area.x, area.y, fill_width, area.height);
77 let fill_color = self.fg_color.unwrap_or(Color::rgba(0.2, 0.6, 1.0, 1.0));
78 painter.fill_rect(fill, fill_color, radius);
79 }
80
81 fn ui_node(&self) -> UiNode {
82 UiNode::new("ProgressBar", SemanticRole::Progress).with_id(&self.id)
83 }
84}
85
86impl Discoverable for ProgressBar {
87 fn schema(&self) -> WidgetSchema {
88 WidgetSchema::new(
89 "ProgressBar",
90 "A progress indicator",
91 SemanticRole::Progress,
92 )
93 }
94
95 fn capabilities(&self) -> Vec<AgentCapability> {
96 vec![]
97 }
98
99 fn actions(&self) -> Vec<AgentAction> {
100 vec![AgentAction::simple(
101 "set_progress",
102 "Set progress 0.0–1.0",
103 true,
104 )]
105 }
106
107 fn semantic_role(&self) -> SemanticRole {
108 SemanticRole::Progress
109 }
110
111 fn agent_state(&self) -> serde_json::Value {
112 serde_json::json!({
113 "progress": self.progress,
114 "determinate": self.progress.is_some(),
115 })
116 }
117
118 fn execute_action(
119 &mut self,
120 action: &str,
121 params: &serde_json::Value,
122 ) -> Result<serde_json::Value, String> {
123 match action {
124 "set_progress" => {
125 if let Some(v) = params.get("value").and_then(|v| v.as_f64()) {
126 let p = (v as f32).clamp(0.0, 1.0);
127 self.progress = Some(p);
128 Ok(serde_json::json!({ "progress": p }))
129 } else {
130 Err("Missing 'value' parameter".into())
131 }
132 }
133 _ => Err(format!("Unknown action: {action}")),
134 }
135 }
136
137 fn agent_id(&self) -> Option<&str> {
138 Some(&self.id)
139 }
140}