boxmux_lib/components/
choice_content.rs1use crate::components::renderable_content::{
2 ContentEvent, EventResult, EventType, HoverState, RenderableContent, SensitiveZone,
3};
4use crate::model::choice::Choice;
5use crate::Bounds;
6
7pub struct ChoiceContent<'a> {
11 choices: &'a [Choice],
13 _menu_fg_color: &'a Option<String>,
15 _menu_bg_color: &'a Option<String>,
17 _selected_menu_fg_color: &'a Option<String>,
19 _selected_menu_bg_color: &'a Option<String>,
21 _highlighted_menu_fg_color: &'a Option<String>,
23 _highlighted_menu_bg_color: &'a Option<String>,
25}
26
27impl<'a> ChoiceContent<'a> {
28 pub fn new(
30 choices: &'a [Choice],
31 menu_fg_color: &'a Option<String>,
32 menu_bg_color: &'a Option<String>,
33 selected_menu_fg_color: &'a Option<String>,
34 selected_menu_bg_color: &'a Option<String>,
35 highlighted_menu_fg_color: &'a Option<String>,
36 highlighted_menu_bg_color: &'a Option<String>,
37 ) -> Self {
38 Self {
39 choices,
40 _menu_fg_color: menu_fg_color,
41 _menu_bg_color: menu_bg_color,
42 _selected_menu_fg_color: selected_menu_fg_color,
43 _selected_menu_bg_color: selected_menu_bg_color,
44 _highlighted_menu_fg_color: highlighted_menu_fg_color,
45 _highlighted_menu_bg_color: highlighted_menu_bg_color,
46 }
47 }
48
49 fn format_choice_content(&self, choice: &Choice) -> String {
51 if let Some(content) = &choice.content {
52 if choice.waiting {
53 format!("{}...", content)
54 } else {
55 content.clone()
56 }
57 } else {
58 String::new()
59 }
60 }
61
62 fn get_max_choice_width(&self) -> usize {
64 self.choices
65 .iter()
66 .map(|choice| self.format_choice_content(choice).len())
67 .max()
68 .unwrap_or(0)
69 }
70}
71
72impl<'a> RenderableContent for ChoiceContent<'a> {
73 fn get_dimensions(&self) -> (usize, usize) {
75 let max_width = self.get_max_choice_width();
76 let height = self.choices.len();
77 (max_width, height)
78 }
79
80 fn get_raw_content(&self) -> String {
82 self.choices
83 .iter()
84 .map(|choice| choice.id.clone())
85 .collect::<Vec<_>>()
86 .join("\n")
87 }
88
89 fn get_box_relative_sensitive_zones(&self) -> Vec<SensitiveZone> {
91 let mut zones = Vec::new();
92
93 for (idx, choice) in self.choices.iter().enumerate() {
94 zones.push(SensitiveZone {
95 bounds: Bounds::new(0, idx, choice.id.len(), 1), content_id: format!("choice_{}", idx),
97 content_type: crate::components::renderable_content::ContentType::Choice,
98 metadata: Default::default(),
99 });
100 }
101
102 zones
103 }
104
105 fn handle_event(&mut self, event: &ContentEvent) -> EventResult {
108 match event.event_type {
109 EventType::Click => {
110 if let Some(zone_id) = &event.zone_id {
111 if let Some(idx_str) = zone_id.strip_prefix("choice_") {
112 if let Ok(choice_idx) = idx_str.parse::<usize>() {
113 if choice_idx < self.choices.len() {
114 return EventResult::Handled;
116 }
117 }
118 }
119 }
120 EventResult::NotHandled
121 }
122 EventType::Hover => {
123 if let Some(hover_info) = event.hover_info() {
125 match hover_info.state {
126 HoverState::Enter => {
127 EventResult::HandledContinue }
130 HoverState::Leave => {
131 EventResult::HandledContinue
133 }
134 HoverState::Move => {
135 EventResult::HandledContinue
137 }
138 }
139 } else {
140 EventResult::HandledContinue
142 }
143 }
144 EventType::MouseMove => {
145 if let Some(mouse_move) = event.mouse_move_info() {
147 if mouse_move.is_dragging {
148 EventResult::NotHandled } else {
151 EventResult::HandledContinue
153 }
154 } else {
155 EventResult::NotHandled
156 }
157 }
158 EventType::KeyPress => {
159 if let Some(key_info) = event.key_info() {
161 match key_info.key.as_str() {
162 "ArrowUp" | "ArrowDown" | "Enter" | "Space" => {
163 EventResult::Handled }
165 _ => EventResult::NotHandled,
166 }
167 } else {
168 EventResult::NotHandled
169 }
170 }
171 EventType::Focus => {
172 EventResult::StateChanged
174 }
175 EventType::Blur => {
176 EventResult::StateChanged
178 }
179 _ => EventResult::NotHandled,
180 }
181 }
182}