matrix_gui/widgets/
listbox.rs1use embedded_graphics::{draw_target::DrawTarget, geometry::Point, prelude::PixelColor};
8
9use crate::prelude::*;
10
11pub struct ListBox<'a, ID, COL: PixelColor> {
23 region: &'a Region<ID>,
25 items: &'a [&'a str],
27 start_item: u16,
29 font: OptionFont<'a>,
31 align: HorizontalAlign,
33 color: OptionColor<COL>,
35 line_spacing: u8,
37 padding_inner: u8,
39 padding_outer: u8,
41 border_width: u8,
43 selected_item: i32,
45}
46
47impl<'a, ID: WidgetId, COL: PixelColor> ListBox<'a, ID, COL> {
48 pub const fn new(region: &'a Region<ID>, items: &'a [&'a str]) -> ListBox<'a, ID, COL> {
49 ListBox {
50 region,
51 items,
52 start_item: 0,
53 font: OptionFont::none(),
54 align: HorizontalAlign::Left,
55 color: OptionColor::none(),
56 line_spacing: 2,
57 padding_inner: 1,
58 padding_outer: 0,
59 border_width: 1,
60 selected_item: -1,
61 }
62 }
63
64 pub const fn with_start(mut self, start_item: u16) -> Self {
65 self.start_item = start_item;
66 self
67 }
68
69 pub const fn with_font(mut self, font: UiFont<'a>) -> Self {
70 self.font.set_font(font);
71 self
72 }
73
74 pub const fn with_align(mut self, align: HorizontalAlign) -> Self {
75 self.align = align;
76 self
77 }
78
79 pub const fn with_color(mut self, color: COL) -> Self {
80 self.color.set_color(color);
81 self
82 }
83
84 pub const fn with_line_spacing(mut self, spacing: u8) -> Self {
85 self.line_spacing = spacing;
86 self
87 }
88
89 pub const fn with_padding(mut self, inner: u8, outer: u8) -> Self {
90 self.padding_inner = inner;
91 self.padding_outer = outer;
92 self
93 }
94
95 pub const fn with_border(mut self, border_width: u8) -> Self {
96 self.border_width = border_width;
97 self
98 }
99
100 pub const fn with_selected_item(mut self, selected_item: i32) -> Self {
101 self.selected_item = selected_item;
102 self
103 }
104}
105
106impl<DRAW: DrawTarget<Color = COL>, ID: WidgetId, COL: PixelColor> Widget<DRAW, COL>
107 for ListBox<'_, ID, COL>
108{
109 fn draw(&mut self, ui: &mut Ui<DRAW, COL>) -> GuiResult<Response> {
110 let widget_state = ui.get_widget_state(self.region.id())?;
111 if widget_state.compare_set(RenderStatus::Rendered) {
112 return Ok(Response::Idle);
113 }
114
115 let mut area = self.region.rectangle();
116 if self.padding_outer > 0 {
117 area = area.offset(-(self.padding_outer as i32));
118 }
119
120 let font = self.font.font(ui.style());
121 let color = self.color.text_color(ui.style());
122 let mut rect_style = PrimitiveStyleBuilder::new()
123 .stroke_color(ui.style().border_color)
124 .stroke_width(self.border_width as u32)
125 .build();
126
127 ui.clear_area(&area)?;
128 if self.border_width > 0 {
129 ui.draw(&area.into_styled(rect_style)).ok();
130 }
131 if self.padding_inner > 0 {
132 area = area.offset(-(self.padding_inner as i32));
133 }
134
135 let start_point = area.top_left;
136 let mut y_offset = start_point.y;
137
138 for (index, item) in self.items.iter().enumerate() {
139 if index + 1 < self.start_item as usize {
140 continue;
141 }
142 let mut text = matrix_utils::make_text(*item, font, color);
143 let character_line_height = text.character_style.line_height;
144 let line_height = (character_line_height + self.line_spacing) as i32;
145 text.position = Point::new(start_point.x, y_offset);
146
147 ui.draw(&text)?;
148
149 if self.selected_item > 0 && self.selected_item as usize == index + 1 {
150 rect_style.stroke_width = 1;
151 let rect = Rectangle::new(
152 text.position,
153 Size::new(area.size.width as u32, character_line_height as u32),
154 );
155 ui.draw(&rect.into_styled(rect_style)).ok();
156 }
157
158 y_offset += line_height;
159 if y_offset - start_point.y > area.size.height as i32 {
160 break;
161 }
162 }
163
164 Ok(Response::Idle)
165 }
166}