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
49 changed files with 205 additions and 254 deletions
@@ -120,7 +120,7 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
[ [
WidgetDiff { WidgetDiff {
widget_path, widget_path,
new_value: DiffUpdate::WidgetLayout(layout), new_value: DiffUpdate::Layout(layout),
}, },
] if widget_path.is_empty() => { ] if widget_path.is_empty() => {
let entries = crate::utils::menu::convert_menu_bar_layout_to_menu_items(layout); let entries = crate::utils::menu::convert_menu_bar_layout_to_menu_items(layout);
+2 -2
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::input_keyboard::{Key, LabeledKey, LabeledShortcut};
use graphite_editor::messages::input_mapper::utility_types::misc::ActionShortcut; use graphite_editor::messages::input_mapper::utility_types::misc::ActionShortcut;
use graphite_editor::messages::layout::LayoutMessage; 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}; 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() { let layout_group = match layout.as_slice() {
[layout_group] => layout_group, [layout_group] => layout_group,
_ => panic!("Menu bar layout is supposed to have exactly one layout group"), _ => panic!("Menu bar layout is supposed to have exactly one layout group"),
+1 -1
View File
@@ -548,7 +548,7 @@ mod test {
for response in responses { for response in responses {
// Check for the existence of the file format incompatibility warning dialog after opening the test file // Check for the existence of the file format incompatibility warning dialog after opening the test file
if let FrontendMessage::UpdateDialogColumn1 { diff } = response { 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 LayoutGroup::Row { widgets } = &sub_layout.0[0] {
if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget { if let Widget::TextLabel(TextLabel { value, .. }) = &widgets[0].widget {
print_problem_to_terminal_on_failure(value); print_problem_to_terminal_on_failure(value);
@@ -76,7 +76,7 @@ impl DialogLayoutHolder for ExportDialogMessageHandler {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), 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(), .widget_instance(),
]; ];
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { widgets: export_type }, LayoutGroup::Row { widgets: export_type },
LayoutGroup::Row { widgets: resolution }, LayoutGroup::Row { widgets: resolution },
LayoutGroup::Row { widgets: export_area }, LayoutGroup::Row { widgets: export_area },
LayoutGroup::Row { widgets: transparent_background }, LayoutGroup::Row { widgets: transparent_background },
])) ])
} }
} }
@@ -66,7 +66,7 @@ impl DialogLayoutHolder for NewDocumentDialogMessageHandler {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), 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(), .widget_instance(),
]; ];
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![LayoutGroup::Row { widgets: name }, LayoutGroup::Row { widgets: infinite }, LayoutGroup::Row { widgets: scale }])
LayoutGroup::Row { widgets: name },
LayoutGroup::Row { widgets: infinite },
LayoutGroup::Row { widgets: scale },
]))
} }
} }
@@ -224,7 +224,7 @@ impl PreferencesDialogMessageHandler {
.widget_instance(), .widget_instance(),
]; ];
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { widgets: navigation_header }, LayoutGroup::Row { widgets: navigation_header },
LayoutGroup::Row { widgets: zoom_rate_label }, LayoutGroup::Row { widgets: zoom_rate_label },
LayoutGroup::Row { widgets: zoom_rate }, LayoutGroup::Row { widgets: zoom_rate },
@@ -237,7 +237,7 @@ impl PreferencesDialogMessageHandler {
LayoutGroup::Row { widgets: graph_wire_style }, LayoutGroup::Row { widgets: graph_wire_style },
LayoutGroup::Row { widgets: use_vello }, LayoutGroup::Row { widgets: use_vello },
LayoutGroup::Row { widgets: vector_meshes }, LayoutGroup::Row { widgets: vector_meshes },
])) ])
} }
pub fn send_layout(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget, preferences: &PreferencesMessageHandler) { 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(), 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) { fn send_layout_buttons(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget) {
@@ -15,7 +15,7 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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 { fn layout_column_2(&self) -> Layout {
@@ -51,13 +51,13 @@ impl DialogLayoutHolder for AboutGraphiteDialog {
.widget_instance(), .widget_instance(),
); );
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Column { widgets }])) Layout(vec![LayoutGroup::Column { widgets }])
} }
} }
impl LayoutHolder for AboutGraphiteDialog { impl LayoutHolder for AboutGraphiteDialog {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("About this release").bold(true).widget_instance()], widgets: vec![TextLabel::new("About this release").bold(true).widget_instance()],
}, },
@@ -67,6 +67,6 @@ impl LayoutHolder for AboutGraphiteDialog {
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("Copyright © {} Graphite contributors", self.localized_commit_year)).widget_instance()], widgets: vec![TextLabel::new(format!("Copyright © {} Graphite contributors", self.localized_commit_year)).widget_instance()],
}, },
])) ])
} }
} }
@@ -24,7 +24,7 @@ impl DialogLayoutHolder for CloseAllDocumentsDialog {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), 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 { fn layout(&self) -> Layout {
let unsaved_list = "• ".to_string() + &self.unsaved_document_names.join("\n• "); let unsaved_list = "• ".to_string() + &self.unsaved_document_names.join("\n• ");
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("Save documents before closing them?").bold(true).multiline(true).widget_instance()], widgets: vec![TextLabel::new("Save documents before closing them?").bold(true).multiline(true).widget_instance()],
}, },
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("Documents with unsaved changes:\n{unsaved_list}")).multiline(true).widget_instance()], widgets: vec![TextLabel::new(format!("Documents with unsaved changes:\n{unsaved_list}")).multiline(true).widget_instance()],
}, },
])) ])
} }
} }
@@ -35,7 +35,7 @@ impl DialogLayoutHolder for CloseDocumentDialog {
TextButton::new("Cancel").on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance(), 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 { ' ' }; let break_lines = if self.document_name.len() > max_one_line_length { '\n' } else { ' ' };
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("Save document before closing it?").bold(true).widget_instance()], widgets: vec![TextLabel::new("Save document before closing it?").bold(true).widget_instance()],
}, },
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_instance()], widgets: vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_instance()],
}, },
])) ])
} }
} }
@@ -13,7 +13,7 @@ impl DialogLayoutHolder for ComingSoonDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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 }); rows.push(LayoutGroup::Row { widgets: row3 });
} }
Layout::WidgetLayout(WidgetLayout(rows)) Layout(rows)
} }
} }
@@ -22,7 +22,7 @@ impl DialogLayoutHolder for DemoArtworkDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("Close").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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(); .collect();
let _ = rows_of_images_with_buttons.pop(); let _ = rows_of_images_with_buttons.pop();
Layout::WidgetLayout(WidgetLayout(rows_of_images_with_buttons)) Layout(rows_of_images_with_buttons)
} }
} }
@@ -14,19 +14,19 @@ impl DialogLayoutHolder for ErrorDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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 { impl LayoutHolder for ErrorDialog {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(&self.title).bold(true).widget_instance()], widgets: vec![TextLabel::new(&self.title).bold(true).widget_instance()],
}, },
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(&self.description).multiline(true).widget_instance()], widgets: vec![TextLabel::new(&self.description).multiline(true).widget_instance()],
}, },
])) ])
} }
} }
@@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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 { 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()) .map(|&(icon, label, message_factory)| TextButton::new(label).icon(Some((icon).into())).flush(true).on_update(move |_| message_factory()).widget_instance())
.collect(); .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(); let description = description.trim();
Layout::WidgetLayout(WidgetLayout(vec![ Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("Graphite is free, open source software").bold(true).widget_instance()], widgets: vec![TextLabel::new("Graphite is free, open source software").bold(true).widget_instance()],
}, },
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new(description).multiline(true).widget_instance()], widgets: vec![TextLabel::new(description).multiline(true).widget_instance()],
}, },
])) ])
} }
} }
@@ -12,7 +12,7 @@ impl DialogLayoutHolder for LicensesThirdPartyDialog {
fn layout_buttons(&self) -> Layout { fn layout_buttons(&self) -> Layout {
let widgets = vec![TextButton::new("OK").emphasized(true).on_update(|_| FrontendMessage::DisplayDialogDismiss.into()).widget_instance()]; 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 // 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; 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![ widgets: vec![
TextLabel::new(license_text) TextLabel::new(license_text)
.monospace(true) .monospace(true)
@@ -39,6 +39,6 @@ impl LayoutHolder for LicensesThirdPartyDialog {
.min_width(format!("{non_wrapping_column_width}ch")) .min_width(format!("{non_wrapping_column_width}ch"))
.widget_instance(), .widget_instance(),
], ],
}])) }])
} }
} }
@@ -24,12 +24,10 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
match message { match message {
LayoutMessage::ResendActiveWidget { layout_target, widget_id } => { LayoutMessage::ResendActiveWidget { layout_target, widget_id } => {
// Find the updated diff based on the specified layout target // Find the updated diff based on the specified layout target
let Some(diff) = (match &self.layouts[layout_target as usize] { let Some(diff) = Self::get_widget_path(&self.layouts[layout_target as usize], widget_id).map(|(widget, widget_path)| {
Layout::WidgetLayout(layout) => Self::get_widget_path(layout, widget_id).map(|(widget, widget_path)| { // Create a widget update diff for the relevant id
// Create a widget update diff for the relevant id let new_value = DiffUpdate::Widget(widget.clone());
let new_value = DiffUpdate::Widget(widget.clone()); WidgetDiff { widget_path, new_value }
WidgetDiff { widget_path, new_value }
}),
}) else { }) else {
return; return;
}; };
@@ -41,7 +39,7 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
} }
LayoutMessage::DestroyLayout { layout_target } => { LayoutMessage::DestroyLayout { layout_target } => {
if let Some(layout) = self.layouts.get_mut(layout_target as usize) { 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 } => { LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
@@ -61,7 +59,7 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
impl LayoutMessageHandler { impl LayoutMessageHandler {
/// Get the widget path for the widget with the specified id /// 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<_>>(); 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() { while let Some((mut widget_path, layout_group)) = stack.pop() {
match layout_group { match layout_group {
@@ -124,10 +122,7 @@ impl LayoutMessageHandler {
return; return;
}; };
let mut layout_iter = match layout { let Some(widget_instance) = layout.iter_mut().find(|widget| widget.widget_id == widget_id) else {
Layout::WidgetLayout(widget_layout) => widget_layout.iter_mut(),
};
let Some(widget_instance) = layout_iter.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:?}`",); 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; return;
}; };
@@ -479,31 +474,25 @@ impl LayoutMessageHandler {
responses: &mut VecDeque<Message>, responses: &mut VecDeque<Message>,
action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>,
) { ) {
match new_layout { let mut widget_diffs = Vec::new();
Layout::WidgetLayout(_) => {
let mut widget_diffs = Vec::new();
let Layout::WidgetLayout(current) = &mut self.layouts[layout_target as usize]; self.layouts[layout_target as usize].diff(new_layout, &mut Vec::new(), &mut widget_diffs);
let Layout::WidgetLayout(new) = new_layout;
current.diff(new, &mut Vec::new(), &mut widget_diffs);
// Skip sending if no diff // Skip sending if no diff
if widget_diffs.is_empty() { if widget_diffs.is_empty() {
return; 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);
}
} }
// 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. /// Send a diff to the frontend based on the layout target.
@@ -103,24 +103,11 @@ pub trait DialogLayoutHolder: LayoutHolder {
} }
} }
// TODO: Unwrap this enum /// Contains an arrangement of widgets mounted somewhere specific in the frontend.
/// 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
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, specta::Type)] #[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<'_> { pub fn iter(&self) -> WidgetIter<'_> {
WidgetIter { WidgetIter {
stack: self.0.iter().collect(), stack: self.0.iter().collect(),
@@ -144,7 +131,7 @@ impl WidgetLayout {
self.0.clone_from(&new.0); self.0.clone_from(&new.0);
// Push an update sublayout to the diff // Push an update sublayout to the diff
let new = DiffUpdate::WidgetLayout(new); let new = DiffUpdate::Layout(new);
widget_diffs.push(WidgetDiff { widget_diffs.push(WidgetDiff {
widget_path: widget_path.to_vec(), widget_path: widget_path.to_vec(),
new_value: new, new_value: new,
@@ -285,7 +272,7 @@ pub enum LayoutGroup {
visible: bool, visible: bool,
pinned: bool, pinned: bool,
id: u64, 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. /// 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)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum DiffUpdate { pub enum DiffUpdate {
#[serde(rename = "widgetLayout")] #[serde(rename = "layout")]
WidgetLayout(WidgetLayout), Layout(Layout),
#[serde(rename = "layoutGroup")] #[serde(rename = "layoutGroup")]
LayoutGroup(LayoutGroup), LayoutGroup(LayoutGroup),
#[serde(rename = "widget")] #[serde(rename = "widget")]
@@ -675,7 +660,7 @@ impl DiffUpdate {
}; };
match self { 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_tooltip(widget_instance);
convert_menu_lists(widget_instance); convert_menu_lists(widget_instance);
}), }),
@@ -63,7 +63,7 @@ pub struct PopoverButton {
pub tooltip_shortcut: Option<ActionShortcut>, pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "popoverLayout")] #[serde(rename = "popoverLayout")]
pub popover_layout: WidgetLayout, pub popover_layout: Layout,
#[serde(rename = "popoverMinWidth")] #[serde(rename = "popoverMinWidth")]
pub popover_min_width: Option<u32>, pub popover_min_width: Option<u32>,
@@ -1,5 +1,5 @@
use super::VectorTableTab; 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::data_panel::DataPanelMessage;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -83,11 +83,12 @@ impl DataPanelMessageHandler {
}; };
// Main data visualization // Main data visualization
let mut layout = self let mut layout = Layout(
.introspected_data self.introspected_data
.as_ref() .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"))) .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(); .unwrap_or_default(),
);
let mut widgets = Vec::new(); let mut widgets = Vec::new();
@@ -127,11 +128,11 @@ impl DataPanelMessageHandler {
} }
if !widgets.is_empty() { if !widgets.is_empty() {
layout.insert(0, LayoutGroup::Row { widgets }); layout.0.insert(0, LayoutGroup::Row { widgets });
} }
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(layout)), layout,
layout_target: LayoutTarget::DataPanel, layout_target: LayoutTarget::DataPanel,
}); });
} }
@@ -323,17 +323,17 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
// Clear the control bar // Clear the control bar
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()), layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelControlLeftBar, layout_target: LayoutTarget::LayersPanelControlLeftBar,
}); });
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()), layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelControlRightBar, layout_target: LayoutTarget::LayersPanelControlRightBar,
}); });
// Clear the bottom bar // Clear the bottom bar
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(Default::default()), layout: Layout::default(),
layout_target: LayoutTarget::LayersPanelBottomBar, 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) { 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) // 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![ widgets: vec![
// DropdownInput::new( // DropdownInput::new(
// vec![vec![ // vec![vec![
@@ -2200,7 +2200,7 @@ impl DocumentMessageHandler {
}]); }]);
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(document_mode_layout), layout,
layout_target: LayoutTarget::DocumentMode, layout_target: LayoutTarget::DocumentMode,
}); });
@@ -2235,7 +2235,7 @@ impl DocumentMessageHandler {
}) })
.widget_instance(), .widget_instance(),
PopoverButton::new() PopoverButton::new()
.popover_layout(WidgetLayout(vec![ .popover_layout(Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("Overlays").bold(true).widget_instance()], widgets: vec![TextLabel::new("Overlays").bold(true).widget_instance()],
}, },
@@ -2484,7 +2484,7 @@ impl DocumentMessageHandler {
}) })
.widget_instance(), .widget_instance(),
PopoverButton::new() PopoverButton::new()
.popover_layout(WidgetLayout( .popover_layout(Layout(
[ [
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![TextLabel::new("Snapping").bold(true).widget_instance()], 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()) .on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
.widget_instance(), .widget_instance(),
PopoverButton::new() 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)) .popover_min_width(Some(320))
.widget_instance(), .widget_instance(),
Separator::new(SeparatorType::Unrelated).widget_instance(), Separator::new(SeparatorType::Unrelated).widget_instance(),
@@ -2574,7 +2574,7 @@ impl DocumentMessageHandler {
.narrow(true) .narrow(true)
.widget_instance(), .widget_instance(),
// PopoverButton::new().popover_layout( // PopoverButton::new().popover_layout(
// WidgetLayout(vec![ // Layout(vec![
// LayoutGroup::Row { // LayoutGroup::Row {
// widgets: vec![TextLabel::new("Render Mode").bold(true).widget_instance()], // widgets: vec![TextLabel::new("Render Mode").bold(true).widget_instance()],
// }, // },
@@ -2632,10 +2632,8 @@ impl DocumentMessageHandler {
.widget_instance(), .widget_instance(),
]); ]);
let document_bar_layout = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(document_bar_layout), layout: Layout(vec![LayoutGroup::Row { widgets }]),
layout_target: LayoutTarget::DocumentBar, layout_target: LayoutTarget::DocumentBar,
}); });
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
@@ -2773,7 +2771,7 @@ impl DocumentMessageHandler {
.tooltip_label("Fill") .tooltip_label("Fill")
.widget_instance(), .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![ let widgets = vec![
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24) IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
@@ -2791,14 +2789,14 @@ impl DocumentMessageHandler {
.disabled(!has_selection) .disabled(!has_selection)
.widget_instance(), .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 { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_control_bar_left), layout: layers_panel_control_bar_left,
layout_target: LayoutTarget::LayersPanelControlLeftBar, layout_target: LayoutTarget::LayersPanelControlLeftBar,
}); });
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_control_bar_right), layout: layers_panel_control_bar_right,
layout_target: LayoutTarget::LayersPanelControlRightBar, layout_target: LayoutTarget::LayersPanelControlRightBar,
}); });
} }
@@ -2845,7 +2843,7 @@ impl DocumentMessageHandler {
} }
}) })
.widget_instance(); .widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }]) Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
}) })
.widget_instance(), .widget_instance(),
Separator::new(SeparatorType::Unrelated).widget_instance(), Separator::new(SeparatorType::Unrelated).widget_instance(),
@@ -2870,10 +2868,8 @@ impl DocumentMessageHandler {
.disabled(!has_selection) .disabled(!has_selection)
.widget_instance(), .widget_instance(),
]; ];
let layers_panel_bottom_bar = WidgetLayout(vec![LayoutGroup::Row { widgets }]);
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layers_panel_bottom_bar), layout: Layout(vec![LayoutGroup::Row { widgets }]),
layout_target: LayoutTarget::LayersPanelBottomBar, layout_target: LayoutTarget::LayersPanelBottomBar,
}); });
} }
@@ -2069,7 +2069,7 @@ impl NodeGraphMessageHandler {
/// Send the cached layout to the frontend for the control bar at the top of the node panel /// 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>) { fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(self.widgets.to_vec())), layout: Layout(self.widgets.to_vec()),
layout_target: LayoutTarget::NodeGraphControlBar, layout_target: LayoutTarget::NodeGraphControlBar,
}); });
} }
@@ -2145,7 +2145,7 @@ impl NodeGraphMessageHandler {
} }
}) })
.widget_instance(); .widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }]) Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
}) })
.widget_instance(), .widget_instance(),
// //
@@ -2443,7 +2443,7 @@ impl NodeGraphMessageHandler {
.into() .into()
}) })
.widget_instance(); .widget_instance();
WidgetLayout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }]) Layout(vec![LayoutGroup::Row { widgets: vec![node_chooser] }])
}) })
.widget_instance(), .widget_instance(),
Separator::new(SeparatorType::Related).widget_instance(), Separator::new(SeparatorType::Related).widget_instance(),
@@ -1699,7 +1699,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
visible, visible,
pinned, pinned,
id: node_id.0, id: node_id.0,
layout: WidgetLayout(layout), layout: Layout(layout),
} }
} }
@@ -35,7 +35,7 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
match message { match message {
PropertiesPanelMessage::Clear => { PropertiesPanelMessage::Clear => {
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![])), layout: Layout::default(),
layout_target: LayoutTarget::PropertiesPanel, layout_target: LayoutTarget::PropertiesPanel,
}); });
} }
@@ -53,10 +53,10 @@ impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> f
document_name, document_name,
executor, 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 { node_properties_context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(properties_sections)), layout,
layout_target: LayoutTarget::PropertiesPanel, layout_target: LayoutTarget::PropertiesPanel,
}); });
} }
@@ -736,6 +736,6 @@ impl LayoutHolder for MenuBarMessageHandler {
.widget_instance(), .widget_instance(),
]; ];
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets: menu_bar_buttons }])) Layout(vec![LayoutGroup::Row { widgets: menu_bar_buttons }])
} }
} }
@@ -924,7 +924,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
layout_target: LayoutTarget::WelcomeScreenButtons, layout_target: LayoutTarget::WelcomeScreenButtons,
}); });
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![table])), layout: Layout(vec![table]),
layout_target: LayoutTarget::WelcomeScreenButtons, layout_target: LayoutTarget::WelcomeScreenButtons,
}); });
} }
@@ -75,7 +75,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Artb
impl LayoutHolder for ArtboardTool { impl LayoutHolder for ArtboardTool {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default()) Layout::default()
} }
} }
@@ -220,7 +220,7 @@ impl LayoutHolder for BrushTool {
.widget_instance(), .widget_instance(),
); );
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -35,7 +35,7 @@ impl ToolMetadata for EyedropperTool {
impl LayoutHolder for EyedropperTool { impl LayoutHolder for EyedropperTool {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default()) Layout::default()
} }
} }
@@ -37,7 +37,7 @@ impl ToolMetadata for FillTool {
impl LayoutHolder for FillTool { impl LayoutHolder for FillTool {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default()) Layout::default()
} }
} }
@@ -151,7 +151,7 @@ impl LayoutHolder for FreehandTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance()); widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight)); widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -107,7 +107,7 @@ impl LayoutHolder for GradientTool {
.selected_index(Some((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32)) .selected_index(Some((self.selected_gradient().unwrap_or(self.options.gradient_type) == GradientType::Radial) as u32))
.widget_instance(); .widget_instance();
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets: vec![gradient_type] }])) Layout(vec![LayoutGroup::Row { widgets: vec![gradient_type] }])
} }
} }
@@ -34,7 +34,7 @@ impl ToolMetadata for NavigateTool {
impl LayoutHolder for NavigateTool { impl LayoutHolder for NavigateTool {
fn layout(&self) -> Layout { fn layout(&self) -> Layout {
Layout::WidgetLayout(WidgetLayout::default()) Layout::default()
} }
} }
@@ -342,7 +342,7 @@ impl LayoutHolder for PathTool {
let _pin_pivot = pin_pivot_widget(self.tool_data.pivot_gizmo.pin_active(), false, PivotToolSource::Path); 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![ widgets: vec![
x_location, x_location,
related_seperator.clone(), related_seperator.clone(),
@@ -367,7 +367,7 @@ impl LayoutHolder for PathTool {
// related_seperator.clone(), // related_seperator.clone(),
// pin_pivot, // pin_pivot,
], ],
}])) }])
} }
} }
@@ -238,7 +238,7 @@ impl LayoutHolder for PenTool {
.widget_instance(), .widget_instance(),
); );
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -252,7 +252,7 @@ impl LayoutHolder for SelectTool {
widgets.extend(self.alignment_widgets(disabled)); widgets.extend(self.alignment_widgets(disabled));
// widgets.push( // widgets.push(
// PopoverButton::new() // PopoverButton::new()
// .popover_layout(WidgetLayout(vec![ // .popover_layout(Layout(vec![
// LayoutGroup::Row { // LayoutGroup::Row {
// widgets: vec![TextLabel::new("Align").bold(true).widget_instance()], // 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.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count)); widgets.extend(self.boolean_widgets(self.tool_data.selected_layers_count));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -335,7 +335,7 @@ impl LayoutHolder for ShapeTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance()); widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight)); widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -158,7 +158,7 @@ impl LayoutHolder for SplineTool {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance()); widgets.push(Separator::new(SeparatorType::Unrelated).widget_instance());
widgets.push(create_weight_widget(self.options.line_weight)); widgets.push(create_weight_widget(self.options.line_weight));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
@@ -203,7 +203,7 @@ impl LayoutHolder for TextTool {
}, },
)); ));
Layout::WidgetLayout(WidgetLayout(vec![LayoutGroup::Row { widgets }])) Layout(vec![LayoutGroup::Row { widgets }])
} }
} }
+5 -5
View File
@@ -117,7 +117,7 @@ pub struct DocumentToolData {
impl DocumentToolData { impl DocumentToolData {
pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) { pub fn update_working_colors(&self, responses: &mut VecDeque<Message>) {
let layout = WidgetLayout(vec![ let layout = Layout(vec![
LayoutGroup::Row { LayoutGroup::Row {
widgets: vec![WorkingColorsInput::new(self.primary_color.to_gamma_srgb(), self.secondary_color.to_gamma_srgb()).widget_instance()], 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 { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(layout), layout,
layout_target: LayoutTarget::WorkingColors, layout_target: LayoutTarget::WorkingColors,
}); });
@@ -290,7 +290,7 @@ impl LayoutHolder for ToolData {
.skip(1) .skip(1)
.collect(); .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>) { pub fn send_layout(&self, responses: &mut VecDeque<Message>) {
@@ -557,7 +557,7 @@ impl HintData {
pub fn clear_layout(responses: &mut VecDeque<Message>) { pub fn clear_layout(responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout { responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout(vec![])), layout: Layout::default(),
layout_target: LayoutTarget::StatusBarHints, layout_target: LayoutTarget::StatusBarHints,
}); });
} }
+3 -3
View File
@@ -2,18 +2,18 @@
import { getContext, onMount, onDestroy } from "svelte"; import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor"; 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 LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
let dataPanelLayout: LayoutGroup[] = []; let dataPanelLayout: Layout = [];
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateDataPanelLayout, (updateDataPanelLayout) => { editor.subscriptions.subscribeJsMessage(UpdateDataPanelLayout, (updateDataPanelLayout) => {
patchWidgetLayout(dataPanelLayout, updateDataPanelLayout); patchLayout(dataPanelLayout, updateDataPanelLayout);
dataPanelLayout = dataPanelLayout; dataPanelLayout = dataPanelLayout;
}); });
}); });
+8 -8
View File
@@ -4,14 +4,14 @@
import { shortcutAltClick } from "@graphite/../wasm/pkg/graphite_wasm"; import { shortcutAltClick } from "@graphite/../wasm/pkg/graphite_wasm";
import type { Editor } from "@graphite/editor"; import type { Editor } from "@graphite/editor";
import { import {
patchWidgetLayout, patchLayout,
UpdateDocumentLayerDetails, UpdateDocumentLayerDetails,
UpdateDocumentLayerStructureJs, UpdateDocumentLayerStructureJs,
UpdateLayersPanelControlBarLeftLayout, UpdateLayersPanelControlBarLeftLayout,
UpdateLayersPanelControlBarRightLayout, UpdateLayersPanelControlBarRightLayout,
UpdateLayersPanelBottomBarLayout, UpdateLayersPanelBottomBarLayout,
} from "@graphite/messages"; } 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 type { NodeGraphState } from "@graphite/state-providers/node-graph";
import { operatingSystem } from "@graphite/utility-functions/platform"; import { operatingSystem } from "@graphite/utility-functions/platform";
import { extractPixelData } from "@graphite/utility-functions/rasterization"; import { extractPixelData } from "@graphite/utility-functions/rasterization";
@@ -69,25 +69,25 @@
let layerToClipAltKeyPressed = false; let layerToClipAltKeyPressed = false;
// Layouts // Layouts
let layersPanelControlBarLeftLayout: LayoutGroup[] = []; let layersPanelControlBarLeftLayout: Layout = [];
let layersPanelControlBarRightLayout: LayoutGroup[] = []; let layersPanelControlBarRightLayout: Layout = [];
let layersPanelBottomBarLayout: LayoutGroup[] = []; let layersPanelBottomBarLayout: Layout = [];
const altClickKeys: ActionShortcut = shortcutAltClick(); const altClickKeys: ActionShortcut = shortcutAltClick();
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => { editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => {
patchWidgetLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout); patchLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
layersPanelControlBarLeftLayout = layersPanelControlBarLeftLayout; layersPanelControlBarLeftLayout = layersPanelControlBarLeftLayout;
}); });
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarRightLayout, (updateLayersPanelControlBarRightLayout) => { editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarRightLayout, (updateLayersPanelControlBarRightLayout) => {
patchWidgetLayout(layersPanelControlBarRightLayout, updateLayersPanelControlBarRightLayout); patchLayout(layersPanelControlBarRightLayout, updateLayersPanelControlBarRightLayout);
layersPanelControlBarRightLayout = layersPanelControlBarRightLayout; layersPanelControlBarRightLayout = layersPanelControlBarRightLayout;
}); });
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelBottomBarLayout, (updateLayersPanelBottomBarLayout) => { editor.subscriptions.subscribeJsMessage(UpdateLayersPanelBottomBarLayout, (updateLayersPanelBottomBarLayout) => {
patchWidgetLayout(layersPanelBottomBarLayout, updateLayersPanelBottomBarLayout); patchLayout(layersPanelBottomBarLayout, updateLayersPanelBottomBarLayout);
layersPanelBottomBarLayout = layersPanelBottomBarLayout; layersPanelBottomBarLayout = layersPanelBottomBarLayout;
}); });
@@ -2,18 +2,18 @@
import { getContext, onMount, onDestroy } from "svelte"; import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor"; 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 LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
let propertiesPanelLayout: LayoutGroup[] = []; let propertiesPanelLayout: Layout = [];
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdatePropertiesPanelLayout, (updatePropertiesPanelLayout) => { editor.subscriptions.subscribeJsMessage(UpdatePropertiesPanelLayout, (updatePropertiesPanelLayout) => {
patchWidgetLayout(propertiesPanelLayout, updatePropertiesPanelLayout); patchLayout(propertiesPanelLayout, updatePropertiesPanelLayout);
propertiesPanelLayout = propertiesPanelLayout; propertiesPanelLayout = propertiesPanelLayout;
}); });
}); });
@@ -2,8 +2,8 @@
import { getContext, onMount, onDestroy } from "svelte"; import { getContext, onMount, onDestroy } from "svelte";
import type { Editor } from "@graphite/editor"; import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages"; import type { Layout } from "@graphite/messages";
import { patchWidgetLayout, UpdateWelcomeScreenButtonsLayout } from "@graphite/messages"; import { patchLayout, UpdateWelcomeScreenButtonsLayout } from "@graphite/messages";
import { extractPixelData } from "@graphite/utility-functions/rasterization"; import { extractPixelData } from "@graphite/utility-functions/rasterization";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
@@ -14,11 +14,11 @@
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
let welcomePanelButtonsLayout: LayoutGroup[] = []; let welcomePanelButtonsLayout: Layout = [];
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateWelcomeScreenButtonsLayout, (updateWelcomeScreenButtonsLayout) => { editor.subscriptions.subscribeJsMessage(UpdateWelcomeScreenButtonsLayout, (updateWelcomeScreenButtonsLayout) => {
patchWidgetLayout(welcomePanelButtonsLayout, updateWelcomeScreenButtonsLayout); patchLayout(welcomePanelButtonsLayout, updateWelcomeScreenButtonsLayout);
welcomePanelButtonsLayout = welcomePanelButtonsLayout; welcomePanelButtonsLayout = welcomePanelButtonsLayout;
}); });
@@ -1,11 +1,11 @@
<script lang="ts"> <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 WidgetSection from "@graphite/components/widgets/WidgetSection.svelte";
import WidgetSpan from "@graphite/components/widgets/WidgetSpan.svelte"; import WidgetSpan from "@graphite/components/widgets/WidgetSpan.svelte";
import WidgetTable from "@graphite/components/widgets/WidgetTable.svelte"; import WidgetTable from "@graphite/components/widgets/WidgetTable.svelte";
export let layout: LayoutGroup[]; export let layout: Layout;
export let layoutTarget: LayoutTarget; export let layoutTarget: LayoutTarget;
let className = ""; let className = "";
export { className as class }; export { className as class };
@@ -2,7 +2,7 @@
import { getContext } from "svelte"; import { getContext } from "svelte";
import type { Editor } from "@graphite/editor"; 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 { narrowWidgetProps, isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
import { debouncer } from "@graphite/utility-functions/debounce"; import { debouncer } from "@graphite/utility-functions/debounce";
@@ -54,8 +54,8 @@
if (isWidgetSpanColumn(widgetData)) return "column"; if (isWidgetSpanColumn(widgetData)) return "column";
} }
function watchWidgets(widgetData: WidgetSpanRow | WidgetSpanColumn): Widget[] { function watchWidgets(widgetData: WidgetSpanRow | WidgetSpanColumn): WidgetInstance[] {
let widgets: Widget[] = []; let widgets: WidgetInstance[] = [];
if (isWidgetSpanRow(widgetData)) widgets = widgetData.rowWidgets; if (isWidgetSpanRow(widgetData)) widgets = widgetData.rowWidgets;
else if (isWidgetSpanColumn(widgetData)) widgets = widgetData.columnWidgets; else if (isWidgetSpanColumn(widgetData)) widgets = widgetData.columnWidgets;
return widgets; return widgets;
@@ -2,19 +2,19 @@
import { getContext, onMount } from "svelte"; import { getContext, onMount } from "svelte";
import type { Editor } from "@graphite/editor"; import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages"; import type { Layout } from "@graphite/messages";
import { patchWidgetLayout, UpdateStatusBarHintsLayout } from "@graphite/messages"; import { patchLayout, UpdateStatusBarHintsLayout } from "@graphite/messages";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
let statusBarHintsLayout: LayoutGroup[] = []; let statusBarHintsLayout: Layout = [];
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateStatusBarHintsLayout, (updateStatusBarHintsLayout) => { editor.subscriptions.subscribeJsMessage(UpdateStatusBarHintsLayout, (updateStatusBarHintsLayout) => {
patchWidgetLayout(statusBarHintsLayout, updateStatusBarHintsLayout); patchLayout(statusBarHintsLayout, updateStatusBarHintsLayout);
statusBarHintsLayout = statusBarHintsLayout; statusBarHintsLayout = statusBarHintsLayout;
}); });
}); });
@@ -2,8 +2,8 @@
import { getContext, onMount } from "svelte"; import { getContext, onMount } from "svelte";
import type { Editor } from "@graphite/editor"; import type { Editor } from "@graphite/editor";
import type { LayoutGroup } from "@graphite/messages"; import type { Layout } from "@graphite/messages";
import { patchWidgetLayout, UpdateMenuBarLayout } from "@graphite/messages"; import { patchLayout, UpdateMenuBarLayout } from "@graphite/messages";
import type { AppWindowState } from "@graphite/state-providers/app-window"; import type { AppWindowState } from "@graphite/state-providers/app-window";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
@@ -15,11 +15,11 @@
const appWindow = getContext<AppWindowState>("appWindow"); const appWindow = getContext<AppWindowState>("appWindow");
const editor = getContext<Editor>("editor"); const editor = getContext<Editor>("editor");
let menuBarLayout: LayoutGroup[] = []; let menuBarLayout: Layout = [];
onMount(() => { onMount(() => {
editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => { editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => {
patchWidgetLayout(menuBarLayout, updateMenuBarLayout); patchLayout(menuBarLayout, updateMenuBarLayout);
menuBarLayout = menuBarLayout; menuBarLayout = menuBarLayout;
}); });
}); });
+30 -38
View File
@@ -1218,7 +1218,7 @@ export class PopoverButton extends WidgetProps {
tooltipShortcut!: ActionShortcut | undefined; tooltipShortcut!: ActionShortcut | undefined;
// Body // Body
popoverLayout!: LayoutGroup[]; popoverLayout!: Layout;
popoverMinWidth: number | undefined; popoverMinWidth: number | undefined;
} }
@@ -1453,19 +1453,14 @@ export function narrowWidgetProps<K extends WidgetPropsNames>(props: WidgetProps
else return undefined; else return undefined;
} }
export class Widget { export class WidgetInstance {
constructor(props: WidgetPropsSet, widgetId: bigint) {
this.props = props;
this.widgetId = widgetId;
}
@Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true }) @Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true })
props!: WidgetPropsSet; props!: WidgetPropsSet;
widgetId!: bigint; widgetId!: bigint;
} }
function hoistWidgetInstance(widgetInstance: any): Widget { function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
const kind = Object.keys(widgetInstance.widget)[0]; const kind = Object.keys(widgetInstance.widget)[0];
const props = widgetInstance.widget[kind]; const props = widgetInstance.widget[kind];
props.kind = kind; props.kind = kind;
@@ -1476,10 +1471,10 @@ function hoistWidgetInstance(widgetInstance: any): Widget {
const { widgetId } = widgetInstance; 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); return widgetInstance.map(hoistWidgetInstance);
} }
@@ -1506,15 +1501,29 @@ export type LayoutTarget =
export class WidgetDiffUpdate extends JsMessage { export class WidgetDiffUpdate extends JsMessage {
// TODO: Replace `any` with correct typing // 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[]; diff!: WidgetDiff[];
} }
type UIItem = LayoutGroup[] | LayoutGroup | Widget[] | Widget; type DiffUpdate = { layout: Layout } | { layoutGroup: LayoutGroup } | { widget: WidgetInstance };
type WidgetDiff = { widgetPath: number[]; newValue: UIItem }; 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 // 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) => { updates.diff.forEach((update) => {
// Find the object where the diff applies to // Find the object where the diff applies to
const diffObject = update.widgetPath.reduce((targetLayout: UIItem | undefined, index: number): UIItem | undefined => { 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 && "rowWidgets" in targetLayout) return targetLayout.rowWidgets[index];
if (targetLayout && "tableWidgets" in targetLayout) return targetLayout.tableWidgets[index]; if (targetLayout && "tableWidgets" in targetLayout) return targetLayout.tableWidgets[index];
if (targetLayout && "layout" in targetLayout) return targetLayout.layout[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) { if (targetLayout.props.kind === "PopoverButton" && targetLayout.props instanceof PopoverButton && targetLayout.props.popoverLayout) {
return targetLayout.props.popoverLayout[index]; 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. // tries to update the layout, it attempts to insert only the changes against the old layout that no longer exists.
if (diffObject === undefined) { if (diffObject === undefined) {
// eslint-disable-next-line no-console // 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; return;
} }
@@ -1559,45 +1568,28 @@ export function patchWidgetLayout(layout: /* &mut */ LayoutGroup[], updates: Wid
} }
export type LayoutGroup = WidgetSpanRow | WidgetSpanColumn | WidgetTable | WidgetSection; 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 { export function isWidgetSpanColumn(layoutColumn: LayoutGroup): layoutColumn is WidgetSpanColumn {
return Boolean((layoutColumn as WidgetSpanColumn)?.columnWidgets); return Boolean((layoutColumn as WidgetSpanColumn)?.columnWidgets);
} }
export type WidgetSpanRow = { rowWidgets: Widget[] }; export type WidgetSpanRow = { rowWidgets: WidgetInstance[] };
export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpanRow { export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpanRow {
return Boolean((layoutRow as WidgetSpanRow)?.rowWidgets); 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 { export function isWidgetTable(layoutTable: LayoutGroup): layoutTable is WidgetTable {
return Boolean((layoutTable as WidgetTable)?.tableWidgets); 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 { export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSection {
return Boolean((layoutRow as WidgetSection)?.layout); 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 // Unpacking a layout group
function createLayoutGroup(layoutGroup: any): LayoutGroup { function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.column) { if (layoutGroup.column) {
+8 -16
View File
@@ -2,25 +2,17 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import { type IconName } from "@graphite/icons"; import { type IconName } from "@graphite/icons";
import { import { DisplayDialog, DisplayDialogDismiss, UpdateDialogButtons, UpdateDialogColumn1, UpdateDialogColumn2, patchLayout, TriggerDisplayThirdPartyLicensesDialog } from "@graphite/messages";
DisplayDialog, import type { Layout } from "@graphite/messages";
DisplayDialogDismiss,
UpdateDialogButtons,
UpdateDialogColumn1,
UpdateDialogColumn2,
patchWidgetLayout,
TriggerDisplayThirdPartyLicensesDialog,
type LayoutGroup,
} from "@graphite/messages";
export function createDialogState(editor: Editor) { export function createDialogState(editor: Editor) {
const { subscribe, update } = writable({ const { subscribe, update } = writable({
visible: false, visible: false,
title: "", title: "",
icon: "" as IconName, icon: "" as IconName,
buttons: [] as LayoutGroup[], buttons: [] as Layout,
column1: [] as LayoutGroup[], column1: [] as Layout,
column2: [] as LayoutGroup[], column2: [] as Layout,
// Special case for the crash dialog because we cannot handle button widget callbacks from Rust once the editor has panicked // Special case for the crash dialog because we cannot handle button widget callbacks from Rust once the editor has panicked
panicDetails: "", panicDetails: "",
}); });
@@ -65,21 +57,21 @@ export function createDialogState(editor: Editor) {
}); });
editor.subscriptions.subscribeJsMessage(UpdateDialogButtons, (updateDialogButtons) => { editor.subscriptions.subscribeJsMessage(UpdateDialogButtons, (updateDialogButtons) => {
update((state) => { update((state) => {
patchWidgetLayout(state.buttons, updateDialogButtons); patchLayout(state.buttons, updateDialogButtons);
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateDialogColumn1, (updateDialogColumn1) => { editor.subscriptions.subscribeJsMessage(UpdateDialogColumn1, (updateDialogColumn1) => {
update((state) => { update((state) => {
patchWidgetLayout(state.column1, updateDialogColumn1); patchLayout(state.column1, updateDialogColumn1);
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateDialogColumn2, (updateDialogColumn2) => { editor.subscriptions.subscribeJsMessage(UpdateDialogColumn2, (updateDialogColumn2) => {
update((state) => { update((state) => {
patchWidgetLayout(state.column2, updateDialogColumn2); patchLayout(state.column2, updateDialogColumn2);
return state; return state;
}); });
+14 -14
View File
@@ -4,7 +4,7 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import { import {
patchWidgetLayout, patchLayout,
UpdateDocumentBarLayout, UpdateDocumentBarLayout,
UpdateDocumentModeLayout, UpdateDocumentModeLayout,
UpdateToolOptionsLayout, UpdateToolOptionsLayout,
@@ -13,18 +13,18 @@ import {
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
UpdateGraphViewOverlay, UpdateGraphViewOverlay,
UpdateGraphFadeArtwork, UpdateGraphFadeArtwork,
type LayoutGroup,
} from "@graphite/messages"; } from "@graphite/messages";
import type { Layout } from "@graphite/messages";
export function createDocumentState(editor: Editor) { export function createDocumentState(editor: Editor) {
const state = writable({ const state = writable({
// Layouts // Layouts
documentModeLayout: [] as LayoutGroup[], documentModeLayout: [] as Layout,
toolOptionsLayout: [] as LayoutGroup[], toolOptionsLayout: [] as Layout,
documentBarLayout: [] as LayoutGroup[], documentBarLayout: [] as Layout,
toolShelfLayout: [] as LayoutGroup[], toolShelfLayout: [] as Layout,
workingColorsLayout: [] as LayoutGroup[], workingColorsLayout: [] as Layout,
nodeGraphControlBarLayout: [] as LayoutGroup[], nodeGraphControlBarLayout: [] as Layout,
// Graph view overlay // Graph view overlay
graphViewOverlayOpen: false, graphViewOverlayOpen: false,
fadeArtwork: 100, fadeArtwork: 100,
@@ -42,7 +42,7 @@ export function createDocumentState(editor: Editor) {
await tick(); await tick();
update((state) => { update((state) => {
patchWidgetLayout(state.documentModeLayout, updateDocumentModeLayout); patchLayout(state.documentModeLayout, updateDocumentModeLayout);
return state; return state;
}); });
}); });
@@ -50,7 +50,7 @@ export function createDocumentState(editor: Editor) {
await tick(); await tick();
update((state) => { update((state) => {
patchWidgetLayout(state.toolOptionsLayout, updateToolOptionsLayout); patchLayout(state.toolOptionsLayout, updateToolOptionsLayout);
return state; return state;
}); });
}); });
@@ -58,7 +58,7 @@ export function createDocumentState(editor: Editor) {
await tick(); await tick();
update((state) => { update((state) => {
patchWidgetLayout(state.documentBarLayout, updateDocumentBarLayout); patchLayout(state.documentBarLayout, updateDocumentBarLayout);
return state; return state;
}); });
}); });
@@ -66,7 +66,7 @@ export function createDocumentState(editor: Editor) {
await tick(); await tick();
update((state) => { update((state) => {
patchWidgetLayout(state.toolShelfLayout, updateToolShelfLayout); patchLayout(state.toolShelfLayout, updateToolShelfLayout);
return state; return state;
}); });
}); });
@@ -74,13 +74,13 @@ export function createDocumentState(editor: Editor) {
await tick(); await tick();
update((state) => { update((state) => {
patchWidgetLayout(state.workingColorsLayout, updateWorkingColorsLayout); patchLayout(state.workingColorsLayout, updateWorkingColorsLayout);
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphControlBarLayout, (updateNodeGraphControlBarLayout) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphControlBarLayout, (updateNodeGraphControlBarLayout) => {
update((state) => { update((state) => {
patchWidgetLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout); patchLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
return state; return state;
}); });
}); });