Diff simple layout changes to avoid excessive DOM replacements (#910)
* Add UI diffs to rust * Clean up some js * Fix lints * Fix test * Remove one unnecessary keyword * Rename to widget path * Rename new_val to new_value * Rename newVal to layoutGroup in createLayoutGroup * Extract get_widget_path to a function * Base skipping on the layout rather than the target * Rename to ResendActiveWidget * Switch info to trace * Add a link to the documentation about Object.assign * knitpick js changes * Add more comments to diff functions Co-authored-by: mfish33 <maxmfishernj@gmail.com>
This commit is contained in:
parent
8d7e6c530e
commit
d742b05d3a
|
|
@ -252,6 +252,7 @@ impl Dispatcher {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::application::Editor;
|
||||
use crate::messages::layout::utility_types::layout_widget::DiffUpdate;
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::test_utils::EditorTestUtils;
|
||||
|
|
@ -570,10 +571,12 @@ mod test {
|
|||
|
||||
for response in responses {
|
||||
// Check for the existence of the file format incompatibility warning dialog after opening the test file
|
||||
if let FrontendMessage::UpdateDialogDetails { layout_target: _, layout } = response {
|
||||
if let LayoutGroup::Row { widgets } = &layout[0] {
|
||||
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
||||
print_problem_to_terminal_on_failure(value);
|
||||
if let FrontendMessage::UpdateDialogDetails { layout_target: _, diff } = response {
|
||||
if let DiffUpdate::SubLayout(sub_layout) = &diff[0].new_value {
|
||||
if let LayoutGroup::Row { widgets } = &sub_layout[0] {
|
||||
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
|
||||
print_problem_to_terminal_on_failure(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::utility_types::{FrontendDocumentDetails, FrontendImageData, MouseCursorIcon};
|
||||
use crate::messages::layout::utility_types::layout_widget::SubLayout;
|
||||
use crate::messages::layout::utility_types::layout_widget::WidgetDiff;
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::menu_widgets::MenuBarEntry;
|
||||
use crate::messages::portfolio::document::node_graph::{FrontendNode, FrontendNodeLink, FrontendNodeType};
|
||||
|
|
@ -143,7 +143,7 @@ pub enum FrontendMessage {
|
|||
UpdateDialogDetails {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateDocumentArtboards {
|
||||
svg: String,
|
||||
|
|
@ -154,7 +154,7 @@ pub enum FrontendMessage {
|
|||
UpdateDocumentBarLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateDocumentLayerDetails {
|
||||
data: LayerPanelEntry,
|
||||
|
|
@ -170,7 +170,7 @@ pub enum FrontendMessage {
|
|||
UpdateDocumentModeLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateDocumentOverlays {
|
||||
svg: String,
|
||||
|
|
@ -208,7 +208,7 @@ pub enum FrontendMessage {
|
|||
UpdateLayerTreeOptionsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateMenuBarLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
|
|
@ -225,7 +225,7 @@ pub enum FrontendMessage {
|
|||
UpdateNodeGraphBarLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateNodeGraphSelection {
|
||||
selected: Vec<NodeId>,
|
||||
|
|
@ -244,26 +244,26 @@ pub enum FrontendMessage {
|
|||
UpdatePropertyPanelOptionsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdatePropertyPanelSectionsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateToolOptionsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateToolShelfLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateWorkingColorsLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
layout: SubLayout,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
#[impl_message(Message, Layout)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum LayoutMessage {
|
||||
RefreshLayout { layout_target: LayoutTarget },
|
||||
ResendActiveWidget { layout_target: LayoutTarget, dirty_id: u64 },
|
||||
SendLayout { layout: Layout, layout_target: LayoutTarget },
|
||||
UpdateLayout { layout_target: LayoutTarget, widget_id: u64, value: serde_json::Value },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use super::utility_types::layout_widget::{LayoutGroup, WidgetDiff, WidgetHolder};
|
||||
use super::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::layout::utility_types::layout_widget::Layout;
|
||||
use crate::messages::layout::utility_types::layout_widget::Widget;
|
||||
use crate::messages::layout::utility_types::layout_widget::{DiffUpdate, Widget};
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, WidgetLayout};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use document_legacy::color::Color;
|
||||
|
|
@ -16,6 +17,32 @@ pub struct LayoutMessageHandler {
|
|||
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
|
||||
}
|
||||
|
||||
impl LayoutMessageHandler {
|
||||
/// Get the widget path for the widget with the specified id
|
||||
fn get_widget_path(widget_layout: &WidgetLayout, id: u64) -> Option<(&WidgetHolder, Vec<usize>)> {
|
||||
let mut stack = widget_layout.layout.iter().enumerate().map(|(index, val)| (vec![index], val)).collect::<Vec<_>>();
|
||||
while let Some((mut widget_path, group)) = stack.pop() {
|
||||
match group {
|
||||
// Check if any of the widgets in the current column or row have the correct id
|
||||
LayoutGroup::Column { widgets } | LayoutGroup::Row { widgets } => {
|
||||
for (index, widget) in widgets.iter().enumerate() {
|
||||
// Return if this is the correct ID
|
||||
if widget.widget_id == id {
|
||||
widget_path.push(index);
|
||||
return Some((widget, widget_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
// A section contains more LayoutGroups which we add to the stack.
|
||||
LayoutGroup::Section { layout, .. } => {
|
||||
stack.extend(layout.iter().enumerate().map(|(index, val)| ([widget_path.as_slice(), &[index]].concat(), val)));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_message(&mut self, message: LayoutMessage, data: F, responses: &mut std::collections::VecDeque<Message>) {
|
||||
|
|
@ -24,14 +51,22 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
use LayoutMessage::*;
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
RefreshLayout { layout_target } => {
|
||||
self.send_layout(layout_target, responses, &action_input_mapping);
|
||||
}
|
||||
SendLayout { layout, layout_target } => {
|
||||
self.layouts[layout_target as usize] = layout;
|
||||
|
||||
self.send_layout(layout_target, responses, &action_input_mapping);
|
||||
ResendActiveWidget { layout_target, dirty_id } => {
|
||||
// Find the updated diff based on the specified layout target
|
||||
let Some(diff) = (match &self.layouts[layout_target as usize] {
|
||||
Layout::MenuLayout(_) => return,
|
||||
Layout::WidgetLayout(layout) => Self::get_widget_path(layout, dirty_id).map(|(widget, widget_path)| {
|
||||
// Create a widget update diff for the relevant id
|
||||
let new_value = DiffUpdate::Widget(widget.clone());
|
||||
WidgetDiff { widget_path, new_value }
|
||||
}),
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
// Resend that diff
|
||||
self.send_diff(vec![diff], layout_target, responses, &action_input_mapping);
|
||||
}
|
||||
SendLayout { layout, layout_target } => self.send_layout(layout_target, layout, responses, &action_input_mapping),
|
||||
UpdateLayout { layout_target, widget_id, value } => {
|
||||
// Look up the layout
|
||||
let layout = if let Some(layout) = self.layouts.get_mut(layout_target as usize) {
|
||||
|
|
@ -84,7 +119,7 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
Some(None)
|
||||
}
|
||||
})()
|
||||
.unwrap_or_else(|| panic!("ColorInput update was not able to be parsed with color data: {:?}", color_input));
|
||||
.unwrap_or_else(|| panic!("ColorInput update was not able to be parsed with color data: {color_input:?}"));
|
||||
color_input.value = parsed_color;
|
||||
let callback_message = (color_input.on_update.callback)(color_input);
|
||||
responses.push_back(callback_message);
|
||||
|
|
@ -197,7 +232,7 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
}
|
||||
Widget::TextLabel(_) => {}
|
||||
};
|
||||
responses.push_back(RefreshLayout { layout_target }.into());
|
||||
responses.push_back(ResendActiveWidget { layout_target, dirty_id: widget_id }.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -208,55 +243,57 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
}
|
||||
|
||||
impl LayoutMessageHandler {
|
||||
/// Diff the update and send to the frontend where necessary
|
||||
fn send_layout(&mut self, layout_target: LayoutTarget, new_layout: Layout, responses: &mut VecDeque<Message>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
// We don't diff the menu bar layout yet.
|
||||
if matches!(new_layout, Layout::MenuLayout(_)) {
|
||||
// Skip update if the same
|
||||
if self.layouts[layout_target as usize] == new_layout {
|
||||
return;
|
||||
}
|
||||
// Update the backend storage
|
||||
self.layouts[layout_target as usize] = new_layout;
|
||||
// Update the UI
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateMenuBarLayout {
|
||||
layout_target,
|
||||
layout: self.layouts[layout_target as usize].clone().unwrap_menu_layout(action_input_mapping).layout,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut widget_diffs = Vec::new();
|
||||
self.layouts[layout_target as usize].diff(new_layout, &mut Vec::new(), &mut widget_diffs);
|
||||
// Skip sending if no diff.
|
||||
if widget_diffs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.send_diff(widget_diffs, layout_target, responses, action_input_mapping);
|
||||
}
|
||||
|
||||
/// Send a diff to the frontend based on the layout target.
|
||||
#[remain::check]
|
||||
fn send_layout(&self, layout_target: LayoutTarget, responses: &mut VecDeque<Message>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
let layout = &self.layouts[layout_target as usize];
|
||||
fn send_diff(&self, mut diff: Vec<WidgetDiff>, layout_target: LayoutTarget, responses: &mut VecDeque<Message>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
diff.iter_mut().for_each(|diff| diff.new_value.apply_shortcut(action_input_mapping));
|
||||
|
||||
trace!("{layout_target:?} diff {diff:#?}");
|
||||
|
||||
#[remain::sorted]
|
||||
let message = match layout_target {
|
||||
LayoutTarget::DialogDetails => FrontendMessage::UpdateDialogDetails {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::DocumentBar => FrontendMessage::UpdateDocumentBarLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::DocumentMode => FrontendMessage::UpdateDocumentModeLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::LayerTreeOptions => FrontendMessage::UpdateLayerTreeOptionsLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_menu_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::NodeGraphBar => FrontendMessage::UpdateNodeGraphBarLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::PropertiesOptions => FrontendMessage::UpdatePropertyPanelOptionsLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::PropertiesSections => FrontendMessage::UpdatePropertyPanelSectionsLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::ToolOptions => FrontendMessage::UpdateToolOptionsLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout {
|
||||
layout_target,
|
||||
layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
|
||||
},
|
||||
LayoutTarget::DialogDetails => FrontendMessage::UpdateDialogDetails { layout_target, diff },
|
||||
LayoutTarget::DocumentBar => FrontendMessage::UpdateDocumentBarLayout { layout_target, diff },
|
||||
LayoutTarget::DocumentMode => FrontendMessage::UpdateDocumentModeLayout { layout_target, diff },
|
||||
LayoutTarget::LayerTreeOptions => FrontendMessage::UpdateLayerTreeOptionsLayout { layout_target, diff },
|
||||
LayoutTarget::MenuBar => unreachable!("Menu bar is not diffed"),
|
||||
LayoutTarget::NodeGraphBar => FrontendMessage::UpdateNodeGraphBarLayout { layout_target, diff },
|
||||
LayoutTarget::PropertiesOptions => FrontendMessage::UpdatePropertyPanelOptionsLayout { layout_target, diff },
|
||||
LayoutTarget::PropertiesSections => FrontendMessage::UpdatePropertyPanelSectionsLayout { layout_target, diff },
|
||||
LayoutTarget::ToolOptions => FrontendMessage::UpdateToolOptionsLayout { layout_target, diff },
|
||||
LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout { layout_target, diff },
|
||||
LayoutTarget::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout { layout_target, diff },
|
||||
|
||||
#[remain::unsorted]
|
||||
LayoutTarget::LayoutTargetLength => panic!("`LayoutTargetLength` is not a valid Layout Target and is used for array indexing"),
|
||||
|
|
|
|||
|
|
@ -34,85 +34,113 @@ pub enum Layout {
|
|||
MenuLayout(MenuLayout),
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn unwrap_widget_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> WidgetLayout {
|
||||
if let Layout::WidgetLayout(mut widget_layout) = self {
|
||||
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
|
||||
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
|
||||
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
|
||||
/// The new value of the UI, sent as part of a diff.
|
||||
///
|
||||
/// An update can represent a single widget or an entire SubLayout, or just a single layout group.
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DiffUpdate {
|
||||
#[serde(rename = "subLayout")]
|
||||
SubLayout(SubLayout),
|
||||
#[serde(rename = "layoutGroup")]
|
||||
LayoutGroup(LayoutGroup),
|
||||
#[serde(rename = "widget")]
|
||||
Widget(WidgetHolder),
|
||||
}
|
||||
|
||||
if let ActionKeys::Keys(_keys) = tooltip_shortcut {
|
||||
if !shortcut_text.is_empty() {
|
||||
if !tooltip.is_empty() {
|
||||
tooltip.push(' ');
|
||||
}
|
||||
tooltip.push('(');
|
||||
tooltip.push_str(&shortcut_text);
|
||||
tooltip.push(')');
|
||||
/// A single change to part of the UI, containing the location of the change and the new value.
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WidgetDiff {
|
||||
/// A path to the change
|
||||
/// e.g. [0, 1, 2] in the properties panel is the first section, second row and third widget.
|
||||
/// An empty path [] shows that the entire panel has changed and is sent when the UI is first created.
|
||||
#[serde(rename = "widgetPath")]
|
||||
pub widget_path: Vec<usize>,
|
||||
/// What the specified part of the UI has changed to.
|
||||
#[serde(rename = "newValue")]
|
||||
pub new_value: DiffUpdate,
|
||||
}
|
||||
|
||||
impl DiffUpdate {
|
||||
/// Append the shortcut to the tooltip where applicable
|
||||
pub fn apply_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
|
||||
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
|
||||
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
|
||||
|
||||
if let ActionKeys::Keys(_keys) = tooltip_shortcut {
|
||||
if !shortcut_text.is_empty() {
|
||||
if !tooltip.is_empty() {
|
||||
tooltip.push(' ');
|
||||
}
|
||||
tooltip.push('(');
|
||||
tooltip.push_str(&shortcut_text);
|
||||
tooltip.push(')');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Go through each widget to convert `ActionKeys::Action` to `ActionKeys::Keys` and append the key combination to the widget tooltip
|
||||
let convert_tooltip = |widget_holder: &mut WidgetHolder| {
|
||||
// Handle all the widgets that have tooltips
|
||||
let mut tooltip_shortcut = match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ColorInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::DropdownInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::FontInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::LayerReferenceInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::NumberInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::OptionalInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::TextButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconLabel(_)
|
||||
| Widget::InvisibleStandinInput(_)
|
||||
| Widget::PivotAssist(_)
|
||||
| Widget::RadioInput(_)
|
||||
| Widget::Separator(_)
|
||||
| Widget::SwatchPairInput(_)
|
||||
| Widget::TextAreaInput(_)
|
||||
| Widget::TextInput(_)
|
||||
| Widget::TextLabel(_) => None,
|
||||
};
|
||||
if let Some((tooltip, Some(tooltip_shortcut))) = &mut tooltip_shortcut {
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
}
|
||||
|
||||
// Go through each widget to convert `ActionKeys::Action` to `ActionKeys::Keys` and append the key combination to the widget tooltip
|
||||
for widget_holder in &mut widget_layout.iter_mut() {
|
||||
// Handle all the widgets that have tooltips
|
||||
let mut tooltip_shortcut = match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ColorInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::DropdownInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::FontInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::LayerReferenceInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::NumberInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::OptionalInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::TextButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
|
||||
Widget::IconLabel(_)
|
||||
| Widget::InvisibleStandinInput(_)
|
||||
| Widget::PivotAssist(_)
|
||||
| Widget::RadioInput(_)
|
||||
| Widget::Separator(_)
|
||||
| Widget::SwatchPairInput(_)
|
||||
| Widget::TextAreaInput(_)
|
||||
| Widget::TextInput(_)
|
||||
| Widget::TextLabel(_) => None,
|
||||
};
|
||||
if let Some((tooltip, Some(tooltip_shortcut))) = &mut tooltip_shortcut {
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
}
|
||||
|
||||
// Handle RadioInput separately because its tooltips are children of the widget
|
||||
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
|
||||
for radio_entry_data in &mut radio_input.entries {
|
||||
if let RadioEntryData {
|
||||
tooltip,
|
||||
tooltip_shortcut: Some(tooltip_shortcut),
|
||||
..
|
||||
} = radio_entry_data
|
||||
{
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
}
|
||||
// Handle RadioInput separately because its tooltips are children of the widget
|
||||
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
|
||||
for radio_entry_data in &mut radio_input.entries {
|
||||
if let RadioEntryData {
|
||||
tooltip,
|
||||
tooltip_shortcut: Some(tooltip_shortcut),
|
||||
..
|
||||
} = radio_entry_data
|
||||
{
|
||||
apply_shortcut_to_tooltip(tooltip_shortcut, tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
widget_layout
|
||||
} else {
|
||||
panic!("Tried to unwrap layout as WidgetLayout. Got {:?}", self)
|
||||
match self {
|
||||
Self::SubLayout(sub_layout) => sub_layout.iter_mut().flat_map(|group| group.iter_mut()).for_each(convert_tooltip),
|
||||
Self::LayoutGroup(layout_group) => layout_group.iter_mut().for_each(convert_tooltip),
|
||||
Self::Widget(widget_holder) => convert_tooltip(widget_holder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn unwrap_menu_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> MenuLayout {
|
||||
if let Layout::MenuLayout(mut menu_layout) = self {
|
||||
for menu_column in &mut menu_layout.layout {
|
||||
menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping);
|
||||
}
|
||||
|
||||
menu_layout
|
||||
if let Self::MenuLayout(mut menu) = self {
|
||||
menu.layout
|
||||
.iter_mut()
|
||||
.for_each(|menu_column| menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping));
|
||||
menu
|
||||
} else {
|
||||
panic!("Tried to unwrap layout as MenuLayout. Got {:?}", self)
|
||||
panic!("Called unwrap_menu_layout on a widget layout");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +157,24 @@ impl Layout {
|
|||
Layout::WidgetLayout(widget_layout) => Box::new(widget_layout.iter_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
||||
pub fn diff(&mut self, new: Self, widget_path: &mut Vec<usize>, widget_diffs: &mut Vec<WidgetDiff>) {
|
||||
match (self, new) {
|
||||
// Simply diff the internal layout
|
||||
(Self::WidgetLayout(current), Self::WidgetLayout(new)) => current.diff(new, widget_path, widget_diffs),
|
||||
(current, Self::WidgetLayout(widget_layout)) => {
|
||||
// Upate current to the new value
|
||||
*current = Self::WidgetLayout(widget_layout.clone());
|
||||
|
||||
// Push an update sublayout value
|
||||
let new_value = DiffUpdate::SubLayout(widget_layout.layout);
|
||||
let widget_path = widget_path.to_vec();
|
||||
widget_diffs.push(WidgetDiff { widget_path, new_value });
|
||||
}
|
||||
(_, Self::MenuLayout(_)) => panic!("Cannot diff menu layout"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Layout {
|
||||
|
|
@ -160,6 +206,30 @@ impl WidgetLayout {
|
|||
current_slice: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
||||
pub fn diff(&mut self, new: Self, widget_path: &mut Vec<usize>, widget_diffs: &mut Vec<WidgetDiff>) {
|
||||
// Check if the length of items is different
|
||||
// TODO: Diff insersion and deletion of items
|
||||
if self.layout.len() != new.layout.len() {
|
||||
// Update the layout to the new layout
|
||||
self.layout = new.layout.clone();
|
||||
|
||||
// Push an update sublayout to the diff
|
||||
let new = DiffUpdate::SubLayout(new.layout);
|
||||
widget_diffs.push(WidgetDiff {
|
||||
widget_path: widget_path.to_vec(),
|
||||
new_value: new,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Diff all of the children
|
||||
for (index, (current_child, new_child)) in self.layout.iter_mut().zip(new.layout.into_iter()).enumerate() {
|
||||
widget_path.push(index);
|
||||
current_child.diff(new_child, widget_path, widget_diffs);
|
||||
widget_path.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -296,6 +366,73 @@ impl LayoutGroup {
|
|||
Self::Row { widgets }
|
||||
}
|
||||
}
|
||||
|
||||
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
||||
pub fn diff(&mut self, new: Self, widget_path: &mut Vec<usize>, widget_diffs: &mut Vec<WidgetDiff>) {
|
||||
let is_column = matches!(new, Self::Column { .. });
|
||||
match (self, new) {
|
||||
(Self::Column { widgets: current_widgets }, Self::Column { widgets: new_widgets }) | (Self::Row { widgets: current_widgets }, Self::Row { widgets: new_widgets }) => {
|
||||
// If the lengths are different then resend the entire panel
|
||||
// TODO: Diff insersion and deletion of items
|
||||
if current_widgets.len() != new_widgets.len() {
|
||||
// Update to the new value
|
||||
*current_widgets = new_widgets.clone();
|
||||
|
||||
// Push back a LayoutGroup update to the diff
|
||||
let new_value = DiffUpdate::LayoutGroup(if is_column { Self::Column { widgets: new_widgets } } else { Self::Row { widgets: new_widgets } });
|
||||
let widget_path = widget_path.to_vec();
|
||||
widget_diffs.push(WidgetDiff { widget_path, new_value });
|
||||
return;
|
||||
}
|
||||
// Diff all of the children
|
||||
for (index, (current_child, new_child)) in current_widgets.iter_mut().zip(new_widgets.into_iter()).enumerate() {
|
||||
widget_path.push(index);
|
||||
current_child.diff(new_child, widget_path, widget_diffs);
|
||||
widget_path.pop();
|
||||
}
|
||||
}
|
||||
(
|
||||
Self::Section {
|
||||
name: current_name,
|
||||
layout: current_layout,
|
||||
},
|
||||
Self::Section { name: new_name, layout: new_layout },
|
||||
) => {
|
||||
// If the lengths are different then resend the entire panel
|
||||
// TODO: Diff insersion and deletion of items
|
||||
if *current_name != new_name || current_layout.len() != new_layout.len() {
|
||||
// Update self to reflect new changes
|
||||
*current_name = new_name.clone();
|
||||
*current_layout = new_layout.clone();
|
||||
|
||||
// Push an update layout group to the diff
|
||||
let new_value = DiffUpdate::LayoutGroup(Self::Section { name: new_name, layout: new_layout });
|
||||
let widget_path = widget_path.to_vec();
|
||||
widget_diffs.push(WidgetDiff { widget_path, new_value });
|
||||
return;
|
||||
}
|
||||
// Diff all of the children
|
||||
for (index, (current_child, new_child)) in current_layout.iter_mut().zip(new_layout.into_iter()).enumerate() {
|
||||
widget_path.push(index);
|
||||
current_child.diff(new_child, widget_path, widget_diffs);
|
||||
widget_path.pop();
|
||||
}
|
||||
}
|
||||
(current, new) => {
|
||||
*current = new.clone();
|
||||
let new_value = DiffUpdate::LayoutGroup(new);
|
||||
let widget_path = widget_path.to_vec();
|
||||
widget_diffs.push(WidgetDiff { widget_path, new_value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> WidgetIterMut<'_> {
|
||||
WidgetIterMut {
|
||||
stack: vec![self],
|
||||
current_slice: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
@ -334,6 +471,22 @@ impl WidgetHolder {
|
|||
..Default::default()
|
||||
}))
|
||||
}
|
||||
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
|
||||
pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) {
|
||||
// If there have been changes to the acutal widget (not just the id)
|
||||
if self.widget != new.widget {
|
||||
// We should update to the new widget value as well as a new widget id
|
||||
*self = new.clone();
|
||||
|
||||
// Push a widget update to the diff
|
||||
let new_value = DiffUpdate::Widget(new);
|
||||
let widget_path = widget_path.to_vec();
|
||||
widget_diffs.push(WidgetDiff { widget_path, new_value });
|
||||
} else {
|
||||
// Required to update the callback function, which the PartialEq check above skips
|
||||
self.widget = new.widget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ import { textInputCleanup } from "@/utility-functions/keyboard-entry";
|
|||
import { rasterizeSVGCanvas } from "@/utility-functions/rasterization";
|
||||
import {
|
||||
defaultWidgetLayout,
|
||||
patchWidgetLayout,
|
||||
type DisplayEditableTextbox,
|
||||
type MouseCursorIcon,
|
||||
type UpdateDocumentBarLayout,
|
||||
|
|
@ -505,19 +506,19 @@ export default defineComponent({
|
|||
},
|
||||
// Update layouts
|
||||
updateDocumentModeLayout(updateDocumentModeLayout: UpdateDocumentModeLayout) {
|
||||
this.documentModeLayout = updateDocumentModeLayout;
|
||||
patchWidgetLayout(this.documentModeLayout, updateDocumentModeLayout);
|
||||
},
|
||||
updateToolOptionsLayout(updateToolOptionsLayout: UpdateToolOptionsLayout) {
|
||||
this.toolOptionsLayout = updateToolOptionsLayout;
|
||||
patchWidgetLayout(this.toolOptionsLayout, updateToolOptionsLayout);
|
||||
},
|
||||
updateDocumentBarLayout(updateDocumentBarLayout: UpdateDocumentBarLayout) {
|
||||
this.documentBarLayout = updateDocumentBarLayout;
|
||||
patchWidgetLayout(this.documentBarLayout, updateDocumentBarLayout);
|
||||
},
|
||||
updateToolShelfLayout(updateToolShelfLayout: UpdateToolShelfLayout) {
|
||||
this.toolShelfLayout = updateToolShelfLayout;
|
||||
patchWidgetLayout(this.toolShelfLayout, updateToolShelfLayout);
|
||||
},
|
||||
updateWorkingColorsLayout(updateWorkingColorsLayout: UpdateWorkingColorsLayout) {
|
||||
this.workingColorsLayout = updateWorkingColorsLayout;
|
||||
patchWidgetLayout(this.workingColorsLayout, updateWorkingColorsLayout);
|
||||
},
|
||||
// Resize elements to render the new viewport size
|
||||
viewportResize() {
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ import {
|
|||
type LayerTypeData,
|
||||
type LayerPanelEntry,
|
||||
defaultWidgetLayout,
|
||||
patchWidgetLayout,
|
||||
UpdateDocumentLayerDetails,
|
||||
UpdateDocumentLayerTreeStructureJs,
|
||||
UpdateLayerTreeOptionsLayout,
|
||||
|
|
@ -523,7 +524,7 @@ export default defineComponent({
|
|||
});
|
||||
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateLayerTreeOptionsLayout, (updateLayerTreeOptionsLayout) => {
|
||||
this.layerTreeOptionsLayout = updateLayerTreeOptionsLayout;
|
||||
patchWidgetLayout(this.layerTreeOptionsLayout, updateLayerTreeOptionsLayout);
|
||||
});
|
||||
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { defaultWidgetLayout, UpdatePropertyPanelOptionsLayout, UpdatePropertyPanelSectionsLayout } from "@/wasm-communication/messages";
|
||||
import { defaultWidgetLayout, patchWidgetLayout, UpdatePropertyPanelOptionsLayout, UpdatePropertyPanelSectionsLayout } from "@/wasm-communication/messages";
|
||||
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
|
|
@ -48,11 +48,11 @@ export default defineComponent({
|
|||
},
|
||||
mounted() {
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdatePropertyPanelOptionsLayout, (updatePropertyPanelOptionsLayout) => {
|
||||
this.propertiesOptionsLayout = updatePropertyPanelOptionsLayout;
|
||||
patchWidgetLayout(this.propertiesOptionsLayout, updatePropertyPanelOptionsLayout);
|
||||
});
|
||||
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdatePropertyPanelSectionsLayout, (updatePropertyPanelSectionsLayout) => {
|
||||
this.propertiesSectionsLayout = updatePropertyPanelSectionsLayout;
|
||||
patchWidgetLayout(this.propertiesSectionsLayout, updatePropertyPanelSectionsLayout);
|
||||
});
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { reactive, readonly } from "vue";
|
|||
|
||||
import { type IconName } from "@/utility-functions/icons";
|
||||
import { type Editor } from "@/wasm-communication/editor";
|
||||
import { type TextButtonWidget, type WidgetLayout, defaultWidgetLayout, DisplayDialog, DisplayDialogDismiss, UpdateDialogDetails } from "@/wasm-communication/messages";
|
||||
import { type TextButtonWidget, type WidgetLayout, defaultWidgetLayout, DisplayDialog, DisplayDialogDismiss, UpdateDialogDetails, patchWidgetLayout } from "@/wasm-communication/messages";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function createDialogState(editor: Editor) {
|
||||
|
|
@ -37,7 +37,7 @@ export function createDialogState(editor: Editor) {
|
|||
state.icon = displayDialog.icon;
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateDialogDetails, (updateDialogDetails) => {
|
||||
state.widgets = updateDialogDetails;
|
||||
patchWidgetLayout(state.widgets, updateDialogDetails);
|
||||
state.jsCallbackBasedButtons = undefined;
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(DisplayDialogDismiss, dismissDialog);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
import { reactive, readonly } from "vue";
|
||||
|
||||
import { type Editor } from "@/wasm-communication/editor";
|
||||
import { type FrontendNode, type FrontendNodeLink, type FrontendNodeType, UpdateNodeGraph, UpdateNodeTypes, UpdateNodeGraphBarLayout, defaultWidgetLayout } from "@/wasm-communication/messages";
|
||||
import {
|
||||
type FrontendNode,
|
||||
type FrontendNodeLink,
|
||||
type FrontendNodeType,
|
||||
UpdateNodeGraph,
|
||||
UpdateNodeTypes,
|
||||
UpdateNodeGraphBarLayout,
|
||||
defaultWidgetLayout,
|
||||
patchWidgetLayout,
|
||||
} from "@/wasm-communication/messages";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function createNodeGraphState(editor: Editor) {
|
||||
|
|
@ -21,7 +30,7 @@ export function createNodeGraphState(editor: Editor) {
|
|||
state.nodeTypes = updateNodeTypes.nodeTypes;
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphBarLayout, (updateNodeGraphBarLayout) => {
|
||||
state.nodeGraphBarLayout = updateNodeGraphBarLayout;
|
||||
patchWidgetLayout(state.nodeGraphBarLayout, updateNodeGraphBarLayout);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1171,18 +1171,20 @@ export class Widget {
|
|||
widgetId!: bigint;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function hoistWidgetHolder(widgetHolder: any): Widget {
|
||||
const kind = Object.keys(widgetHolder.widget)[0];
|
||||
const props = widgetHolder.widget[kind];
|
||||
props.kind = kind;
|
||||
|
||||
const { widgetId } = widgetHolder;
|
||||
|
||||
return plainToClass(Widget, { props, widgetId });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function hoistWidgetHolders(widgetHolders: any[]): Widget[] {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return widgetHolders.map((widgetHolder: any) => {
|
||||
const kind = Object.keys(widgetHolder.widget)[0];
|
||||
const props = widgetHolder.widget[kind];
|
||||
props.kind = kind;
|
||||
|
||||
const { widgetId } = widgetHolder;
|
||||
|
||||
return plainToClass(Widget, { props, widgetId });
|
||||
});
|
||||
return widgetHolders.map(hoistWidgetHolder);
|
||||
}
|
||||
|
||||
// WIDGET LAYOUT
|
||||
|
|
@ -1192,6 +1194,18 @@ export type WidgetLayout = {
|
|||
layout: LayoutGroup[];
|
||||
};
|
||||
|
||||
export class WidgetDiffUpdate extends JsMessage {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
|
||||
diff!: WidgetDiff[];
|
||||
}
|
||||
|
||||
type UIItem = LayoutGroup[] | LayoutGroup | Widget | MenuBarEntry[] | MenuBarEntry;
|
||||
type WidgetDiff = { widgetPath: number[]; newValue: UIItem };
|
||||
|
||||
export function defaultWidgetLayout(): WidgetLayout {
|
||||
return {
|
||||
layoutTarget: undefined,
|
||||
|
|
@ -1199,6 +1213,42 @@ export function defaultWidgetLayout(): WidgetLayout {
|
|||
};
|
||||
}
|
||||
|
||||
// Updates a widget layout based on a list of updates, returning the new layout
|
||||
export function patchWidgetLayout(layout: WidgetLayout, updates: WidgetDiffUpdate): void {
|
||||
layout.layoutTarget = updates.layoutTarget;
|
||||
|
||||
updates.diff.forEach((update) => {
|
||||
// Find the object where the diff applies to
|
||||
const diffObject = update.widgetPath.reduce((targetLayout, index) => {
|
||||
if ("columnWidgets" in targetLayout) return targetLayout.columnWidgets[index];
|
||||
if ("rowWidgets" in targetLayout) return targetLayout.rowWidgets[index];
|
||||
if ("layout" in targetLayout) return targetLayout.layout[index];
|
||||
if (targetLayout instanceof Widget) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Tried to index widget");
|
||||
return targetLayout;
|
||||
}
|
||||
// This is a path traversal so we can assume from the backend that it exists
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
if ("action" in targetLayout) return targetLayout.children![index];
|
||||
return targetLayout[index];
|
||||
}, layout.layout as UIItem);
|
||||
|
||||
// If this is a list with a length, then set the length to 0 to clear the list
|
||||
if ("length" in diffObject) {
|
||||
diffObject.length = 0;
|
||||
}
|
||||
// Remove all of the keys from the old object
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
|
||||
|
||||
// Assign keys to the new object
|
||||
// `Object.assign` works but `diffObject = update.newValue;` doesn't.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
Object.assign(diffObject, update.newValue);
|
||||
});
|
||||
}
|
||||
|
||||
export type LayoutGroup = WidgetRow | WidgetColumn | WidgetSection;
|
||||
|
||||
export type WidgetColumn = { columnWidgets: Widget[] };
|
||||
|
|
@ -1218,116 +1268,66 @@ export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSect
|
|||
|
||||
// Unpacking rust types to more usable type in the frontend
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createWidgetLayout(widgetLayout: any[]): LayoutGroup[] {
|
||||
return widgetLayout.map((layoutType): LayoutGroup => {
|
||||
if (layoutType.column) {
|
||||
const columnWidgets = hoistWidgetHolders(layoutType.column.columnWidgets);
|
||||
|
||||
const result: WidgetColumn = { columnWidgets };
|
||||
return result;
|
||||
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
||||
return diffs.map((diff) => {
|
||||
const { widgetPath, newValue } = diff;
|
||||
if (newValue.subLayout) {
|
||||
return { widgetPath, newValue: newValue.subLayout.map(createLayoutGroup) };
|
||||
}
|
||||
|
||||
if (layoutType.row) {
|
||||
const rowWidgets = hoistWidgetHolders(layoutType.row.rowWidgets);
|
||||
|
||||
const result: WidgetRow = { rowWidgets };
|
||||
return result;
|
||||
if (newValue.layoutGroup) {
|
||||
return { widgetPath, newValue: createLayoutGroup(newValue.layoutGroup) };
|
||||
}
|
||||
|
||||
if (layoutType.section) {
|
||||
const { name } = layoutType.section;
|
||||
const layout = createWidgetLayout(layoutType.section.layout);
|
||||
|
||||
const result: WidgetSection = { name, layout };
|
||||
return result;
|
||||
if (newValue.widget) {
|
||||
return { widgetPath, newValue: hoistWidgetHolder(newValue.widget) };
|
||||
}
|
||||
|
||||
throw new Error("Layout row type does not exist");
|
||||
// This code should be unreachable
|
||||
throw new Error("DiffUpdate invalid");
|
||||
});
|
||||
}
|
||||
|
||||
// Unpacking a layout group
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
||||
if (layoutGroup.column) {
|
||||
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
|
||||
|
||||
const result: WidgetColumn = { columnWidgets };
|
||||
return result;
|
||||
}
|
||||
|
||||
if (layoutGroup.row) {
|
||||
const result: WidgetRow = { rowWidgets: hoistWidgetHolders(layoutGroup.row.rowWidgets) };
|
||||
return result;
|
||||
}
|
||||
|
||||
if (layoutGroup.section) {
|
||||
const result: WidgetSection = { name: layoutGroup.section.name, layout: layoutGroup.section.layout.map(createLayoutGroup) };
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new Error("Layout row type does not exist");
|
||||
}
|
||||
|
||||
// WIDGET LAYOUTS
|
||||
export class UpdateDialogDetails extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateDialogDetails extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
export class UpdateDocumentModeLayout extends WidgetDiffUpdate {}
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
export class UpdateToolOptionsLayout extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateDocumentModeLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
export class UpdateDocumentBarLayout extends WidgetDiffUpdate {}
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateToolOptionsLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
export class UpdatePropertyPanelOptionsLayout extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateDocumentBarLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
export class UpdatePropertyPanelSectionsLayout extends WidgetDiffUpdate {}
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
export class UpdateLayerTreeOptionsLayout extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateToolShelfLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
|
||||
export class UpdateWorkingColorsLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
|
||||
export class UpdatePropertyPanelOptionsLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
|
||||
export class UpdatePropertyPanelSectionsLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
|
||||
export class UpdateLayerTreeOptionsLayout extends JsMessage implements WidgetLayout {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
export class UpdateNodeGraphBarLayout extends WidgetDiffUpdate {}
|
||||
|
||||
export class UpdateMenuBarLayout extends JsMessage {
|
||||
layoutTarget!: unknown;
|
||||
|
|
@ -1338,15 +1338,6 @@ export class UpdateMenuBarLayout extends JsMessage {
|
|||
layout!: MenuBarEntry[];
|
||||
}
|
||||
|
||||
export class UpdateNodeGraphBarLayout extends JsMessage {
|
||||
layoutTarget!: unknown;
|
||||
|
||||
// TODO: Replace `any` with correct typing
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@Transform(({ value }: { value: any }) => createWidgetLayout(value))
|
||||
layout!: LayoutGroup[];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] {
|
||||
return menuBarEntry.map((entry) => ({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
[target.wasm32-unknown-unknown]
|
||||
rustflags = ["-C", "target-feature=+simd128,+atomics,+bulk-memory,+mutable-globals"]
|
||||
|
||||
[unstable]
|
||||
build-std = ["panic_abort", "std"]
|
||||
Loading…
Reference in New Issue