Unwrap the Layout enum to replace it with the WidgetLayout struct now called Layout (#3448)

This commit is contained in:
Keavon Chambers 2025-12-04 15:24:40 -08:00 committed by GitHub
parent 3c4ad8b720
commit 783ea0b437
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 205 additions and 254 deletions

View file

@ -120,7 +120,7 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
[
WidgetDiff {
widget_path,
new_value: DiffUpdate::WidgetLayout(layout),
new_value: DiffUpdate::Layout(layout),
},
] if widget_path.is_empty() => {
let entries = crate::utils::menu::convert_menu_bar_layout_to_menu_items(layout);

View file

@ -6,11 +6,11 @@ pub(crate) mod menu {
use graphite_editor::messages::input_mapper::utility_types::input_keyboard::{Key, LabeledKey, LabeledShortcut};
use graphite_editor::messages::input_mapper::utility_types::misc::ActionShortcut;
use graphite_editor::messages::layout::LayoutMessage;
use graphite_editor::messages::tool::tool_messages::tool_prelude::{LayoutGroup, LayoutTarget, MenuListEntry, Widget, WidgetId, WidgetLayout};
use graphite_editor::messages::tool::tool_messages::tool_prelude::{Layout, LayoutGroup, LayoutTarget, MenuListEntry, Widget, WidgetId};
use crate::messages::{EditorMessage, KeyCode, MenuItem, Modifiers, Shortcut};
pub(crate) fn convert_menu_bar_layout_to_menu_items(layout: &WidgetLayout) -> Vec<MenuItem> {
pub(crate) fn convert_menu_bar_layout_to_menu_items(layout: &Layout) -> Vec<MenuItem> {
let layout_group = match layout.as_slice() {
[layout_group] => layout_group,
_ => panic!("Menu bar layout is supposed to have exactly one layout group"),

View file

@ -548,7 +548,7 @@ 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::UpdateDialogColumn1 { diff } = response {
if let DiffUpdate::WidgetLayout(sub_layout) = &diff[0].new_value {
if let DiffUpdate::Layout(sub_layout) = &diff[0].new_value {
if let LayoutGroup::Row { widgets } = &sub_layout.0[0] {
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
print_problem_to_terminal_on_failure(value);

View file

@ -76,7 +76,7 @@ impl DialogLayoutHolder for ExportDialogMessageHandler {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -160,11 +160,11 @@ impl LayoutHolder for ExportDialogMessageHandler {
.widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row { widgets: export_type },
LayoutGroup::Row { widgets: resolution },
LayoutGroup::Row { widgets: export_area },
LayoutGroup::Row { widgets: transparent_background },
]))
])
}
}

View file

@ -66,7 +66,7 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -117,10 +117,6 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
.widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![
LayoutGroup::Row { widgets: name },
LayoutGroup::Row { widgets: infinite },
LayoutGroup::Row { widgets: scale },
]))
Layout(vec![LayoutGroup::Row { widgets: name }, LayoutGroup::Row { widgets: infinite }, LayoutGroup::Row { widgets: scale }])
}
}

View file

@ -224,7 +224,7 @@ impl PreferencesDialogMessageHandler {
.widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row { widgets: navigation_header },
LayoutGroup::Row { widgets: zoom_rate_label },
LayoutGroup::Row { widgets: zoom_rate },
@ -237,7 +237,7 @@ impl PreferencesDialogMessageHandler {
LayoutGroup::Row { widgets: graph_wire_style },
LayoutGroup::Row { widgets: use_vello },
LayoutGroup::Row { widgets: vector_meshes },
]))
])
}
pub fn send_layout(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget, preferences: &PreferencesMessageHandler) {
@ -272,7 +272,7 @@ impl PreferencesDialogMessageHandler {
TextButton::new("Reset to Defaults").on_update(|_| PreferencesMessage::ResetToDefaults.into()).widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
fn send_layout_buttons(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget) {

View file

@ -15,7 +15,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
fn layout_column_2(&self) -> Layout {
@ -51,13 +51,13 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
.widget_instance(),
);
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Column { widgets }]))
Layout(vec![LayoutGroup::Column { widgets }])
}
}
impl LayoutHolder for AboutGraphiteDialog {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("About this release").bold(true).widget_instance()],
},
@ -67,6 +67,6 @@ impl LayoutHolder for AboutGraphiteDialog {
LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("Copyright © {} Graphite contributors", self.localized_commit_year)).widget_instance()],
},
]))
])
}
}

View file

@ -24,7 +24,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -32,13 +32,13 @@ impl LayoutHolder for CloseAllDocumentsDialog {
fn layout(&self) -> Layout {
let unsaved_list = "".to_string() + &self.unsaved_document_names.join("\n");
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Save documents before closing them?").bold(true).multiline(true).widget_instance()],
},
LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("Documents with unsaved changes:\n{unsaved_list}")).multiline(true).widget_instance()],
},
]))
])
}
}

View file

@ -35,7 +35,7 @@ impl DialogLayoutHolder for CloseDocumentDialog {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -51,13 +51,13 @@ impl LayoutHolder for CloseDocumentDialog {
let break_lines = if self.document_name.len() > max_one_line_length { '\n' } else { ' ' };
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Save document before closing it?").bold(true).widget_instance()],
},
LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_instance()],
},
]))
])
}
}

View file

