diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 6c560c5b..502438fc 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1699,25 +1699,25 @@ impl DocumentMessageHandler { let document_mode_layout = WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![ - DropdownInput::new( - vec![vec![ - MenuListEntry::new(format!("{:?}", DocumentMode::DesignMode)) - .label(DocumentMode::DesignMode.to_string()) - .icon(DocumentMode::DesignMode.icon_name()), - MenuListEntry::new(format!("{:?}", DocumentMode::SelectMode)) - .label(DocumentMode::SelectMode.to_string()) - .icon(DocumentMode::SelectMode.icon_name()) - .on_commit(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()), - MenuListEntry::new(format!("{:?}", DocumentMode::GuideMode)) - .label(DocumentMode::GuideMode.to_string()) - .icon(DocumentMode::GuideMode.icon_name()) - .on_commit(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()), - ]]) - .selected_index(Some(self.document_mode as u32)) - .draw_icon(true) - .interactive(false) // TODO: set to true when dialogs are not spawned - .widget_holder(), - Separator::new(SeparatorType::Section).widget_holder(), + // DropdownInput::new( + // vec![vec![ + // MenuListEntry::new(format!("{:?}", DocumentMode::DesignMode)) + // .label(DocumentMode::DesignMode.to_string()) + // .icon(DocumentMode::DesignMode.icon_name()), + // MenuListEntry::new(format!("{:?}", DocumentMode::SelectMode)) + // .label(DocumentMode::SelectMode.to_string()) + // .icon(DocumentMode::SelectMode.icon_name()) + // .on_commit(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()), + // MenuListEntry::new(format!("{:?}", DocumentMode::GuideMode)) + // .label(DocumentMode::GuideMode.to_string()) + // .icon(DocumentMode::GuideMode.icon_name()) + // .on_commit(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()), + // ]]) + // .selected_index(Some(self.document_mode as u32)) + // .draw_icon(true) + // .interactive(false) // TODO: set to true when dialogs are not spawned + // .widget_holder(), + // Separator::new(SeparatorType::Section).widget_holder(), ], }]); @@ -1744,7 +1744,7 @@ impl DocumentMessageHandler { widgets: vec![TextLabel::new("Overlays").bold(true).widget_holder()], }, LayoutGroup::Row { - widgets: vec![TextLabel::new("Coming soon").widget_holder()], + widgets: vec![TextLabel::new("Granular settings in this menu are coming soon").widget_holder()], }, ]) .widget_holder(), @@ -1828,16 +1828,16 @@ impl DocumentMessageHandler { _ => Some(1), }) .widget_holder(), - PopoverButton::new() - .popover_layout(vec![ - LayoutGroup::Row { - widgets: vec![TextLabel::new("View Mode").bold(true).widget_holder()], - }, - LayoutGroup::Row { - widgets: vec![TextLabel::new("Coming soon").widget_holder()], - }, - ]) - .widget_holder(), + // PopoverButton::new() + // .popover_layout(vec![ + // LayoutGroup::Row { + // widgets: vec![TextLabel::new("View Mode").bold(true).widget_holder()], + // }, + // LayoutGroup::Row { + // widgets: vec![TextLabel::new("Coming soon").widget_holder()], + // }, + // ]) + // .widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder(), ]; @@ -2191,7 +2191,7 @@ impl<'a> ClickXRayIter<'a> { } } -pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, tooltip_name: &str) -> [WidgetHolder; 6] { +pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, tooltip_name: &str) -> [WidgetHolder; 5] { [ IconButton::new("ZoomIn", 24) .tooltip("Zoom In") @@ -2209,34 +2209,34 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand .on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into()) .disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4) .widget_holder(), - PopoverButton::new() - .popover_layout(vec![ - LayoutGroup::Row { - widgets: vec![TextLabel::new(format!("{tooltip_name} Navigation")).bold(true).widget_holder()], - }, - LayoutGroup::Row { - widgets: vec![TextLabel::new({ - let tilt = if tooltip_name == "Canvas" { "Tilt:\n• Alt + Middle Click Drag\n\n" } else { "" }; - format!( - " - Interactive controls in this\n\ - menu are coming soon.\n\ - \n\ - Pan:\n\ - • Middle Click Drag\n\ - \n\ - {tilt}Zoom:\n\ - • Shift + Middle Click Drag\n\ - • Ctrl + Scroll Wheel Roll - " - ) - .trim() - }) - .multiline(true) - .widget_holder()], - }, - ]) - .widget_holder(), + // PopoverButton::new() + // .popover_layout(vec![ + // LayoutGroup::Row { + // widgets: vec![TextLabel::new(format!("{tooltip_name} Navigation")).bold(true).widget_holder()], + // }, + // LayoutGroup::Row { + // widgets: vec![TextLabel::new({ + // let tilt = if tooltip_name == "Canvas" { "Tilt:\n• Alt + Middle Click Drag\n\n" } else { "" }; + // format!( + // " + // Interactive controls in this\n\ + // menu are coming soon.\n\ + // \n\ + // Pan:\n\ + // • Middle Click Drag\n\ + // \n\ + // {tilt}Zoom:\n\ + // • Shift + Middle Click Drag\n\ + // • Ctrl + Scroll Wheel Roll + // " + // ) + // .trim() + // }) + // .multiline(true) + // .widget_holder()], + // }, + // ]) + // .widget_holder(), Separator::new(SeparatorType::Related).widget_holder(), NumberInput::new(Some(navigation_handler.snapped_zoom(ptz.zoom()) * 100.)) .unit("%") 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 2700c938..972035a7 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 @@ -2250,13 +2250,17 @@ impl NodeGraphMessageHandler { .map(|layer| layer.to_node()) .collect::>(); - let mut selected_parents = HashSet::new(); + let mut ancestors_of_selected = HashSet::new(); + let mut descendants_of_selected = HashSet::new(); for selected_layer in &selected_layers { for ancestor in LayerNodeIdentifier::new(*selected_layer, network_interface, &[]).ancestors(network_interface.document_metadata()) { - if ancestor != LayerNodeIdentifier::ROOT_PARENT && !selected_layers.contains(&ancestor.to_node()) { - selected_parents.insert(ancestor.to_node()); + if ancestor != LayerNodeIdentifier::ROOT_PARENT && ancestor.to_node() != *selected_layer { + ancestors_of_selected.insert(ancestor.to_node()); } } + for descendant in LayerNodeIdentifier::new(*selected_layer, network_interface, &[]).descendants(network_interface.document_metadata()) { + descendants_of_selected.insert(descendant.to_node()); + } } for (&node_id, node_metadata) in &network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata { @@ -2294,26 +2298,25 @@ impl NodeGraphMessageHandler { } }); - let is_selected_parent = selected_parents.contains(&node_id); - let data = LayerPanelEntry { id: node_id, + alias: network_interface.frontend_display_name(&node_id, &[]), + tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, + in_selected_network: selection_network_path.is_empty(), children_allowed, children_present: layer.has_children(network_interface.document_metadata()), expanded: layer.has_children(network_interface.document_metadata()) && !collapsed.0.contains(&layer), depth: layer.ancestors(network_interface.document_metadata()).count() - 1, - parent_id: layer - .parent(network_interface.document_metadata()) - .and_then(|parent| if parent != LayerNodeIdentifier::ROOT_PARENT { Some(parent.to_node()) } else { None }), - alias: network_interface.frontend_display_name(&node_id, &[]), - tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, visible: network_interface.is_visible(&node_id, &[]), parents_visible, unlocked: !network_interface.is_locked(&node_id, &[]), parents_unlocked, - selected: selected_layers.contains(&node_id) || is_selected_parent, - in_selected_network: selection_network_path.is_empty(), - selected_parent: is_selected_parent, + parent_id: layer + .parent(network_interface.document_metadata()) + .and_then(|parent| if parent != LayerNodeIdentifier::ROOT_PARENT { Some(parent.to_node()) } else { None }), + selected: selected_layers.contains(&node_id), + ancestor_of_selected: ancestors_of_selected.contains(&node_id), + descendant_of_selected: descendants_of_selected.contains(&node_id), }; responses.add(FrontendMessage::UpdateDocumentLayerDetails { data }); } diff --git a/editor/src/messages/portfolio/document/utility_types/nodes.rs b/editor/src/messages/portfolio/document/utility_types/nodes.rs index ebd839a7..7f579679 100644 --- a/editor/src/messages/portfolio/document/utility_types/nodes.rs +++ b/editor/src/messages/portfolio/document/utility_types/nodes.rs @@ -36,6 +36,8 @@ pub struct LayerPanelEntry { pub id: NodeId, pub alias: String, pub tooltip: String, + #[serde(rename = "inSelectedNetwork")] + pub in_selected_network: bool, #[serde(rename = "childrenAllowed")] pub children_allowed: bool, #[serde(rename = "childrenPresent")] @@ -51,10 +53,10 @@ pub struct LayerPanelEntry { #[serde(rename = "parentId")] pub parent_id: Option, pub selected: bool, - #[serde(rename = "inSelectedNetwork")] - pub in_selected_network: bool, - #[serde(rename = "selectedParent")] - pub selected_parent: bool, + #[serde(rename = "ancestorOfSelected")] + pub ancestor_of_selected: bool, + #[serde(rename = "descendantOfSelected")] + pub descendant_of_selected: bool, } #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)] diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index db55da1e..87c39456 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -176,19 +176,19 @@ impl LayoutHolder for SelectTool { let disabled = self.tool_data.selected_layers_count < 2; widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); widgets.extend(self.alignment_widgets(disabled)); - widgets.push( - PopoverButton::new() - .popover_layout(vec![ - LayoutGroup::Row { - widgets: vec![TextLabel::new("Align").bold(true).widget_holder()], - }, - LayoutGroup::Row { - widgets: vec![TextLabel::new("Coming soon").widget_holder()], - }, - ]) - .disabled(disabled) - .widget_holder(), - ); + // widgets.push( + // PopoverButton::new() + // .popover_layout(vec![ + // LayoutGroup::Row { + // widgets: vec![TextLabel::new("Align").bold(true).widget_holder()], + // }, + // LayoutGroup::Row { + // widgets: vec![TextLabel::new("Coming soon").widget_holder()], + // }, + // ]) + // .disabled(disabled) + // .widget_holder(), + // ); // Flip let disabled = self.tool_data.selected_layers_count == 0; diff --git a/editor/src/messages/tool/utility_types.rs b/editor/src/messages/tool/utility_types.rs index 561c38db..74d4c179 100644 --- a/editor/src/messages/tool/utility_types.rs +++ b/editor/src/messages/tool/utility_types.rs @@ -250,14 +250,13 @@ impl LayoutHolder for ToolData { .iter() .map(|tool_group| tool_group.iter().map(|tool_availability| { match tool_availability { - ToolAvailability::Available(tool) => ToolEntry::new( tool.tool_type(), tool.icon_name()) - .tooltip( tool.tooltip()) - .tooltip_shortcut(action_keys!(tool_type_to_activate_tool_message(tool.tool_type()))) - - , + ToolAvailability::Available(tool) => ToolEntry::new(tool.tool_type(), tool.icon_name()) + .tooltip(tool.tooltip()) + .tooltip_shortcut(action_keys!(tool_type_to_activate_tool_message(tool.tool_type()))), ToolAvailability::ComingSoon(tool) => tool.clone(), } - }).collect::>()) + }) + .collect::>()) .flat_map(|group| { let separator = std::iter::once(Separator::new(SeparatorType::Section).direction(SeparatorDirection::Vertical).widget_holder()); let buttons = group.into_iter().map(|ToolEntry { tooltip, tooltip_shortcut, tool_type, icon_name }| { @@ -397,16 +396,14 @@ fn list_tools_in_groups() -> Vec> { ], vec![ // Raster tool group - // ToolAvailability::Available(Box::::default()), // TODO: Fix and reenable ASAP - ToolAvailability::ComingSoon( - ToolEntry::new(ToolType::Heal, "RasterImaginateTool").tooltip("Coming Soon: Imaginate Tool - Temporarily disabled, please use Imaginate node directly from graph"), - ), ToolAvailability::Available(Box::::default()), ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Heal, "RasterHealTool").tooltip("Coming Soon: Heal Tool (J)")), ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Clone, "RasterCloneTool").tooltip("Coming Soon: Clone Tool (C)")), ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Patch, "RasterPatchTool").tooltip("Coming Soon: Patch Tool")), ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Detail, "RasterDetailTool").tooltip("Coming Soon: Detail Tool (D)")), ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Relight, "RasterRelightTool").tooltip("Coming Soon: Relight Tool (O)")), + // ToolAvailability::Available(Box::::default()), // TODO: Fix and reenable + ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Heal, "RasterImaginateTool").tooltip("Coming Soon: Imaginate Tool")), ], ] } diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 67be1cce..749ecfea 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -152,7 +152,7 @@ --color-transparent-checkered-background-position-mini: 0 0, 4px 4px, 4px 4px; --color-transparent-checkered-background-repeat: repeat, repeat, repeat; - --background-inactive-stripes: repeating-linear-gradient( + --inheritance-stripes-background: repeating-linear-gradient( -45deg, transparent 0px, transparent calc((3px * sqrt(2) / 2) - 0.5px), @@ -161,6 +161,9 @@ transparent calc((3px * sqrt(2) / 2) + 0.5px), transparent calc(6px * sqrt(2) / 2) ); + --inheritance-dots-background: url('data:image/svg+xml;utf8,\ + \ + '); // Array of 2x3 dots (fill: --color-e-nearwhite) --icon-drag-grip: url('data:image/svg+xml;utf8,\ diff --git a/frontend/src/components/layout/FloatingMenu.svelte b/frontend/src/components/layout/FloatingMenu.svelte index 9dd4f9e5..861435da 100644 --- a/frontend/src/components/layout/FloatingMenu.svelte +++ b/frontend/src/components/layout/FloatingMenu.svelte @@ -473,7 +473,7 @@ .floating-menu-content { background: var(--color-2-mildblack); - box-shadow: rgba(var(--color-0-black-rgb), 50%) 0 2px 4px; + box-shadow: rgba(var(--color-0-black-rgb), 0.5) 0 2px 4px; border-radius: 4px; color: var(--color-e-nearwhite); font-size: inherit; diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index ea6acd5b..b58b3bad 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -43,7 +43,7 @@ // Interactive dragging let draggable = true; let draggingData: undefined | DraggingData = undefined; - let fakeHighlight: undefined | bigint = undefined; + let fakeHighlightOfNotYetSelectedLayerBeingDragged: undefined | bigint = undefined; let dragInPanel = false; // Layouts @@ -285,7 +285,7 @@ const layer = listing.entry; dragInPanel = true; if (!$nodeGraph.selected.includes(layer.id)) { - fakeHighlight = layer.id; + fakeHighlightOfNotYetSelectedLayerBeingDragged = layer.id; } const select = () => { if (!$nodeGraph.selected.includes(layer.id)) selectLayer(listing, false, false); @@ -358,7 +358,7 @@ } draggingData = undefined; - fakeHighlight = undefined; + fakeHighlightOfNotYetSelectedLayerBeingDragged = undefined; dragInPanel = false; } @@ -409,11 +409,14 @@ deselectAllLayers()} on:dragover={updateInsertLine} on:dragend={drop} on:drop={drop}> {#each layers as listing, index} + {@const selected = fakeHighlightOfNotYetSelectedLayerBeingDragged !== undefined ? fakeHighlightOfNotYetSelectedLayerBeingDragged === listing.entry.id : listing.entry.selected} handleExpandArrowClick(listing.entry.id)} tabindex="0" /> @@ -457,22 +461,22 @@ {#if !listing.entry.unlocked || !listing.entry.parentsUnlocked} (toggleLayerLock(listing.entry.id), e?.stopPropagation())} size={24} icon={listing.entry.unlocked ? "PadlockUnlocked" : "PadlockLocked"} hoverIcon={listing.entry.unlocked ? "PadlockLocked" : "PadlockUnlocked"} - tooltip={listing.entry.unlocked ? "Lock" : "Unlock"} + tooltip={(listing.entry.unlocked ? "Lock" : "Unlock") + (!listing.entry.parentsUnlocked ? "\n(A parent of this layer is locked and that status is being inherited)" : "")} /> {/if} (toggleNodeVisibilityLayerPanel(listing.entry.id), e?.stopPropagation())} size={24} icon={listing.entry.visible ? "EyeVisible" : "EyeHidden"} hoverIcon={listing.entry.visible ? "EyeHide" : "EyeShow"} - tooltip={listing.entry.visible ? "Hide" : "Show"} + tooltip={(listing.entry.visible ? "Hide" : "Show") + (!listing.entry.parentsVisible ? "\n(A parent of this layer is hidden and that status is being inherited)" : "")} /> {/each} @@ -537,13 +541,19 @@ // Dimming &.selected { - // 1/3 between 3-darkgray and 4-dimgray (this interpolation approach only works on grayscale values) - --component: calc((Max(var(--color-3-darkgray-rgb)) * 2 + Max(var(--color-4-dimgray-rgb))) / 3); - background: rgb(var(--component), var(--component), var(--component)); + background: var(--color-4-dimgray); + } - &.full-highlight { - background: var(--color-4-dimgray); - } + &.ancestor-of-selected .expand-arrow:not(.expanded) { + background-image: var(--inheritance-stripes-background); + } + + &.descendant-of-selected { + background-image: var(--inheritance-dots-background); + } + + &.selected-but-not-in-selected-network { + background: rgba(var(--color-4-dimgray-rgb), 0.5); } &.insert-folder { @@ -664,8 +674,8 @@ align-items: center; height: 100%; - &.inactive { - background-image: var(--background-inactive-stripes); + &.inherited { + background-image: var(--inheritance-stripes-background); } .icon-button { diff --git a/frontend/src/components/widgets/buttons/ColorButton.svelte b/frontend/src/components/widgets/buttons/ColorButton.svelte index 90e9170a..c1581045 100644 --- a/frontend/src/components/widgets/buttons/ColorButton.svelte +++ b/frontend/src/components/widgets/buttons/ColorButton.svelte @@ -54,7 +54,7 @@ &.open { &, > .text-label { - background: rgba(var(--color-6-lowergray-rgb), 50%); + background: rgba(var(--color-6-lowergray-rgb), 0.5); } } @@ -101,13 +101,13 @@ bottom: 0; left: 0; right: 0; - background: rgba(var(--color-4-dimgray-rgb), 50%); + background: rgba(var(--color-4-dimgray-rgb), 0.5); } } } > .text-label { - background: rgba(var(--color-5-dullgray-rgb), 50%); + background: rgba(var(--color-5-dullgray-rgb), 0.5); font-size: 10px; line-height: 12px; height: 12px; diff --git a/frontend/src/components/widgets/inputs/NumberInput.svelte b/frontend/src/components/widgets/inputs/NumberInput.svelte index 82b7c904..a9d64e61 100644 --- a/frontend/src/components/widgets/inputs/NumberInput.svelte +++ b/frontend/src/components/widgets/inputs/NumberInput.svelte @@ -651,12 +651,12 @@ &.increment { // Widen the label and input margins from the edges by an extra 8px to make room for the increment arrows label { - margin-left: 16px; + margin-left: 8px; } // Keep the right-aligned input element from overlapping the increment arrow on the right input[type="text"]:not(:focus).has-label { - margin-right: 16px; + margin-right: 8px; } // Hide the increment arrows when entering text, disabled, or not hovered @@ -680,7 +680,7 @@ padding: 9px 0; border: none; border-radius: 2px; - background: none; + background: rgba(var(--color-1-nearblack-rgb), 0.5); &:hover { background: var(--color-4-dimgray); diff --git a/frontend/src/wasm-communication/messages.ts b/frontend/src/wasm-communication/messages.ts index c6fdf916..feba002f 100644 --- a/frontend/src/wasm-communication/messages.ts +++ b/frontend/src/wasm-communication/messages.ts @@ -861,6 +861,8 @@ export class LayerPanelEntry { @Transform(({ value }: { value: string }) => value || undefined) tooltip!: string | undefined; + inSelectedNetwork!: boolean; + childrenAllowed!: boolean; childrenPresent!: boolean; @@ -882,9 +884,9 @@ export class LayerPanelEntry { selected!: boolean; - inSelectedNetwork!: boolean; + ancestorOfSelected!: boolean; - selectedParent!: boolean; + descendantOfSelected!: boolean; } export class DisplayDialogDismiss extends JsMessage {}