mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 19:28:12 +08:00
Add a drill-in button beside Data panel gradient preview widgets (#4398)
This commit is contained in:
committed by
Dennis Kobert
parent
f688e4d478
commit
b13ff712f5
@@ -117,23 +117,26 @@ impl LayoutMessageHandler {
|
|||||||
}
|
}
|
||||||
LayoutGroup::Table(WidgetTable { rows, .. }) => {
|
LayoutGroup::Table(WidgetTable { rows, .. }) => {
|
||||||
for (row_index, row) in rows.iter().enumerate() {
|
for (row_index, row) in rows.iter().enumerate() {
|
||||||
for (value_index, value) in row.iter().enumerate() {
|
for (cell_index, cell) in row.iter().enumerate() {
|
||||||
// Return if this is the correct ID
|
for (value_index, value) in cell.iter().enumerate() {
|
||||||
if value.widget_id == widget_id {
|
// Return if this is the correct ID
|
||||||
widget_path.push(row_index);
|
if value.widget_id == widget_id {
|
||||||
widget_path.push(value_index);
|
widget_path.push(row_index);
|
||||||
return Some((value, widget_path));
|
widget_path.push(cell_index);
|
||||||
}
|
widget_path.push(value_index);
|
||||||
|
return Some((value, widget_path));
|
||||||
|
}
|
||||||
|
|
||||||
if let Widget::PopoverButton(popover) = &*value.widget {
|
if let Widget::PopoverButton(popover) = &*value.widget {
|
||||||
stack.extend(
|
stack.extend(
|
||||||
popover
|
popover
|
||||||
.popover_layout
|
.popover_layout
|
||||||
.0
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(child, val)| ([widget_path.as_slice(), &[row_index, value_index, child]].concat(), val)),
|
.map(|(child, val)| ([widget_path.as_slice(), &[row_index, cell_index, value_index, child]].concat(), val)),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ impl<'a> Iterator for WidgetIter<'a> {
|
|||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
|
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
|
||||||
self.table.extend(rows.iter().flatten().rev());
|
self.table.extend(rows.iter().flatten().flatten().rev());
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
|
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
|
||||||
@@ -325,7 +325,7 @@ impl<'a> Iterator for WidgetIterMut<'a> {
|
|||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
|
Some(LayoutGroup::Table(WidgetTable { rows, .. })) => {
|
||||||
self.table.extend(rows.iter_mut().flatten().rev());
|
self.table.extend(rows.iter_mut().flatten().flatten().rev());
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
|
Some(LayoutGroup::Section(WidgetSection { layout, .. })) => {
|
||||||
@@ -365,8 +365,9 @@ pub struct WidgetRow {
|
|||||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||||
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct WidgetTable {
|
pub struct WidgetTable {
|
||||||
|
/// Rows of cells, each cell holding one or more widgets laid out inline.
|
||||||
#[serde(rename = "tableWidgets")]
|
#[serde(rename = "tableWidgets")]
|
||||||
pub rows: Vec<Vec<WidgetInstance>>,
|
pub rows: Vec<Vec<Vec<WidgetInstance>>>,
|
||||||
pub unstyled: bool,
|
pub unstyled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +413,14 @@ impl LayoutGroup {
|
|||||||
Self::Column(WidgetColumn { widgets })
|
Self::Column(WidgetColumn { widgets })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A table with one widget per cell.
|
||||||
pub fn table(rows: Vec<Vec<WidgetInstance>>, unstyled: bool) -> Self {
|
pub fn table(rows: Vec<Vec<WidgetInstance>>, unstyled: bool) -> Self {
|
||||||
|
let rows = rows.into_iter().map(|row| row.into_iter().map(|widget| vec![widget]).collect()).collect();
|
||||||
|
Self::Table(WidgetTable { rows, unstyled })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A table whose cells may each hold multiple widgets laid out inline.
|
||||||
|
pub fn table_of_cells(rows: Vec<Vec<Vec<WidgetInstance>>>, unstyled: bool) -> Self {
|
||||||
Self::Table(WidgetTable { rows, unstyled })
|
Self::Table(WidgetTable { rows, unstyled })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,13 +608,17 @@ impl Diffable for LayoutGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Table(WidgetTable { rows, .. }) => {
|
Self::Table(WidgetTable { rows, .. }) => {
|
||||||
for (row_idx, row) in rows.iter().enumerate() {
|
for (row_index, row) in rows.iter().enumerate() {
|
||||||
for (col_idx, widget) in row.iter().enumerate() {
|
for (column_index, cell) in row.iter().enumerate() {
|
||||||
widget_path.push(row_idx);
|
for (widget_index, widget) in cell.iter().enumerate() {
|
||||||
widget_path.push(col_idx);
|
widget_path.push(row_index);
|
||||||
widget.collect_checkbox_ids(layout_target, widget_path, checkbox_map);
|
widget_path.push(column_index);
|
||||||
widget_path.pop();
|
widget_path.push(widget_index);
|
||||||
widget_path.pop();
|
widget.collect_checkbox_ids(layout_target, widget_path, checkbox_map);
|
||||||
|
widget_path.pop();
|
||||||
|
widget_path.pop();
|
||||||
|
widget_path.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,13 +638,17 @@ impl Diffable for LayoutGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Table(WidgetTable { rows, .. }) => {
|
Self::Table(WidgetTable { rows, .. }) => {
|
||||||
for (row_idx, row) in rows.iter_mut().enumerate() {
|
for (row_index, row) in rows.iter_mut().enumerate() {
|
||||||
for (col_idx, widget) in row.iter_mut().enumerate() {
|
for (column_index, cell) in row.iter_mut().enumerate() {
|
||||||
widget_path.push(row_idx);
|
for (widget_index, widget) in cell.iter_mut().enumerate() {
|
||||||
widget_path.push(col_idx);
|
widget_path.push(row_index);
|
||||||
widget.replace_widget_ids(layout_target, widget_path, checkbox_map);
|
widget_path.push(column_index);
|
||||||
widget_path.pop();
|
widget_path.push(widget_index);
|
||||||
widget_path.pop();
|
widget.replace_widget_ids(layout_target, widget_path, checkbox_map);
|
||||||
|
widget_path.pop();
|
||||||
|
widget_path.pop();
|
||||||
|
widget_path.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,6 +311,10 @@ fn column_headings(value: &[&str]) -> Vec<WidgetInstance> {
|
|||||||
value.iter().map(|text| TextLabel::new(*text).widget_instance()).collect()
|
value.iter().map(|text| TextLabel::new(*text).widget_instance()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn single_widget_cells(widgets: Vec<WidgetInstance>) -> Vec<Vec<WidgetInstance>> {
|
||||||
|
widgets.into_iter().map(|widget| vec![widget]).collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn label(x: impl Into<String>) -> Vec<LayoutGroup> {
|
fn label(x: impl Into<String>) -> Vec<LayoutGroup> {
|
||||||
let error = vec![TextLabel::new(x).widget_instance()];
|
let error = vec![TextLabel::new(x).widget_instance()];
|
||||||
vec![LayoutGroup::row(error)]
|
vec![LayoutGroup::row(error)]
|
||||||
@@ -323,18 +327,20 @@ trait TableItemLayout {
|
|||||||
data.breadcrumbs.push(self.identifier());
|
data.breadcrumbs.push(self.identifier());
|
||||||
self.value_page(data)
|
self.value_page(data)
|
||||||
}
|
}
|
||||||
/// Renders this value as a single inline widget inside an item of a `List`.
|
/// Renders this value as the inline widgets filling one table cell inside an item of a `List`.
|
||||||
/// `target` is the [`PathStep`] to push when the widget is clicked to drill into the value.
|
/// `target` is the [`PathStep`] to push when a widget is clicked to drill into the value.
|
||||||
/// `data` provides shared context (notably `network_interface`) for types whose label or content
|
/// `data` provides shared context (notably `network_interface`) for types whose label or content
|
||||||
/// depends on lookup beyond their own value (e.g. `NodeId` resolving a node's display name).
|
/// depends on lookup beyond their own value (e.g. `NodeId` resolving a node's display name).
|
||||||
/// The default is a button labeled with `identifier()`. Types whose values are best shown
|
/// The default is a single button labeled with `identifier()`. Types whose values are best shown
|
||||||
/// inline (colors, transforms, primitives, etc.) override this to ignore `target` and
|
/// inline (colors, transforms, primitives, etc.) override this to ignore `target` and return a
|
||||||
/// return a richer non-navigating widget.
|
/// richer non-navigating widget, optionally joined by companions like a drill-in button.
|
||||||
fn value_widget(&self, target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextButton::new(self.identifier())
|
vec![
|
||||||
.on_update(move |_| DataPanelMessage::PushToElementPath { step: target.clone() }.into())
|
TextButton::new(self.identifier())
|
||||||
.narrow(true)
|
.on_update(move |_| DataPanelMessage::PushToElementPath { step: target.clone() }.into())
|
||||||
.widget_instance()
|
.narrow(true)
|
||||||
|
.widget_instance(),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![]
|
vec![]
|
||||||
@@ -375,20 +381,20 @@ impl<T: TableItemLayout> TableItemLayout for Item<T> {
|
|||||||
let attribute_keys: Vec<String> = self.attributes().keys().map(str::to_string).collect();
|
let attribute_keys: Vec<String> = self.attributes().keys().map(str::to_string).collect();
|
||||||
|
|
||||||
// A single element, so no leading ID column, unlike the `List` table
|
// A single element, so no leading ID column, unlike the `List` table
|
||||||
let mut values = vec![self.element().value_widget(PathStep::Element(0), data)];
|
let mut values = vec![self.element().value_widgets(PathStep::Element(0), data)];
|
||||||
for key in &attribute_keys {
|
for key in &attribute_keys {
|
||||||
let target = PathStep::Attribute { row: 0, key: key.clone() };
|
let target = PathStep::Attribute { row: 0, key: key.clone() };
|
||||||
let widget = self.attributes().get_any(key).and_then(|any| dispatch_value_widget(any, target, data)).unwrap_or_else(|| {
|
let cell = self.attributes().get_any(key).and_then(|any| dispatch_value_widgets(any, target, data)).unwrap_or_else(|| {
|
||||||
let text = self.attributes().display_value(key, display_value_override).unwrap_or_else(|| "-".to_string());
|
let text = self.attributes().display_value(key, display_value_override).unwrap_or_else(|| "-".to_string());
|
||||||
TextLabel::new(text).narrow(true).widget_instance()
|
vec![TextLabel::new(text).narrow(true).widget_instance()]
|
||||||
});
|
});
|
||||||
values.push(widget);
|
values.push(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut column_names = vec!["element"];
|
let mut column_names = vec!["element"];
|
||||||
column_names.extend(attribute_keys.iter().map(|s| s.as_str()));
|
column_names.extend(attribute_keys.iter().map(|s| s.as_str()));
|
||||||
|
|
||||||
vec![LayoutGroup::table(vec![column_headings(&column_names), values], false)]
|
vec![LayoutGroup::table_of_cells(vec![single_widget_cells(column_headings(&column_names)), values], false)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,14 +439,17 @@ impl<T: TableItemLayout> TableItemLayout for List<T> {
|
|||||||
let mut rows = (0..self.len())
|
let mut rows = (0..self.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
let mut values = vec![TextLabel::new(format!("{index}")).narrow(true).widget_instance(), element.value_widget(PathStep::Element(index), data)];
|
let mut values = vec![
|
||||||
|
vec![TextLabel::new(format!("{index}")).narrow(true).widget_instance()],
|
||||||
|
element.value_widgets(PathStep::Element(index), data),
|
||||||
|
];
|
||||||
for key in &attribute_keys {
|
for key in &attribute_keys {
|
||||||
let target = PathStep::Attribute { row: index, key: key.clone() };
|
let target = PathStep::Attribute { row: index, key: key.clone() };
|
||||||
let widget = self.attribute_any(key, index).and_then(|any| dispatch_value_widget(any, target, data)).unwrap_or_else(|| {
|
let cell = self.attribute_any(key, index).and_then(|any| dispatch_value_widgets(any, target, data)).unwrap_or_else(|| {
|
||||||
let text = self.attribute_display_value(key, index, display_value_override).unwrap_or_else(|| "-".to_string());
|
let text = self.attribute_display_value(key, index, display_value_override).unwrap_or_else(|| "-".to_string());
|
||||||
TextLabel::new(text).narrow(true).widget_instance()
|
vec![TextLabel::new(text).narrow(true).widget_instance()]
|
||||||
});
|
});
|
||||||
values.push(widget);
|
values.push(cell);
|
||||||
}
|
}
|
||||||
values
|
values
|
||||||
})
|
})
|
||||||
@@ -448,9 +457,9 @@ impl<T: TableItemLayout> TableItemLayout for List<T> {
|
|||||||
|
|
||||||
let mut column_names = vec!["", "element"];
|
let mut column_names = vec!["", "element"];
|
||||||
column_names.extend(attribute_keys.iter().map(|s| s.as_str()));
|
column_names.extend(attribute_keys.iter().map(|s| s.as_str()));
|
||||||
rows.insert(0, column_headings(&column_names));
|
rows.insert(0, single_widget_cells(column_headings(&column_names)));
|
||||||
|
|
||||||
vec![LayoutGroup::table(rows, false)]
|
vec![LayoutGroup::table_of_cells(rows, false)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,8 +491,8 @@ impl TableItemLayout for DashPattern {
|
|||||||
self.value_page(data)
|
self.value_page(data)
|
||||||
}
|
}
|
||||||
// Label the spreadsheet's element button with the inner list's identifier, like Artboard
|
// Label the spreadsheet's element button with the inner list's identifier, like Artboard
|
||||||
fn value_widget(&self, target: PathStep, data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, target: PathStep, data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
self.0.value_widget(target, data)
|
self.0.value_widgets(target, data)
|
||||||
}
|
}
|
||||||
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
self.0.layout_with_breadcrumb(data)
|
self.0.layout_with_breadcrumb(data)
|
||||||
@@ -502,8 +511,8 @@ impl TableItemLayout for BoxCorners {
|
|||||||
self.value_page(data)
|
self.value_page(data)
|
||||||
}
|
}
|
||||||
// Label the spreadsheet's element button with the inner list's identifier, like Artboard
|
// Label the spreadsheet's element button with the inner list's identifier, like Artboard
|
||||||
fn value_widget(&self, target: PathStep, data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, target: PathStep, data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
self.0.value_widget(target, data)
|
self.0.value_widgets(target, data)
|
||||||
}
|
}
|
||||||
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
self.0.layout_with_breadcrumb(data)
|
self.0.layout_with_breadcrumb(data)
|
||||||
@@ -715,16 +724,17 @@ impl TableItemLayout for Color {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
format!("Color (#{})", SRGBA8::from(*self).to_rgba_hex())
|
format!("Color (#{})", SRGBA8::from(*self).to_rgba_hex())
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(*self)))
|
vec![
|
||||||
.disabled(true)
|
ColorInput::new(FillChoiceUI::from(&FillChoice::Solid(*self)))
|
||||||
.menu_direction(Some(MenuDirection::Top))
|
.disabled(true)
|
||||||
.narrow(true)
|
.menu_direction(Some(MenuDirection::Top))
|
||||||
.widget_instance()
|
.narrow(true)
|
||||||
|
.widget_instance(),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
let widgets = vec![self.value_widget(PathStep::Element(0), _data)];
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
vec![LayoutGroup::row(widgets)]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,16 +745,27 @@ impl TableItemLayout for Gradient {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
format!("Gradient ({} stops)", self.len())
|
format!("Gradient ({} stops)", self.len())
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
// The wrapping `Item` already contributes the breadcrumb; the inner list supplies the next level
|
||||||
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(self.clone())))
|
fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
.menu_direction(Some(MenuDirection::Top))
|
self.value_page(data)
|
||||||
.disabled(true)
|
|
||||||
.narrow(true)
|
|
||||||
.widget_instance()
|
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
// The preview widget doesn't navigate, so a drill-in button beside it opens the newtype's underlying color list
|
||||||
let widgets = vec![self.value_widget(PathStep::Element(0), _data)];
|
fn value_widgets(&self, target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
vec![LayoutGroup::row(widgets)]
|
vec![
|
||||||
|
TextButton::new(self.as_color_list().identifier())
|
||||||
|
.on_update(move |_| DataPanelMessage::PushToElementPath { step: target.clone() }.into())
|
||||||
|
.narrow(true)
|
||||||
|
.widget_instance(),
|
||||||
|
Separator::new(SeparatorStyle::Related).widget_instance(),
|
||||||
|
ColorInput::new(FillChoiceUI::from(&FillChoice::Gradient(self.clone())))
|
||||||
|
.menu_direction(Some(MenuDirection::Top))
|
||||||
|
.disabled(true)
|
||||||
|
.narrow(true)
|
||||||
|
.widget_instance(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
|
self.as_color_list().layout_with_breadcrumb(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,11 +881,11 @@ impl TableItemLayout for bool {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Bool".to_string()
|
"Bool".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
CheckboxInput::new(*self).disabled(true).widget_instance()
|
vec![CheckboxInput::new(*self).disabled(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,11 +915,11 @@ impl TableItemLayout for Option<f64> {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Option<f64>".to_string()
|
"Option<f64>".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(format!("{self:?}")).narrow(true).widget_instance()
|
vec![TextLabel::new(format!("{self:?}")).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -909,11 +930,11 @@ impl TableItemLayout for DVec2 {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Vec2".to_string()
|
"Vec2".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(format_dvec2(*self)).narrow(true).widget_instance()
|
vec![TextLabel::new(format_dvec2(*self)).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,11 +945,11 @@ impl TableItemLayout for Vec2 {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Vec2".to_string()
|
"Vec2".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(format_dvec2(DVec2::new(self.x as f64, self.y as f64))).narrow(true).widget_instance()
|
vec![TextLabel::new(format_dvec2(DVec2::new(self.x as f64, self.y as f64))).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,11 +960,11 @@ impl TableItemLayout for DAffine2 {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Transform".to_string()
|
"Transform".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(format_transform_matrix(*self)).narrow(true).widget_instance()
|
vec![TextLabel::new(format_transform_matrix(*self)).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,12 +975,12 @@ impl TableItemLayout for Affine2 {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
"Transform".to_string()
|
"Transform".to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
let matrix = DAffine2::from_cols_array(&self.to_cols_array().map(|x| x as f64));
|
let matrix = DAffine2::from_cols_array(&self.to_cols_array().map(|x| x as f64));
|
||||||
TextLabel::new(format_transform_matrix(matrix)).narrow(true).widget_instance()
|
vec![TextLabel::new(format_transform_matrix(matrix)).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -974,11 +995,11 @@ macro_rules! impl_table_item_layout_for_choice_enum {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
self.to_string()
|
self.to_string()
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(self.to_string()).narrow(true).widget_instance()
|
vec![TextLabel::new(self.to_string()).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
@@ -1028,11 +1049,11 @@ impl TableItemLayout for ReferencePoint {
|
|||||||
fn identifier(&self) -> String {
|
fn identifier(&self) -> String {
|
||||||
format!("{self:?}")
|
format!("{self:?}")
|
||||||
}
|
}
|
||||||
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, _target: PathStep, _data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
TextLabel::new(self.identifier()).narrow(true).widget_instance()
|
vec![TextLabel::new(self.identifier()).narrow(true).widget_instance()]
|
||||||
}
|
}
|
||||||
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
|
vec![LayoutGroup::row(self.value_widgets(PathStep::Element(0), _data))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,7 +1083,7 @@ impl TableItemLayout for NodeId {
|
|||||||
// in the Node Graph / Layers panels. The lookup uses `data.node_lookup_network_path` (set by the enclosing
|
// in the Node Graph / Layers panels. The lookup uses `data.node_lookup_network_path` (set by the enclosing
|
||||||
// `List<NodeId>` if rendering a path) so the resolution succeeds at any nesting depth. The button's icon
|
// `List<NodeId>` if rendering a path) so the resolution succeeds at any nesting depth. The button's icon
|
||||||
// signals layer-vs-node kind. Falls back to "Node {id}" with no icon if the lookup misses.
|
// signals layer-vs-node kind. Falls back to "Node {id}" with no icon if the lookup misses.
|
||||||
fn value_widget(&self, target: PathStep, data: &LayoutData) -> WidgetInstance {
|
fn value_widgets(&self, target: PathStep, data: &LayoutData) -> Vec<WidgetInstance> {
|
||||||
let label = node_id_display_label(*self, data.network_interface, &data.node_lookup_network_path);
|
let label = node_id_display_label(*self, data.network_interface, &data.node_lookup_network_path);
|
||||||
let mut button = TextButton::new(label)
|
let mut button = TextButton::new(label)
|
||||||
.on_update(move |_| DataPanelMessage::PushToElementPath { step: target.clone() }.into())
|
.on_update(move |_| DataPanelMessage::PushToElementPath { step: target.clone() }.into())
|
||||||
@@ -1071,7 +1092,7 @@ impl TableItemLayout for NodeId {
|
|||||||
let icon = if data.network_interface.is_layer(self, &data.node_lookup_network_path) { "Layer" } else { "Node" };
|
let icon = if data.network_interface.is_layer(self, &data.node_lookup_network_path) { "Layer" } else { "Node" };
|
||||||
button = button.icon(icon);
|
button = button.icon(icon);
|
||||||
}
|
}
|
||||||
button.widget_instance()
|
vec![button.widget_instance()]
|
||||||
}
|
}
|
||||||
// The value page shows the node's kind, name (editable), lock/visibility toggles, and a "Select Layer/Node" action button.
|
// The value page shows the node's kind, name (editable), lock/visibility toggles, and a "Select Layer/Node" action button.
|
||||||
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
|
||||||
@@ -1246,16 +1267,16 @@ fn display_value_override(any: &dyn Any) -> Option<String> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type-dispatched widget for displaying an attribute value in a `List<T>` item.
|
/// Type-dispatched cell widgets for displaying an attribute value in a `List<T>` item.
|
||||||
/// Delegates to [`TableItemLayout::value_widget`] so the same widget code is shared between
|
/// Delegates to [`TableItemLayout::value_widgets`] so the same widget code is shared between
|
||||||
/// element-column rendering and attribute-column rendering. Returns `None` for unrecognized
|
/// element-column rendering and attribute-column rendering. Returns `None` for unrecognized
|
||||||
/// types so the caller can fall back to a debug-formatted [`TextLabel`].
|
/// types so the caller can fall back to a debug-formatted [`TextLabel`].
|
||||||
fn dispatch_value_widget(any: &dyn Any, target: PathStep, data: &LayoutData) -> Option<WidgetInstance> {
|
fn dispatch_value_widgets(any: &dyn Any, target: PathStep, data: &LayoutData) -> Option<Vec<WidgetInstance>> {
|
||||||
macro_rules! check {
|
macro_rules! check {
|
||||||
( $($ty:ty),* $(,)? ) => {
|
( $($ty:ty),* $(,)? ) => {
|
||||||
$(
|
$(
|
||||||
if let Some(value) = any.downcast_ref::<$ty>() {
|
if let Some(value) = any.downcast_ref::<$ty>() {
|
||||||
return Some(value.value_widget(target, data));
|
return Some(value.value_widgets(target, data));
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
@@ -1293,14 +1314,14 @@ fn table_node_id_path_layout_with_breadcrumb(path: &List<NodeId>, data: &mut Lay
|
|||||||
let node_id = path.element(index).unwrap();
|
let node_id = path.element(index).unwrap();
|
||||||
let prefix: Vec<NodeId> = path.iter_element_values().take(index).copied().collect();
|
let prefix: Vec<NodeId> = path.iter_element_values().take(index).copied().collect();
|
||||||
let saved = std::mem::replace(&mut data.node_lookup_network_path, prefix);
|
let saved = std::mem::replace(&mut data.node_lookup_network_path, prefix);
|
||||||
let widget = node_id.value_widget(PathStep::Element(index), data);
|
let widgets = node_id.value_widgets(PathStep::Element(index), data);
|
||||||
data.node_lookup_network_path = saved;
|
data.node_lookup_network_path = saved;
|
||||||
vec![TextLabel::new(format!("{index}")).narrow(true).widget_instance(), widget]
|
vec![vec![TextLabel::new(format!("{index}")).narrow(true).widget_instance()], widgets]
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
rows.insert(0, column_headings(&["", "element"]));
|
rows.insert(0, single_widget_cells(column_headings(&["", "element"])));
|
||||||
|
|
||||||
vec![LayoutGroup::table(rows, false)]
|
vec![LayoutGroup::table_of_cells(rows, false)]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type-dispatched recursion into an attribute value for the Data panel breadcrumb navigation.
|
/// Type-dispatched recursion into an attribute value for the Data panel breadcrumb navigation.
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{#each widgetData.tableWidgets as row}
|
{#each widgetData.tableWidgets as row}
|
||||||
<tr>
|
<tr>
|
||||||
{#each row as widget}
|
{#each row as cell}
|
||||||
<td colspan={row.length < columns ? columns - row.length + 1 : undefined}>
|
<td colspan={row.length < columns ? columns - row.length + 1 : undefined}>
|
||||||
<WidgetSpan direction="row" widgets={[widget]} {layoutTarget} narrow={true} />
|
<WidgetSpan direction="row" widgets={cell} {layoutTarget} narrow={true} />
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Layout, LayoutGroup, WidgetDiff, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper";
|
import type { Layout, LayoutGroup, WidgetDiff, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper";
|
||||||
|
|
||||||
type UIItem = Layout | LayoutGroup | WidgetInstance[] | WidgetInstance;
|
type UIItem = Layout | LayoutGroup | WidgetInstance[][] | WidgetInstance[] | WidgetInstance;
|
||||||
// Updates a widget layout based on a list of updates, giving the new layout by mutating the `layout` argument
|
// Updates a widget layout based on a list of updates, giving the new layout by mutating the `layout` argument
|
||||||
export function patchLayout(layout: /* &mut */ Layout, diffs: WidgetDiff[]) {
|
export function patchLayout(layout: /* &mut */ Layout, diffs: WidgetDiff[]) {
|
||||||
diffs.forEach((update) => {
|
diffs.forEach((update) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user