@ -13,7 +13,7 @@ impl DialogLayoutHolder for ComingSoonDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -43,6 +43,6 @@ impl LayoutHolder for ComingSoonDialog {
rows.push(LayoutGroup::Row { widgets: row3 });
}
Layout::WidgetLayout(WidgetLayout(rows))
Layout(rows)
}
}

View file

@ -22,7 +22,7 @@ impl DialogLayoutHolder for DemoArtworkDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("Close").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -59,6 +59,6 @@ impl LayoutHolder for DemoArtworkDialog {
.collect();
let _ = rows_of_images_with_buttons.pop();
Layout::WidgetLayout(WidgetLayout(rows_of_images_with_buttons))
Layout(rows_of_images_with_buttons)
}
}

View file

@ -14,19 +14,19 @@ impl DialogLayoutHolder for ErrorDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
impl LayoutHolder for ErrorDialog {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new(&self.title).bold(true).widget_instance()],
},
LayoutGroup::Row {
widgets: vec![TextLabel::new(&self.description).multiline(true).widget_instance()],
},
]))
])
}
}

View file

@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
fn layout_column_2(&self) -> Layout {
@ -43,7 +43,7 @@ impl DialogLayoutHolder for LicensesDialog {
.map(|&(icon, label, message_factory)| TextButton::new(label).icon(Some((icon).into())).flush(true).on_update(move |_| message_factory()).widget_instance())
.collect();
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Column { widgets }]))
Layout(vec![LayoutGroup::Column { widgets }])
}
}
@ -63,13 +63,13 @@ impl LayoutHolder for LicensesDialog {
);
let description = description.trim();
Layout::WidgetLayout(WidgetLayout(vec![
Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Graphite is free, open source software").bold(true).widget_instance()],
},
LayoutGroup::Row {
widgets: vec![TextLabel::new(description).multiline(true).widget_instance()],
},
]))
])
}
}

View file

@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesThirdPartyDialog {
fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}
@ -31,7 +31,7 @@ impl LayoutHolder for LicensesThirdPartyDialog {
// Two characters (one before, one after) the sequence of underscore characters, plus one additional column to provide a space between the text and the scrollbar
let non_wrapping_column_width = license_text.split('\n').map(|line| line.chars().filter(|&c| c == '_').count()).max().unwrap_or(0) + 2 + 1;
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row {
Layout(vec![LayoutGroup::Row {
widgets: vec![
TextLabel::new(license_text)
.monospace(true)
@ -39,6 +39,6 @@ impl LayoutHolder for LicensesThirdPartyDialog {
.min_width(format!("{non_wrapping_column_width}ch"))
.widget_instance(),
],
}]))
}])
}
}

View file

