teksilo_widgets/table_view/
a11y.rs1use teksilo_canvas::{Rect, SizeProposal};
21use teksilo_core::accessibility::AccessNodeBuilder;
22use teksilo_core::widget::{LayoutContext, Widget, WidgetPlacement};
23use teksilo_core::widget_id::WidgetId;
24
25use teksilo_data::SortDirection;
26
27#[derive(Debug)]
30pub(crate) struct CellA11y {
31 child: WidgetId,
32 row_index_1based: usize,
33 col_index_1based: usize,
34 selected: bool,
35 is_row_header: bool,
38 name: Option<String>,
40}
41
42impl CellA11y {
43 pub(crate) fn new(
44 child: WidgetId,
45 row_index_1based: usize,
46 col_index_1based: usize,
47 selected: bool,
48 ) -> Self {
49 Self {
50 child,
51 row_index_1based,
52 col_index_1based,
53 selected,
54 is_row_header: false,
55 name: None,
56 }
57 }
58
59 #[allow(dead_code)]
61 pub(crate) fn with_role_row_header(mut self, is_row_header: bool) -> Self {
62 self.is_row_header = is_row_header;
63 self
64 }
65
66 #[allow(dead_code)]
68 pub(crate) fn with_name(mut self, name: Option<String>) -> Self {
69 self.name = name;
70 self
71 }
72}
73
74impl Widget for CellA11y {
75 fn layout_response(
76 &self,
77 proposal: SizeProposal,
78 ctx: &LayoutContext,
79 ) -> teksilo_core::widget::LayoutResponse {
80 ctx.child_size(self.child, proposal)
81 .unwrap_or_else(|| proposal.resolve(0.0, 0.0))
82 .into()
83 }
84
85 fn place_children(
86 &self,
87 bounds: Rect,
88 _proposal: SizeProposal,
89 children: &mut [WidgetPlacement],
90 _ctx: &LayoutContext,
91 ) {
92 for child in children.iter_mut() {
93 child.origin = bounds.origin();
94 child.size = bounds.size();
95 }
96 }
97
98 fn accessibility(&self, builder: &mut AccessNodeBuilder) {
99 builder.set_role(if self.is_row_header {
100 teksilo_core::accesskit::Role::RowHeader
101 } else {
102 teksilo_core::accesskit::Role::Cell
103 });
104 if let Some(ref name) = self.name {
105 builder.set_name(name.clone());
106 }
107 builder.set_selected(self.selected);
108 builder.set_row_index(self.row_index_1based);
109 builder.set_column_index(self.col_index_1based);
110 }
111
112 fn children(&self) -> Vec<WidgetId> {
113 vec![self.child]
114 }
115}
116
117#[derive(Debug)]
124pub(crate) struct TreeRowA11y {
125 child: WidgetId,
126 row_index_1based: usize,
127 level_1based: usize,
129 expanded: Option<bool>,
131 selected: bool,
132 position_in_set: usize,
134 size_of_set: usize,
136}
137
138impl TreeRowA11y {
139 #[allow(clippy::too_many_arguments)]
140 pub(crate) fn new(
141 child: WidgetId,
142 row_index_1based: usize,
143 level_1based: usize,
144 expanded: Option<bool>,
145 selected: bool,
146 position_in_set: usize,
147 size_of_set: usize,
148 ) -> Self {
149 Self {
150 child,
151 row_index_1based,
152 level_1based,
153 expanded,
154 selected,
155 position_in_set,
156 size_of_set,
157 }
158 }
159}
160
161impl Widget for TreeRowA11y {
162 fn layout_response(
163 &self,
164 proposal: SizeProposal,
165 ctx: &LayoutContext,
166 ) -> teksilo_core::widget::LayoutResponse {
167 ctx.child_size(self.child, proposal)
168 .unwrap_or_else(|| proposal.resolve(0.0, 0.0))
169 .into()
170 }
171
172 fn place_children(
173 &self,
174 bounds: Rect,
175 _proposal: SizeProposal,
176 children: &mut [WidgetPlacement],
177 _ctx: &LayoutContext,
178 ) {
179 for child in children.iter_mut() {
180 child.origin = bounds.origin();
181 child.size = bounds.size();
182 }
183 }
184
185 fn accessibility(&self, builder: &mut AccessNodeBuilder) {
186 builder.set_role(teksilo_core::accesskit::Role::Row);
187 builder.set_selected(self.selected);
188 if let Some(exp) = self.expanded {
189 builder.set_expanded(exp);
190 }
191 builder.set_row_index(self.row_index_1based);
192 builder.inner_mut().set_level(self.level_1based.max(1));
195 builder
196 .inner_mut()
197 .set_position_in_set(self.position_in_set);
198 builder.inner_mut().set_size_of_set(self.size_of_set);
199 }
200
201 fn children(&self) -> Vec<WidgetId> {
202 vec![self.child]
203 }
204}
205
206#[derive(Debug)]
209#[allow(dead_code)]
210pub(crate) struct ColumnHeaderA11y {
211 child: WidgetId,
212 col_index_1based: usize,
213 name: String,
214 sort: Option<SortDirection>,
215}
216
217#[allow(dead_code)] impl ColumnHeaderA11y {
219 pub(crate) fn new(
220 child: WidgetId,
221 col_index_1based: usize,
222 name: impl Into<String>,
223 sort: Option<SortDirection>,
224 ) -> Self {
225 Self {
226 child,
227 col_index_1based,
228 name: name.into(),
229 sort,
230 }
231 }
232}
233
234impl Widget for ColumnHeaderA11y {
235 fn layout_response(
236 &self,
237 proposal: SizeProposal,
238 ctx: &LayoutContext,
239 ) -> teksilo_core::widget::LayoutResponse {
240 ctx.child_size(self.child, proposal)
241 .unwrap_or_else(|| proposal.resolve(0.0, 0.0))
242 .into()
243 }
244
245 fn place_children(
246 &self,
247 bounds: Rect,
248 _proposal: SizeProposal,
249 children: &mut [WidgetPlacement],
250 _ctx: &LayoutContext,
251 ) {
252 for child in children.iter_mut() {
253 child.origin = bounds.origin();
254 child.size = bounds.size();
255 }
256 }
257
258 fn accessibility(&self, builder: &mut AccessNodeBuilder) {
259 builder.set_role(teksilo_core::accesskit::Role::ColumnHeader);
260 builder.set_name(self.name.clone());
261 builder.set_column_index(self.col_index_1based);
262 if let Some(dir) = self.sort {
263 let ak_dir = match dir {
264 SortDirection::Ascending => teksilo_core::accesskit::SortDirection::Ascending,
265 SortDirection::Descending => teksilo_core::accesskit::SortDirection::Descending,
266 };
267 builder.inner_mut().set_sort_direction(ak_dir);
268 }
269 }
270
271 fn children(&self) -> Vec<WidgetId> {
272 vec![self.child]
273 }
274}