forked from 1b-agent/fieldcraft-upstream
ui: block divisions and bias as two or three typed fields
A Tuple field kind: min_len to labels.len() positive numbers, whole when asked, trailing blanks left out. The block's divisions and bias use it instead of a JSON box (#449). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
e4d1f6c7da
commit
d17cf414f7
4 files changed
+108
-2
No files matched your search
@@ -151,6 +151,7 @@ fn scalar(f: &Field, v: &Value, units: UnitSystem) -> String {
|
||||
options.iter().find(|(o, _)| *o == s).map_or_else(|| s.to_string(), |(_, l)| (*l).to_string())
|
||||
}
|
||||
Kind::Vector { labels, .. } => vector(labels, v, unit.as_deref()),
|
||||
Kind::Tuple { labels, .. } => vector(labels, v, None),
|
||||
Kind::Ref(_) | Kind::Text | Kind::Path { .. } => v.as_str().map_or_else(|| crate::context::value_text(v), str::to_string),
|
||||
Kind::RefList(_) => list(v, |x| x.as_str().map(str::to_string)),
|
||||
// `Bracket.3: Tet, Bracket.7: Swept`.
|
||||
|
||||
@@ -307,6 +307,7 @@ impl Form<'_> {
|
||||
fields::combo(ui, id, selected, &labels, fill(ui)).map(|i| options.get(i).map_or(Value::Null, |(v, _)| json!(v)))
|
||||
}
|
||||
Kind::Vector { labels, nullable, .. } => self.vector(ui, id, value, f, labels, nullable, None),
|
||||
Kind::Tuple { labels, whole, .. } => self.tuple(ui, id, value, labels, whole),
|
||||
Kind::Ref(c) => self.reference(ui, id, path, value, c, f.hint, f.required),
|
||||
Kind::RefList(c) => self.reference_list(ui, path, value, c),
|
||||
Kind::RefChoices(c, options) => self.reference_choices(ui, id, value, c, options),
|
||||
@@ -440,6 +441,53 @@ impl Form<'_> {
|
||||
changed.then(|| Value::Array(next.into_iter().map(|x| if x.is_null() && !nullable { json!(0.0) } else { x }).collect()))
|
||||
}
|
||||
|
||||
/// A number per label on one line, captioned; trailing empty slots are left out (a 2D
|
||||
/// block's divisions are the first two of three).
|
||||
fn tuple(&mut self, ui: &mut Ui, id: Id, value: Option<&Value>, labels: &[&str], whole: bool) -> Option<Value> {
|
||||
let mut slots: Vec<Value> = match value {
|
||||
None | Some(Value::Null) => Vec::new(),
|
||||
Some(Value::Array(a)) if a.len() <= labels.len() => a.clone(),
|
||||
Some(_) => {
|
||||
self.json_only(ui);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
slots.resize(labels.len(), Value::Null);
|
||||
let gap = 3.0;
|
||||
let small = egui::TextStyle::Small.resolve(ui.style());
|
||||
let caption_w = labels.iter().map(|l| crate::elide::width(ui, &caption(l), &small)).fold(0.0, f32::max) + 2.0 * gap;
|
||||
// All on one line when each still shows a value whole; otherwise one per line.
|
||||
let room = fill(ui);
|
||||
let n = labels.len().max(1);
|
||||
let per_line = if (room - gap * (n as f32 - 1.0)) / n as f32 - caption_w >= VECTOR_MIN { n } else { 1 };
|
||||
let width = ((room - gap * (per_line as f32 - 1.0)) / per_line as f32 - caption_w - 1.0).max(1.0);
|
||||
let mut changed = false;
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = gap;
|
||||
for (i, label) in labels.iter().enumerate() {
|
||||
if per_line == 1 && i > 0 {
|
||||
ui.end_row();
|
||||
}
|
||||
fields::caption(ui, &self.style, &caption(label));
|
||||
let slot = slots.get(i).cloned().unwrap_or(Value::Null);
|
||||
let shown = (!slot.is_null()).then_some(&slot);
|
||||
if let Some(v) = self.number(ui, id.with(i), shown, label, whole, width) {
|
||||
if let Some(s) = slots.get_mut(i) {
|
||||
*s = v;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if !changed {
|
||||
return None;
|
||||
}
|
||||
while slots.last().is_some_and(Value::is_null) {
|
||||
slots.pop();
|
||||
}
|
||||
Some(if slots.is_empty() { Value::Null } else { Value::Array(slots) })
|
||||
}
|
||||
|
||||
/// A drop-down of the collection's items; never free text. With none, the drop-down is
|
||||
/// greyed and a button opens the command that makes one ([`Outcome::create`]).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
Reference in new issue
Block a user