@ -24,12 +24,10 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
match message {
LayoutMessage::ResendActiveWidget { layout_target, widget_id } => {
// Find the updated diff based on the specified layout target
let Some(diff) = (match &self.layouts[layout_target as usize] {
Layout::WidgetLayout(layout) => Self::get_widget_path(layout, widget_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 }
}),
let Some(diff) = Self::get_widget_path(&self.layouts[layout_target as usize], widget_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;
};
@ -41,7 +39,7 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
}
LayoutMessage::DestroyLayout { layout_target } => {
if let Some(layout) = self.layouts.get_mut(layout_target as usize) {
*layout = Default::default();
*layout = Layout::default();
}
}
LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
@ -61,7 +59,7 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
impl LayoutMessageHandler {
/// Get the widget path for the widget with the specified id
fn get_widget_path(widget_layout: &WidgetLayout, widget_id: WidgetId) -> Option<(&WidgetInstance, Vec<usize>)> {
fn get_widget_path(widget_layout: &Layout, widget_id: WidgetId) -> Option<(&WidgetInstance, Vec<usize>)> {
let mut stack = widget_layout.0.iter().enumerate().map(|(index, val)| (vec![index], val)).collect::<Vec<_>>();
while let Some((mut widget_path, layout_group)) = stack.pop() {
match layout_group {
@ -124,10 +122,7 @@ impl LayoutMessageHandler {
return;
};
let mut layout_iter = match layout {
Layout::WidgetLayout(widget_layout) => widget_layout.iter_mut(),
};
let Some(widget_instance) = layout_iter.find(|widget| widget.widget_id == widget_id) else {
let Some(widget_instance) = layout.iter_mut().find(|widget| widget.widget_id == widget_id) else {
warn!("handle_widget_callback was called referencing an invalid widget ID, although the layout target was valid. `widget_id: {widget_id}`, `layout_target: {layout_target:?}`",);
return;
};
@ -479,31 +474,25 @@ impl LayoutMessageHandler {
responses: &mut VecDeque<Message>,
action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>,
) {
match new_layout {
Layout::WidgetLayout(_) => {
let mut widget_diffs = Vec::new();
let mut widget_diffs = Vec::new();
let Layout::WidgetLayout(current) = &mut self.layouts[layout_target as usize];
let Layout::WidgetLayout(new) = new_layout;
current.diff(new, &mut Vec::new(), &mut widget_diffs);
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;
}
// On Mac we need the full MenuBar layout to construct the native menu
#[cfg(target_os = "macos")]
if layout_target == LayoutTarget::MenuBar {
widget_diffs = vec![WidgetDiff {
widget_path: Vec::new(),
new_value: DiffUpdate::WidgetLayout(current.layout.clone()),
}];
}
self.send_diff(widget_diffs, layout_target, responses, action_input_mapping);
}
// Skip sending if no diff
if widget_diffs.is_empty() {
return;
}
// On Mac we need the full MenuBar layout to construct the native menu
#[cfg(target_os = "macos")]
if layout_target == LayoutTarget::MenuBar {
widget_diffs = vec![WidgetDiff {
widget_path: Vec::new(),
new_value: DiffUpdate::Layout(current.layout.clone()),
}];
}
self.send_diff(widget_diffs, layout_target, responses, action_input_mapping);
}
/// Send a diff to the frontend based on the layout target.

View file

@ -103,24 +103,11 @@ pub trait DialogLayoutHolder: LayoutHolder {
}
}
// TODO: Unwrap this enum
/// Wraps a choice of layout type. The chosen layout contains an arrangement of widgets mounted somewhere specific in the frontend.
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum Layout {
WidgetLayout(WidgetLayout),
}
impl Default for Layout {
fn default() -> Self {
Self::WidgetLayout(WidgetLayout::default())
}
}
// TODO: Unwrap this struct
/// Contains an arrangement of widgets mounted somewhere specific in the frontend.
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, specta::Type)]
pub struct WidgetLayout(pub Vec<LayoutGroup>);
pub struct Layout(pub Vec<LayoutGroup>);
impl WidgetLayout {
impl Layout {
pub fn iter(&self) -> WidgetIter<'_> {
WidgetIter {
stack: self.0.iter().collect(),
@ -144,7 +131,7 @@ impl WidgetLayout {
self.0.clone_from(&new.0);
// Push an update sublayout to the diff
let new = DiffUpdate::WidgetLayout(new);
let new = DiffUpdate::Layout(new);
widget_diffs.push(WidgetDiff {
widget_path: widget_path.to_vec(),
new_value: new,
@ -285,7 +272,7 @@ pub enum LayoutGroup {
visible: bool,
pinned: bool,
id: u64,
layout: WidgetLayout,
layout: Layout,
},
}
@ -588,12 +575,10 @@ pub struct WidgetDiff {
}
/// The new value of the UI, sent as part of a diff.
///
/// An update can represent a single widget or an entire WidgetLayout, or just a single layout group.
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum DiffUpdate {
#[serde(rename = "widgetLayout")]
WidgetLayout(WidgetLayout),
#[serde(rename = "layout")]
Layout(Layout),
#[serde(rename = "layoutGroup")]
LayoutGroup(LayoutGroup),
#[serde(rename = "widget")]
@ -675,7 +660,7 @@ impl DiffUpdate {
};
match self {
Self::WidgetLayout(widget_layout) => widget_layout.0.iter_mut().flat_map(|layout_group| layout_group.iter_mut()).for_each(|widget_instance| {
Self::Layout(layout) => layout.0.iter_mut().flat_map(|layout_group| layout_group.iter_mut()).for_each(|widget_instance| {
convert_tooltip(widget_instance);
convert_menu_lists(widget_instance);
}),

View file

@ -63,7 +63,7 @@ pub struct PopoverButton {
pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "popoverLayout")]
pub popover_layout: WidgetLayout,
pub popover_layout: Layout,
#[serde(rename = "popoverMinWidth")]
pub popover_min_width: Option<u32>,

View file

@ -1,5 +1,5 @@
use super::VectorTableTab;
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, LayoutTarget, WidgetLayout};
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, LayoutTarget};
use crate::messages::portfolio::document::data_panel::DataPanelMessage;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
@ -83,11 +83,12 @@ impl DataPanelMessageHandler {
};
// Main data visualization
let mut layout = self
.introspected_data
.as_ref()
.map(|instrospected_data| generate_layout(instrospected_data, &mut layout_data).unwrap_or_else(|| label("Visualization of this data type is not yet supported")))
.unwrap_or_default();
let mut layout = Layout(
self.introspected_data
.as_ref()
.map(|instrospected_data| generate_layout(instrospected_data, &mut layout_data).unwrap_or_else(|| label("Visualization of this data type is not yet supported")))
.unwrap_or_default(),
);
let mut widgets = Vec::new();
@ -127,11 +128,11 @@ impl DataPanelMessageHandler {
}
if !widgets.is_empty() {
layout.insert(0, LayoutGroup::Row { widgets });
layout.0.insert(0, LayoutGroup::Row { widgets });
}
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(layout)),
layout,
layout_target: LayoutTarget::DataPanel,
});
}

View file

@ -323,17 +323,17 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
// Clear the control bar
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()),
layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelControlLeftBar,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()),
layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelControlRightBar,
});
// Clear the bottom bar
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()),
layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelBottomBar,
});
}
@ -2175,7 +2175,7 @@ impl DocumentMessageHandler {
pub fn update_document_widgets(&self, responses: &mut VecDeque<Message>, animation_is_playing: bool, time: Duration) {
// Document mode (dropdown menu at the left of the bar above the viewport, before the tool options)
let document_mode_layout = WidgetLayout(vec![LayoutGroup::Row {
let layout = Layout(vec![LayoutGroup::Row {
widgets: vec![
// DropdownInput::new(
// vec![vec![
@ -2200,7 +2200,7 @@ impl DocumentMessageHandler {
}]);
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(document_mode_layout),
layout,
layout_target: LayoutTarget::DocumentMode,
});
@ -2235,7 +2235,7 @@ impl DocumentMessageHandler {
})
.widget_instance(),
PopoverButton::new()
.popover_layout(WidgetLayout(vec![
.popover_layout(Layout(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Overlays").bold(true).widget_instance()],
},
@ -2484,7 +2484,7 @@ impl DocumentMessageHandler {
})
.widget_instance(),
PopoverButton::new()
.popover_layout(WidgetLayout(
.popover_layout(Layout(
[
LayoutGroup::Row {
widgets: vec![TextLabel::new("Snapping").bold(true).widget_instance()],
@ -2548,7 +2548,7 @@ impl DocumentMessageHandler {
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
.widget_instance(),
PopoverButton::new()
.popover_layout(WidgetLayout(overlay_options(&self.snapping_state.grid)))
.popover_layout(Layout(overlay_options(&self.snapping_state.grid)))
.popover_min_width(Some(320))
.widget_instance(),
Separator::new(SeparatorType::Unrelated).widget_instance(),
@ -2574,7 +2574,7 @@ impl DocumentMessageHandler {
.narrow(true)
.widget_instance(),
// PopoverButton::new().popover_layout(
// WidgetLayout(vec![
// Layout(vec![
// LayoutGroup::Row {
// widgets: vec![TextLabel::new("Render Mode").bold(true).widget_instance()],
// },
@ -2632,10 +2632,8 @@ impl DocumentMessageHandler {
.widget_instance(),
]);
let document_bar_layout = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(document_bar_layout),
layout: Layout(vec![LayoutGroup::Row { widgets }]),
layout_target: LayoutTarget::DocumentBar,
});
responses.add(NodeGraphMessage::RunDocumentGraph);
@ -2773,7 +2771,7 @@ impl DocumentMessageHandler {
.tooltip_label("Fill")
.widget_instance(),
];
let layers_panel_control_bar_left = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
let layers_panel_control_bar_left = Layout(vec![LayoutGroup::Row { widgets }]);
let widgets = vec![
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
@ -2791,14 +2789,14 @@ impl DocumentMessageHandler {
.disabled(!has_selection)
.widget_instance(),
];
let layers_panel_control_bar_right = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
let layers_panel_control_bar_right = Layout(vec![LayoutGroup::Row { widgets }]);
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_control_bar_left),
layout: layers_panel_control_bar_left,
layout_target: LayoutTarget::LayersPanelControlLeftBar,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_control_bar_right),
layout: layers_panel_control_bar_right,
layout_target: LayoutTarget::LayersPanelControlRightBar,
});
}
@ -2845,7 +2843,7 @@ impl DocumentMessageHandler {
}
})
.widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
})
.widget_instance(),
Separator::new(SeparatorType::Unrelated).widget_instance(),
@ -2870,10 +2868,8 @@ impl DocumentMessageHandler {
.disabled(!has_selection)
.widget_instance(),
];
let layers_panel_bottom_bar = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_bottom_bar),
layout: Layout(vec![LayoutGroup::Row { widgets }]),
layout_target: LayoutTarget::LayersPanelBottomBar,
});
}

