From 938a688fa08511ed4a5f3c3d5bcfa26f4bd1eb47 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 1 Apr 2024 01:15:33 -0700 Subject: [PATCH] Polish up the Layers panel design --- .../utility_types/widgets/button_widgets.rs | 6 ++ .../document/document_message_handler.rs | 37 +++++++- .../node_graph/node_graph_message_handler.rs | 16 +++- .../portfolio/document/utility_types/nodes.rs | 7 ++ frontend/assets/icon-16px-solid/eye-hide.svg | 3 + frontend/assets/icon-16px-solid/eye-show.svg | 3 + frontend/assets/icon-16px-solid/new-layer.svg | 3 + .../assets/icon-16px-solid/padlock-locked.svg | 3 + .../icon-16px-solid/padlock-unlocked.svg | 3 + frontend/src/components/Editor.svelte | 11 +++ .../src/components/layout/LayoutCol.svelte | 1 + .../src/components/layout/LayoutRow.svelte | 1 + frontend/src/components/panels/Layers.svelte | 88 ++++++++++++++----- .../widgets/buttons/IconButton.svelte | 26 +++++- .../widgets/buttons/TextButton.svelte | 15 ++++ frontend/src/utility-functions/icons.ts | 10 +++ frontend/src/wasm-communication/messages.ts | 12 +++ 17 files changed, 217 insertions(+), 28 deletions(-) create mode 100644 frontend/assets/icon-16px-solid/eye-hide.svg create mode 100644 frontend/assets/icon-16px-solid/eye-show.svg create mode 100644 frontend/assets/icon-16px-solid/new-layer.svg create mode 100644 frontend/assets/icon-16px-solid/padlock-locked.svg create mode 100644 frontend/assets/icon-16px-solid/padlock-unlocked.svg diff --git a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs index f20f73e1..2918b59f 100644 --- a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs +++ b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs @@ -13,6 +13,9 @@ pub struct IconButton { #[widget_builder(constructor)] pub icon: String, + #[serde(rename = "hoverIcon")] + pub hover_icon: Option, + #[widget_builder(constructor)] pub size: u32, // TODO: Convert to an `IconSize` enum @@ -95,6 +98,9 @@ pub struct TextButton { pub icon: Option, + #[serde(rename = "hoverIcon")] + pub hover_icon: Option, + pub flush: bool, pub emphasized: bool, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 4f6f2442..e708c31b 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1296,7 +1296,8 @@ impl DocumentMessageHandler { widgets.extend([ Separator::new(SeparatorType::Unrelated).widget_holder(), TextButton::new("Node Graph") - .icon(Some(if self.graph_view_overlay_open { "GraphViewOpen".into() } else { "GraphViewClosed".into() })) + .icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into())) + .hover_icon(Some((if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" }).into())) .tooltip(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" }) .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle)) .on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into()) @@ -1358,6 +1359,10 @@ impl DocumentMessageHandler { }) .collect(); + let has_selection = self.selected_nodes.selected_layers(self.metadata()).next().is_some(); + let selection_all_visible = self.selected_nodes.selected_layers(self.metadata()).all(|layer| self.metadata().node_is_visible(layer.to_node())); + let selection_all_locked = false; // TODO: Implement + let layers_panel_options_bar = WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![ DropdownInput::new(blend_mode_menu_entries) @@ -1384,16 +1389,42 @@ impl DocumentMessageHandler { } }) .widget_holder(), + // Separator::new(SeparatorType::Unrelated).widget_holder(), - IconButton::new("Folder", 24) - .tooltip("New Folder") + // + IconButton::new("NewLayer", 24) + .tooltip("New Folder/Layer") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder)) .on_update(|_| DocumentMessage::CreateEmptyFolder.into()) .widget_holder(), + IconButton::new("Folder", 24) + .tooltip("Group Selected") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers)) + .on_update(|_| DocumentMessage::GroupSelectedLayers.into()) + .disabled(!has_selection) + .widget_holder(), IconButton::new("Trash", 24) .tooltip("Delete Selected") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers)) .on_update(|_| DocumentMessage::DeleteSelectedLayers.into()) + .disabled(!has_selection) + .widget_holder(), + // + Separator::new(SeparatorType::Unrelated).widget_holder(), + // + IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24) + .hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into())) + .tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" }) + .tooltip_shortcut(action_keys!(DialogMessageDiscriminant::RequestComingSoonDialog)) + .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1127) }.into()) + .disabled(!has_selection) + .widget_holder(), + IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24) + .hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into())) + .tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" }) + .tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility)) + .on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into()) + .disabled(!has_selection) .widget_holder(), ], }]); diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index e6a01d25..b0be96da 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -544,14 +544,16 @@ impl<'a> MessageHandler> for NodeGrap } fn actions(&self) -> ActionList { - unimplemented!("Must use `actions_with_graph_open` instead (unless we change every implementation of the MessageHandler trait).") + unimplemented!("Must use `actions_with_node_graph_open` instead (unless we change every implementation of the MessageHandler trait).") } } impl NodeGraphMessageHandler { pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList { if self.has_selection && graph_open { - actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedVisibility) + actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, DuplicateSelectedNodes, DeleteSelectedNodes, Cut, Copy) + } else if self.has_selection { + actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility) } else { actions!(NodeGraphMessageDiscriminant;) } @@ -777,15 +779,24 @@ impl NodeGraphMessageHandler { } }; + let parents_visible = layer + .ancestors(metadata) + .filter(|&ancestor| ancestor != layer) + .all(|layer| network.nodes.get(&layer.to_node()).map(|node| node.visible).unwrap_or_default()); + let data = LayerPanelEntry { id: node_id, layer_classification, expanded: layer.has_children(metadata) && !collapsed.0.contains(&layer), + has_children: layer.has_children(metadata), depth: layer.ancestors(metadata).count() - 1, parent_id: layer.parent(metadata).map(|parent| parent.to_node()), name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(), tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, visible: node.visible, + parents_visible, + unlocked: true, + parents_unlocked: true, }; responses.add(FrontendMessage::UpdateDocumentLayerDetails { data }); } @@ -918,6 +929,7 @@ impl Default for NodeGraphMessageHandler { Separator::new(SeparatorType::Unrelated).widget_holder(), TextButton::new("Node Graph") .icon(Some("GraphViewOpen".into())) + .hover_icon(Some("GraphViewClosed".into())) .tooltip("Hide Node Graph") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle)) .on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into()) diff --git a/editor/src/messages/portfolio/document/utility_types/nodes.rs b/editor/src/messages/portfolio/document/utility_types/nodes.rs index f3e788b8..c792d57f 100644 --- a/editor/src/messages/portfolio/document/utility_types/nodes.rs +++ b/editor/src/messages/portfolio/document/utility_types/nodes.rs @@ -46,7 +46,14 @@ pub struct LayerPanelEntry { #[serde(rename = "layerClassification")] pub layer_classification: LayerClassification, pub expanded: bool, + #[serde(rename = "hasChildren")] + pub has_children: bool, pub visible: bool, + #[serde(rename = "parentsVisible")] + pub parents_visible: bool, + pub unlocked: bool, + #[serde(rename = "parentsUnlocked")] + pub parents_unlocked: bool, #[serde(rename = "parentId")] pub parent_id: Option, pub depth: usize, diff --git a/frontend/assets/icon-16px-solid/eye-hide.svg b/frontend/assets/icon-16px-solid/eye-hide.svg new file mode 100644 index 00000000..805c7e5b --- /dev/null +++ b/frontend/assets/icon-16px-solid/eye-hide.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/icon-16px-solid/eye-show.svg b/frontend/assets/icon-16px-solid/eye-show.svg new file mode 100644 index 00000000..b34115eb --- /dev/null +++ b/frontend/assets/icon-16px-solid/eye-show.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/icon-16px-solid/new-layer.svg b/frontend/assets/icon-16px-solid/new-layer.svg new file mode 100644 index 00000000..45d82f6d --- /dev/null +++ b/frontend/assets/icon-16px-solid/new-layer.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/icon-16px-solid/padlock-locked.svg b/frontend/assets/icon-16px-solid/padlock-locked.svg new file mode 100644 index 00000000..440e2337 --- /dev/null +++ b/frontend/assets/icon-16px-solid/padlock-locked.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/icon-16px-solid/padlock-unlocked.svg b/frontend/assets/icon-16px-solid/padlock-unlocked.svg new file mode 100644 index 00000000..006fecd0 --- /dev/null +++ b/frontend/assets/icon-16px-solid/padlock-unlocked.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 8d0dbd05..c78a7a90 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -69,6 +69,7 @@ :root { // Replace usage of `-rgb` variants with CSS color() function to calculate alpha when browsers support it // See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color() and https://caniuse.com/css-color-function + // Specifically, support for the relative syntax is needed: `color(from var(--color-0-black) srgb r g b / 0.5)` to convert black to 50% alpha --color-0-black: #000; --color-0-black-rgb: 0, 0, 0; --color-1-nearblack: #111; @@ -138,6 +139,16 @@ --color-transparent-checkered-background-size: 16px 16px; --color-transparent-checkered-background-position: 0 0, 8px 8px; + --background-inactive-stripes: repeating-linear-gradient( + -45deg, + transparent 0px, + transparent calc((3px * sqrt(2) / 2) - 0.5px), + var(--color-5-dullgray) calc((3px * sqrt(2) / 2) - 0.5px), + var(--color-5-dullgray) calc((3px * sqrt(2) / 2) + 0.5px), + transparent calc((3px * sqrt(2) / 2) + 0.5px), + transparent calc(6px * sqrt(2) / 2) + ); + // Arrow triangle (#eee fill) --icon-expand-collapse-arrow: url('data:image/svg+xml;utf8,\ \ diff --git a/frontend/src/components/layout/LayoutCol.svelte b/frontend/src/components/layout/LayoutCol.svelte index cce3ffc1..346672af 100644 --- a/frontend/src/components/layout/LayoutCol.svelte +++ b/frontend/src/components/layout/LayoutCol.svelte @@ -6,6 +6,7 @@ export { styleName as style }; export let styles: Record = {}; export let tooltip: string | undefined = undefined; + // TODO: Add middle-click drag scrolling export let scrollableX = false; export let scrollableY = false; diff --git a/frontend/src/components/layout/LayoutRow.svelte b/frontend/src/components/layout/LayoutRow.svelte index 242eef86..f8354bc4 100644 --- a/frontend/src/components/layout/LayoutRow.svelte +++ b/frontend/src/components/layout/LayoutRow.svelte @@ -6,6 +6,7 @@ export { styleName as style }; export let styles: Record = {}; export let tooltip: string | undefined = undefined; + // TODO: Add middle-click drag scrolling export let scrollableX = false; export let scrollableY = false; diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index f1ca23cc..03f86205 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -377,6 +377,7 @@ classes={{ selected: fakeHighlight !== undefined ? fakeHighlight === listing.entry.id : $nodeGraph.selected.includes(listing.entry.id), "insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertParentId === listing.entry.id, + "nesting-layer": isNestingLayer(listing.entry.layerClassification), }} styles={{ "--layer-indent-levels": `${listing.entry.depth - 1}` }} data-layer @@ -387,7 +388,13 @@ on:click={(e) => selectLayerWithModifiers(e, listing)} > {#if isNestingLayer(listing.entry.layerClassification)} -