graphix_package_gui/widgets/
table.rs1use super::{compile, compile_children, GuiW, GuiWidget, IcedElement};
2use crate::types::{HAlignV, LengthV, VAlignV};
3use anyhow::{Context, Result};
4use arcstr::ArcStr;
5use graphix_compiler::expr::ExprId;
6use graphix_rt::{CallableId, GXExt, GXHandle, Ref, TRef};
7use iced_widget as widget;
8use netidx::publisher::Value;
9use smallvec::SmallVec;
10use tokio::try_join;
11
12struct CompiledColumn<X: GXExt> {
13 header_ref: Ref<X>,
14 header: GuiW<X>,
15 width: TRef<X, LengthV>,
16 halign: TRef<X, HAlignV>,
17 valign: TRef<X, VAlignV>,
18}
19
20pub(crate) struct TableW<X: GXExt> {
21 gx: GXHandle<X>,
22 columns_ref: Ref<X>,
23 columns: Vec<CompiledColumn<X>>,
24 rows_ref: Ref<X>,
25 cells: Vec<Vec<GuiW<X>>>,
26 width: TRef<X, LengthV>,
27 padding: TRef<X, Option<f64>>,
28 separator: TRef<X, Option<f64>>,
29}
30
31async fn compile_columns<X: GXExt>(
32 gx: &GXHandle<X>,
33 v: Value,
34) -> Result<Vec<CompiledColumn<X>>> {
35 let items = v.cast_to::<SmallVec<[Value; 8]>>()?;
36 let mut cols = Vec::with_capacity(items.len());
37 for item in items {
38 let [(_, halign_id), (_, header_id), (_, valign_id), (_, width_id)] =
39 item.cast_to::<[(ArcStr, u64); 4]>().context("table column flds")?;
40 let (halign, header_ref, valign, width) = try_join! {
41 gx.compile_ref(halign_id),
42 gx.compile_ref(header_id),
43 gx.compile_ref(valign_id),
44 gx.compile_ref(width_id),
45 }?;
46 let header = match header_ref.last.as_ref() {
47 None => Box::new(super::EmptyW) as GuiW<X>,
48 Some(v) => compile(gx.clone(), v.clone()).await.context("table column header")?,
49 };
50 cols.push(CompiledColumn {
51 header_ref,
52 header,
53 width: TRef::new(width).context("table column tref width")?,
54 halign: TRef::new(halign).context("table column tref halign")?,
55 valign: TRef::new(valign).context("table column tref valign")?,
56 });
57 }
58 Ok(cols)
59}
60
61async fn compile_rows<X: GXExt>(
62 gx: &GXHandle<X>,
63 v: Value,
64) -> Result<Vec<Vec<GuiW<X>>>> {
65 let rows = v.cast_to::<SmallVec<[Value; 8]>>()?;
66 let mut result = Vec::with_capacity(rows.len());
67 for row in rows {
68 let cells = compile_children(gx.clone(), row).await.context("table row")?;
69 result.push(cells);
70 }
71 Ok(result)
72}
73
74impl<X: GXExt> TableW<X> {
75 pub(crate) async fn compile(gx: GXHandle<X>, source: Value) -> Result<GuiW<X>> {
76 let [(_, columns), (_, padding), (_, rows), (_, separator), (_, width)] =
77 source.cast_to::<[(ArcStr, u64); 5]>().context("table flds")?;
78 let (columns_ref, padding, rows_ref, separator, width) = try_join! {
79 gx.compile_ref(columns),
80 gx.compile_ref(padding),
81 gx.compile_ref(rows),
82 gx.compile_ref(separator),
83 gx.compile_ref(width),
84 }?;
85 let compiled_columns = match columns_ref.last.as_ref() {
86 None => vec![],
87 Some(v) => compile_columns(&gx, v.clone()).await.context("table columns")?,
88 };
89 let compiled_rows = match rows_ref.last.as_ref() {
90 None => vec![],
91 Some(v) => compile_rows(&gx, v.clone()).await.context("table rows")?,
92 };
93 Ok(Box::new(Self {
94 gx: gx.clone(),
95 columns_ref,
96 columns: compiled_columns,
97 rows_ref,
98 cells: compiled_rows,
99 width: TRef::new(width).context("table tref width")?,
100 padding: TRef::new(padding).context("table tref padding")?,
101 separator: TRef::new(separator).context("table tref separator")?,
102 }))
103 }
104}
105
106impl<X: GXExt> GuiWidget<X> for TableW<X> {
107 fn handle_update(
108 &mut self,
109 rt: &tokio::runtime::Handle,
110 id: ExprId,
111 v: &Value,
112 ) -> Result<bool> {
113 let mut changed = false;
114 changed |= self.width.update(id, v).context("table update width")?.is_some();
115 changed |= self.padding.update(id, v).context("table update padding")?.is_some();
116 changed |=
117 self.separator.update(id, v).context("table update separator")?.is_some();
118 if id == self.columns_ref.id {
119 self.columns_ref.last = Some(v.clone());
120 self.columns = rt
121 .block_on(compile_columns(&self.gx, v.clone()))
122 .context("table columns recompile")?;
123 changed = true;
124 }
125 for col in &mut self.columns {
126 if id == col.header_ref.id {
127 col.header_ref.last = Some(v.clone());
128 col.header = rt
129 .block_on(compile(self.gx.clone(), v.clone()))
130 .context("table column header recompile")?;
131 changed = true;
132 }
133 changed |= col.header.handle_update(rt, id, v)?;
134 changed |=
135 col.width.update(id, v).context("table col update width")?.is_some();
136 changed |=
137 col.halign.update(id, v).context("table col update halign")?.is_some();
138 changed |=
139 col.valign.update(id, v).context("table col update valign")?.is_some();
140 }
141 if id == self.rows_ref.id {
142 self.rows_ref.last = Some(v.clone());
143 self.cells = rt
144 .block_on(compile_rows(&self.gx, v.clone()))
145 .context("table rows recompile")?;
146 changed = true;
147 }
148 for row in &mut self.cells {
149 for cell in row {
150 changed |= cell.handle_update(rt, id, v)?;
151 }
152 }
153 Ok(changed)
154 }
155
156 fn editor_action(
157 &mut self,
158 id: ExprId,
159 action: &iced_widget::text_editor::Action,
160 ) -> Option<(CallableId, Value)> {
161 for col in &mut self.columns {
162 if let some @ Some(_) = col.header.editor_action(id, action) {
163 return some;
164 }
165 }
166 for row in &mut self.cells {
167 for cell in row {
168 if let some @ Some(_) = cell.editor_action(id, action) {
169 return some;
170 }
171 }
172 }
173 None
174 }
175
176 fn view(&self) -> IcedElement<'_> {
177 let num_rows = self.cells.len();
178 let cells = &self.cells;
179 let cols = self.columns.iter().enumerate().map(|(c, col)| {
180 let header = col.header.view();
181 let mut tc = widget::table::column(header, move |row: usize| {
182 if row < cells.len() && c < cells[row].len() {
183 cells[row][c].view()
184 } else {
185 iced_widget::Space::new().into()
186 }
187 });
188 if let Some(w) = col.width.t.as_ref() {
189 tc = tc.width(w.0);
190 }
191 if let Some(a) = col.halign.t.as_ref() {
192 tc = tc.align_x(a.0);
193 }
194 if let Some(a) = col.valign.t.as_ref() {
195 tc = tc.align_y(a.0);
196 }
197 tc
198 });
199 let mut t = widget::table::table(cols, 0..num_rows);
200 if let Some(w) = self.width.t.as_ref() {
201 t = t.width(w.0);
202 }
203 if let Some(Some(p)) = self.padding.t {
204 t = t.padding(p as f32);
205 }
206 if let Some(Some(s)) = self.separator.t {
207 t = t.separator(s as f32);
208 }
209 t.into()
210 }
211}