View file

@ -2069,7 +2069,7 @@ impl NodeGraphMessageHandler {
/// Send the cached layout to the frontend for the control bar at the top of the node panel
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(self.widgets.to_vec())),
layout: Layout(self.widgets.to_vec()),
layout_target: LayoutTarget::NodeGraphControlBar,
});
}
@ -2145,7 +2145,7 @@ impl NodeGraphMessageHandler {
}
})
.widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
})
.widget_instance(),
//
@ -2443,7 +2443,7 @@ impl NodeGraphMessageHandler {
.into()
})
.widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
})
.widget_instance(),
Separator::new(SeparatorType::Related).widget_instance(),

View file

@ -1699,7 +1699,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
visible,
pinned,
id: node_id.0,
layout: WidgetLayout(layout),
layout: Layout(layout),
}
}

View file

@ -35,7 +35,7 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
match message {
PropertiesPanelMessage::Clear => {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![])),
layout: Layout::default(),
layout_target: LayoutTarget::PropertiesPanel,
});
}
@ -53,10 +53,10 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
document_name,
executor,
};
let properties_sections = NodeGraphMessageHandler::collate_properties(&mut node_properties_context);
let layout = Layout(NodeGraphMessageHandler::collate_properties(&mut node_properties_context));
node_properties_context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(properties_sections)),
layout,
layout_target: LayoutTarget::PropertiesPanel,
});
}

View file

@ -736,6 +736,6 @@ impl LayoutHolder for MenuBarMessageHandler {
.widget_instance(),
];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets: menu_bar_buttons }]))
Layout(vec![LayoutGroup::Row { widgets: menu_bar_buttons }])
}
}

View file

@ -924,7 +924,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
layout_target: LayoutTarget::WelcomeScreenButtons,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![table])),
layout: Layout(vec![table]),
layout_target: LayoutTarget::WelcomeScreenButtons,
});
}

View file

@ -75,7 +75,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Artb
impl LayoutHolder for ArtboardTool {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default())
Layout::default()
}
}

View file

@ -220,7 +220,7 @@ impl LayoutHolder for BrushTool {
.widget_instance(),
);
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -35,7 +35,7 @@ impl ToolMetadata for EyedropperTool {
impl LayoutHolder for EyedropperTool {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default())
Layout::default()
}
}

View file

@ -37,7 +37,7 @@ impl ToolMetadata for FillTool {
impl LayoutHolder for FillTool {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default())
Layout::default()
}
}

View file

@ -151,7 +151,7 @@ impl LayoutHolder for FreehandTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -107,7 +107,7 @@ impl LayoutHolder for GradientTool {
.selected_index(Some((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32))
.widget_instance();
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets: vec![gradient_type] }]))
Layout(vec![LayoutGroup::Row { widgets: vec![gradient_type] }])
}
}

View file

@ -34,7 +34,7 @@ impl ToolMetadata for NavigateTool {
impl LayoutHolder for NavigateTool {
fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default())
Layout::default()
}
}

View file

@ -342,7 +342,7 @@ impl LayoutHolder for PathTool {
let _pin_pivot = pin_pivot_widget(self.tool_data.pivot_gizmo.pin_active(), false, PivotToolSource::Path);
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row {
Layout(vec![LayoutGroup::Row {
widgets: vec![
x_location,
related_seperator.clone(),
@ -367,7 +367,7 @@ impl LayoutHolder for PathTool {
// related_seperator.clone(),
// pin_pivot,
],
}]))
}])
}
}

View file

@ -238,7 +238,7 @@ impl LayoutHolder for PenTool {
.widget_instance(),
);
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -252,7 +252,7 @@ impl LayoutHolder for SelectTool {
widgets.extend(self.alignment_widgets(disabled));
// widgets.push(
// PopoverButton::new()
// .popover_layout(WidgetLayout(vec![
// .popover_layout(Layout(vec![
// LayoutGroup::Row {
// widgets: vec![TextLabel::new("Align").bold(true).widget_instance()],
// },
@ -277,7 +277,7 @@ impl LayoutHolder for SelectTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -335,7 +335,7 @@ impl LayoutHolder for ShapeTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -158,7 +158,7 @@ impl LayoutHolder for SplineTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -203,7 +203,7 @@ impl LayoutHolder for TextTool {
},
));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
}

View file

@ -117,7 +117,7 @@ pub struct DocumentToolData {
impl DocumentToolData {
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
let layout = WidgetLayout(vec![
let layout = Layout(vec![
LayoutGroup::Row {
widgets: vec![WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_instance()],
},
@ -138,7 +138,7 @@ impl DocumentToolData {
]);
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layout),
layout,
layout_target: LayoutTarget::WorkingColors,
});
@ -290,7 +290,7 @@ impl LayoutHolder for ToolData {
.skip(1)
.collect();
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets: tool_groups_layout }]))
Layout(vec![LayoutGroup::Row { widgets: tool_groups_layout }])
}
}
@ -545,7 +545,7 @@ impl HintData {
}
}
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }]))
Layout(vec![LayoutGroup::Row { widgets }])
}
pub fn send_layout(&self, responses: &mut VecDeque<Message>) {
@ -557,7 +557,7 @@ impl HintData {
pub fn clear_layout(responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![])),
layout: Layout::default(),
layout_target: LayoutTarget::StatusBarHints,
});
}

View file

@ -2,18 +2,18 @@
import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor";
import { patchWidgetLayout, UpdateDataPanelLayout, type LayoutGroup } from "@graphite/messages";
import { patchLayout, UpdateDataPanelLayout, type Layout } from "@graphite/messages";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor");
let dataPanelLayout: LayoutGroup[] = [];
let dataPanelLayout: Layout = [];
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateDataPanelLayout, (updateDataPanelLayout) => {
patchWidgetLayout(dataPanelLayout, updateDataPanelLayout);
patchLayout(dataPanelLayout, updateDataPanelLayout);
dataPanelLayout = dataPanelLayout;
});
});

View file

@ -4,14 +4,14 @@
import { shortcutAltClick } from "@graphite/../wasm/pkg/graphite_wasm";
import type { Editor } from "@graphite/editor";
import {
patchWidgetLayout,
patchLayout,
UpdateDocumentLayerDetails,
UpdateDocumentLayerStructureJs,
UpdateLayersPanelControlBarLeftLayout,
UpdateLayersPanelControlBarRightLayout,
UpdateLayersPanelBottomBarLayout,
} from "@graphite/messages";
import type { ActionShortcut, DataBuffer, LayerPanelEntry, LayoutGroup } from "@graphite/messages";
import type { ActionShortcut, DataBuffer, LayerPanelEntry, Layout } from "@graphite/messages";
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
import { operatingSystem } from "@graphite/utility-functions/platform";
import { extractPixelData } from "@graphite/utility-functions/rasterization";
@ -69,25 +69,25 @@
let layerToClipAltKeyPressed = false;
// Layouts
let layersPanelControlBarLeftLayout: LayoutGroup[] = [];
let layersPanelControlBarRightLayout: LayoutGroup[] = [];
let layersPanelBottomBarLayout: LayoutGroup[] = [];
let layersPanelControlBarLeftLayout: Layout = [];
let layersPanelControlBarRightLayout: Layout = [];
let layersPanelBottomBarLayout: Layout = [];
const altClickKeys: ActionShortcut = shortcutAltClick();
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => {
patchWidgetLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
patchLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
layersPanelControlBarLeftLayout = layersPanelControlBarLeftLayout;
});
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarRightLayout, (updateLayersPanelControlBarRightLayout) => {
patchWidgetLayout(layersPanelControlBarRightLayout, updateLayersPanelControlBarRightLayout);
patchLayout(layersPanelControlBarRightLayout, updateLayersPanelControlBarRightLayout);
layersPanelControlBarRightLayout = layersPanelControlBarRightLayout;
});
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelBottomBarLayout, (updateLayersPanelBottomBarLayout) => {
patchWidgetLayout(layersPanelBottomBarLayout, updateLayersPanelBottomBarLayout);
patchLayout(layersPanelBottomBarLayout, updateLayersPanelBottomBarLayout);
layersPanelBottomBarLayout = layersPanelBottomBarLayout;
});

View file

@ -2,18 +2,18 @@
import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor";
import { patchWidgetLayout, UpdatePropertiesPanelLayout, type LayoutGroup } from "@graphite/messages";
import { patchLayout, UpdatePropertiesPanelLayout, type Layout } from "@graphite/messages";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor");
let propertiesPanelLayout: LayoutGroup[] = [];
let propertiesPanelLayout: Layout = [];
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdatePropertiesPanelLayout, (updatePropertiesPanelLayout) => {
patchWidgetLayout(propertiesPanelLayout, updatePropertiesPanelLayout);
patchLayout(propertiesPanelLayout, updatePropertiesPanelLayout);
propertiesPanelLayout = propertiesPanelLayout;
});
});

View file

@ -2,8 +2,8 @@
import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages";
import { patchWidgetLayout, UpdateWelcomeScreenButtonsLayout } from "@graphite/messages";
import type { Layout } from "@graphite/messages";
import { patchLayout, UpdateWelcomeScreenButtonsLayout } from "@graphite/messages";
import { extractPixelData } from "@graphite/utility-functions/rasterization";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
@ -14,11 +14,11 @@
const editor = getContext<Editor>("editor");
let welcomePanelButtonsLayout: LayoutGroup[] = [];
let welcomePanelButtonsLayout: Layout = [];
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateWelcomeScreenButtonsLayout, (updateWelcomeScreenButtonsLayout) => {
patchWidgetLayout(welcomePanelButtonsLayout, updateWelcomeScreenButtonsLayout);
patchLayout(welcomePanelButtonsLayout, updateWelcomeScreenButtonsLayout);
welcomePanelButtonsLayout = welcomePanelButtonsLayout;
});

View file

@ -1,11 +1,11 @@
<script lang="ts">
import { isWidgetSpanColumn, isWidgetSpanRow, isWidgetSection, type LayoutGroup, isWidgetTable, type LayoutTarget } from "@graphite/messages";
import { isWidgetSpanColumn, isWidgetSpanRow, isWidgetSection, type Layout, isWidgetTable, type LayoutTarget } from "@graphite/messages";
import WidgetSection from "@graphite/components/widgets/WidgetSection.svelte";
import WidgetSpan from "@graphite/components/widgets/WidgetSpan.svelte";
import WidgetTable from "@graphite/components/widgets/WidgetTable.svelte";
export let layout: LayoutGroup[];
export let layout: Layout;
export let layoutTarget: LayoutTarget;
let className = "";
export { className as class };

View file

@ -2,7 +2,7 @@
import { getContext } from "svelte";
import type { Editor } from "@graphite/editor";
import type { LayoutTarget, Widget, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
import type { LayoutTarget, WidgetInstance, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
import { narrowWidgetProps, isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
import { debouncer } from "@graphite/utility-functions/debounce";
@ -54,8 +54,8 @@
if (isWidgetSpanColumn(widgetData)) return "column";
}
function watchWidgets(widgetData: WidgetSpanRow | WidgetSpanColumn): Widget[] {
let widgets: Widget[] = [];
function watchWidgets(widgetData: WidgetSpanRow | WidgetSpanColumn): WidgetInstance[] {
let widgets: WidgetInstance[] = [];
if (isWidgetSpanRow(widgetData)) widgets = widgetData.rowWidgets;
else if (isWidgetSpanColumn(widgetData)) widgets = widgetData.columnWidgets;
return widgets;

View file

@ -2,19 +2,19 @@
import { getContext, onMount } from "svelte";
import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages";
import { patchWidgetLayout, UpdateStatusBarHintsLayout } from "@graphite/messages";
import type { Layout } from "@graphite/messages";
import { patchLayout, UpdateStatusBarHintsLayout } from "@graphite/messages";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor");
let statusBarHintsLayout: LayoutGroup[] = [];
let statusBarHintsLayout: Layout = [];
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateStatusBarHintsLayout, (updateStatusBarHintsLayout) => {
patchWidgetLayout(statusBarHintsLayout, updateStatusBarHintsLayout);
patchLayout(statusBarHintsLayout, updateStatusBarHintsLayout);
statusBarHintsLayout = statusBarHintsLayout;
});
});

View file

@ -2,8 +2,8 @@
import { getContext, onMount } from "svelte";
import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages";
import { patchWidgetLayout, UpdateMenuBarLayout } from "@graphite/messages";
import type { Layout } from "@graphite/messages";
import { patchLayout, UpdateMenuBarLayout } from "@graphite/messages";
import type { AppWindowState } from "@graphite/state-providers/app-window";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
@ -15,11 +15,11 @@
const appWindow = getContext<AppWindowState>("appWindow");
const editor = getContext<Editor>("editor");
let menuBarLayout: LayoutGroup[] = [];
let menuBarLayout: Layout = [];
onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => {
patchWidgetLayout(menuBarLayout, updateMenuBarLayout);
patchLayout(menuBarLayout, updateMenuBarLayout);
menuBarLayout = menuBarLayout;
});
});

View file

@ -1218,7 +1218,7 @@ export class PopoverButton extends WidgetProps {
tooltipShortcut!: ActionShortcut | undefined;
// Body
popoverLayout!: LayoutGroup[];
popoverLayout!: Layout;
popoverMinWidth: number | undefined;
}
@ -1453,19 +1453,14 @@ export function narrowWidgetProps<K extends WidgetPropsNames>(props: WidgetProps
else return undefined;
}
export class Widget {
constructor(props: WidgetPropsSet, widgetId: bigint) {
this.props = props;
this.widgetId = widgetId;
}
export class WidgetInstance {
@Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true })
props!: WidgetPropsSet;
widgetId!: bigint;
}
function hoistWidgetInstance(widgetInstance: any): Widget {
function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
const kind = Object.keys(widgetInstance.widget)[0];
const props = widgetInstance.widget[kind];
props.kind = kind;
@ -1476,10 +1471,10 @@ function hoistWidgetInstance(widgetInstance: any): Widget {
const { widgetId } = widgetInstance;
return plainToClass(Widget, { props, widgetId });
return plainToClass(WidgetInstance, { props, widgetId });
}
function hoistWidgetInstances(widgetInstance: any[]): Widget[] {
function hoistWidgetInstances(widgetInstance: any[]): WidgetInstance[] {
return widgetInstance.map(hoistWidgetInstance);
}
@ -1506,15 +1501,29 @@ export type LayoutTarget =
export class WidgetDiffUpdate extends JsMessage {
// TODO: Replace `any` with correct typing
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
@Transform(({ value }: { value: WidgetDiff[] }) => {
// Unpacking rust types to more usable type in the frontend
return value.map((diff) => {
const { widgetPath, newValue } = diff;
if ("layout" in newValue) return { widgetPath, newValue: newValue.layout.map(createLayoutGroup) };
if ("layoutGroup" in newValue) return { widgetPath, newValue: createLayoutGroup(newValue.layoutGroup) };
if ("widget" in newValue) return { widgetPath, newValue: hoistWidgetInstance(newValue.widget) };
// This code should be unreachable
throw new Error("DiffUpdate invalid");
});
})
diff!: WidgetDiff[];
}
type UIItem = LayoutGroup[] | LayoutGroup | Widget[] | Widget;
type WidgetDiff = { widgetPath: number[]; newValue: UIItem };
type DiffUpdate = { layout: Layout } | { layoutGroup: LayoutGroup } | { widget: WidgetInstance };
type WidgetDiff = { widgetPath: number[]; newValue: DiffUpdate };
type UIItem = Layout | LayoutGroup | WidgetInstance[] | WidgetInstance;
// Updates a widget layout based on a list of updates, giving the new layout by mutating the `layout` argument
export function patchWidgetLayout(layout: /* &mut */ LayoutGroup[], updates: WidgetDiffUpdate) {
export function patchLayout(layout: /* &mut */ Layout, updates: WidgetDiffUpdate) {
updates.diff.forEach((update) => {
// Find the object where the diff applies to
const diffObject = update.widgetPath.reduce((targetLayout: UIItem | undefined, index: number): UIItem | undefined => {
@ -1522,7 +1531,7 @@ export function patchWidgetLayout(layout: /* &mut */ LayoutGroup[], updates: Wid
if (targetLayout && "rowWidgets" in targetLayout) return targetLayout.rowWidgets[index];
if (targetLayout && "tableWidgets" in targetLayout) return targetLayout.tableWidgets[index];
if (targetLayout && "layout" in targetLayout) return targetLayout.layout[index];
if (targetLayout instanceof Widget) {
if (targetLayout instanceof WidgetInstance) {
if (targetLayout.props.kind === "PopoverButton" && targetLayout.props instanceof PopoverButton && targetLayout.props.popoverLayout) {
return targetLayout.props.popoverLayout[index];
}
@ -1540,7 +1549,7 @@ export function patchWidgetLayout(layout: /* &mut */ LayoutGroup[], updates: Wid
// tries to update the layout, it attempts to insert only the changes against the old layout that no longer exists.
if (diffObject === undefined) {
// eslint-disable-next-line no-console
console.error("In `patchWidgetLayout`, the `diffObject` is undefined. The layout has not been updated. See the source code comment above this error for hints.");
console.error("In `patchLayout`, the `diffObject` is undefined. The layout has not been updated. See the source code comment above this error for hints.");
return;
}
@ -1559,45 +1568,28 @@ export function patchWidgetLayout(layout: /* &mut */ LayoutGroup[], updates: Wid
}
export type LayoutGroup = WidgetSpanRow | WidgetSpanColumn | WidgetTable | WidgetSection;
export type Layout = LayoutGroup[];
export type WidgetSpanColumn = { columnWidgets: Widget[] };
export type WidgetSpanColumn = { columnWidgets: WidgetInstance[] };
export function isWidgetSpanColumn(layoutColumn: LayoutGroup): layoutColumn is WidgetSpanColumn {
return Boolean((layoutColumn as WidgetSpanColumn)?.columnWidgets);
}
export type WidgetSpanRow = { rowWidgets: Widget[] };
export type WidgetSpanRow = { rowWidgets: WidgetInstance[] };
export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpanRow {
return Boolean((layoutRow as WidgetSpanRow)?.rowWidgets);
}
export type WidgetTable = { tableWidgets: Widget[][]; unstyled: boolean };
export type WidgetTable = { tableWidgets: WidgetInstance[][]; unstyled: boolean };
export function isWidgetTable(layoutTable: LayoutGroup): layoutTable is WidgetTable {
return Boolean((layoutTable as WidgetTable)?.tableWidgets);
}
export type WidgetSection = { name: string; description: string; visible: boolean; pinned: boolean; id: bigint; layout: LayoutGroup[] };
export type WidgetSection = { name: string; description: string; visible: boolean; pinned: boolean; id: bigint; layout: Layout };
export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSection {
return Boolean((layoutRow as WidgetSection)?.layout);
}
// Unpacking rust types to more usable type in the frontend
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
return diffs.map((diff) => {
const { widgetPath, newValue } = diff;
if (newValue.widgetLayout) {
return { widgetPath, newValue: newValue.widgetLayout.map(createLayoutGroup) };
}
if (newValue.layoutGroup) {
return { widgetPath, newValue: createLayoutGroup(newValue.layoutGroup) };
}
if (newValue.widget) {
return { widgetPath, newValue: hoistWidgetInstance(newValue.widget) };
}
// This code should be unreachable
throw new Error("DiffUpdate invalid");
});
}
// Unpacking a layout group
function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.column) {

View file

@ -2,25 +2,17 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor";
import { type IconName } from "@graphite/icons";
import {
DisplayDialog,
DisplayDialogDismiss,
UpdateDialogButtons,
UpdateDialogColumn1,
UpdateDialogColumn2,
patchWidgetLayout,
TriggerDisplayThirdPartyLicensesDialog,
type LayoutGroup,
} from "@graphite/messages";
import { DisplayDialog, DisplayDialogDismiss, UpdateDialogButtons, UpdateDialogColumn1, UpdateDialogColumn2, patchLayout, TriggerDisplayThirdPartyLicensesDialog } from "@graphite/messages";
import type { Layout } from "@graphite/messages";
export function createDialogState(editor: Editor) {
const { subscribe, update } = writable({
visible: false,
title: "",
icon: "" as IconName,
buttons: [] as LayoutGroup[],
column1: [] as LayoutGroup[],
column2: [] as LayoutGroup[],
buttons: [] as Layout,
column1: [] as Layout,
column2: [] as Layout,
// Special case for the crash dialog because we cannot handle button widget callbacks from Rust once the editor has panicked
panicDetails: "",
});
@ -65,21 +57,21 @@ export function createDialogState(editor: Editor) {
});
editor.subscriptions.subscribeJsMessage(UpdateDialogButtons, (updateDialogButtons) => {
update((state) => {
patchWidgetLayout(state.buttons, updateDialogButtons);
patchLayout(state.buttons, updateDialogButtons);
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateDialogColumn1, (updateDialogColumn1) => {
update((state) => {
patchWidgetLayout(state.column1, updateDialogColumn1);
patchLayout(state.column1, updateDialogColumn1);
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateDialogColumn2, (updateDialogColumn2) => {
update((state) => {
patchWidgetLayout(state.column2, updateDialogColumn2);
patchLayout(state.column2, updateDialogColumn2);
return state;
});

View file

@ -4,7 +4,7 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor";
import {
patchWidgetLayout,
patchLayout,
UpdateDocumentBarLayout,
UpdateDocumentModeLayout,
UpdateToolOptionsLayout,
@ -13,18 +13,18 @@ import {
UpdateNodeGraphControlBarLayout,
UpdateGraphViewOverlay,
UpdateGraphFadeArtwork,
type LayoutGroup,
} from "@graphite/messages";
import type { Layout } from "@graphite/messages";
export function createDocumentState(editor: Editor) {
const state = writable({
// Layouts
documentModeLayout: [] as LayoutGroup[],
toolOptionsLayout: [] as LayoutGroup[],
documentBarLayout: [] as LayoutGroup[],
toolShelfLayout: [] as LayoutGroup[],
workingColorsLayout: [] as LayoutGroup[],
nodeGraphControlBarLayout: [] as LayoutGroup[],
documentModeLayout: [] as Layout,
toolOptionsLayout: [] as Layout,
documentBarLayout: [] as Layout,
toolShelfLayout: [] as Layout,
workingColorsLayout: [] as Layout,
nodeGraphControlBarLayout: [] as Layout,
// Graph view overlay
graphViewOverlayOpen: false,
fadeArtwork: 100,
@ -42,7 +42,7 @@ export function createDocumentState(editor: Editor) {
await tick();
update((state) => {
patchWidgetLayout(state.documentModeLayout, updateDocumentModeLayout);
patchLayout(state.documentModeLayout, updateDocumentModeLayout);
return state;
});
});
@ -50,7 +50,7 @@ export function createDocumentState(editor: Editor) {
await tick();
update((state) => {
patchWidgetLayout(state.toolOptionsLayout, updateToolOptionsLayout);
patchLayout(state.toolOptionsLayout, updateToolOptionsLayout);
return state;
});
});
@ -58,7 +58,7 @@ export function createDocumentState(editor: Editor) {
await tick();
update((state) => {
patchWidgetLayout(state.documentBarLayout, updateDocumentBarLayout);
patchLayout(state.documentBarLayout, updateDocumentBarLayout);
return state;
});
});
@ -66,7 +66,7 @@ export function createDocumentState(editor: Editor) {
await tick();
update((state) => {
patchWidgetLayout(state.toolShelfLayout, updateToolShelfLayout);
patchLayout(state.toolShelfLayout, updateToolShelfLayout);
return state;
});
});
@ -74,13 +74,13 @@ export function createDocumentState(editor: Editor) {
await tick();
update((state) => {
patchWidgetLayout(state.workingColorsLayout, updateWorkingColorsLayout);
patchLayout(state.workingColorsLayout, updateWorkingColorsLayout);
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphControlBarLayout, (updateNodeGraphControlBarLayout) => {
update((state) => {
patchWidgetLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
patchLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
return state;
});
});