Skip to content

Commit

Permalink
Polish up the Layers panel design
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Apr 1, 2024
1 parent 5bab38e commit fc355cf
Show file tree
Hide file tree
Showing 15 changed files with 205 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub struct IconButton {
#[widget_builder(constructor)]
pub icon: String,

#[serde(rename = "hoverIcon")]
pub hover_icon: Option<String>,

#[widget_builder(constructor)]
pub size: u32, // TODO: Convert to an `IconSize` enum

Expand Down Expand Up @@ -95,6 +98,9 @@ pub struct TextButton {

pub icon: Option<String>,

#[serde(rename = "hoverIcon")]
pub hover_icon: Option<String>,

pub flush: bool,

pub emphasized: bool,
Expand Down
37 changes: 34 additions & 3 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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)
Expand All @@ -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 { "EyeHidden" } else { "EyeVisible" }).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(),
],
}]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,14 +544,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> 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;)
}
Expand Down Expand Up @@ -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 });
}
Expand Down Expand Up @@ -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())
Expand Down
7 changes: 7 additions & 0 deletions editor/src/messages/portfolio/document/utility_types/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NodeId>,
pub depth: usize,
Expand Down
3 changes: 3 additions & 0 deletions frontend/assets/icon-16px-solid/new-layer.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/assets/icon-16px-solid/padlock-locked.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/assets/icon-16px-solid/padlock-unlocked.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions frontend/src/components/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><polygon fill="%23eee" points="3,0 1,0 5,4 1,8 3,8 7,4" /></svg>\
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/layout/LayoutCol.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export { styleName as style };
export let styles: Record<string, string | number | undefined> = {};
export let tooltip: string | undefined = undefined;
// TODO: Add middle-click drag scrolling
export let scrollableX = false;
export let scrollableY = false;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/layout/LayoutRow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export { styleName as style };
export let styles: Record<string, string | number | undefined> = {};
export let tooltip: string | undefined = undefined;
// TODO: Add middle-click drag scrolling
export let scrollableX = false;
export let scrollableY = false;
Expand Down
84 changes: 64 additions & 20 deletions frontend/src/components/panels/Layers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -387,7 +388,13 @@
on:click={(e) => selectLayerWithModifiers(e, listing)}
>
{#if isNestingLayer(listing.entry.layerClassification)}
<button class="expand-arrow" class:expanded={listing.entry.expanded} on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.id)} tabindex="0" />
<button
class="expand-arrow"
class:expanded={listing.entry.expanded}
disabled={!listing.entry.hasChildren}
on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.id)}
tabindex="0"
/>
{#if listing.entry.layerClassification === "Artboard"}
<IconLabel icon="Artboard" class={"layer-type-icon"} />
{:else if listing.entry.layerClassification === "Folder"}
Expand All @@ -413,12 +420,25 @@
on:change={(e) => onEditLayerNameChange(listing, e)}
/>
</LayoutRow>
{#if !listing.entry.unlocked || !listing.entry.parentsUnlocked}
<IconButton
class={"status-toggle"}
classes={{ inactive: !listing.entry.parentsUnlocked }}
action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())}
size={24}
icon={listing.entry.parentsUnlocked ? "PadlockLocked" : "PadlockUnlocked"}
hoverIcon={listing.entry.parentsUnlocked ? "PadlockUnlocked" : "PadlockLocked"}
tooltip={listing.entry.parentsUnlocked ? "Unlock" : "Lock"}
/>
{/if}
<IconButton
class={"visibility"}
class={"status-toggle"}
classes={{ inactive: !listing.entry.parentsVisible }}
action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())}
size={24}
icon={listing.entry.visible ? "EyeVisible" : "EyeHidden"}
tooltip={listing.entry.visible ? "Visible" : "Hidden"}
hoverIcon={listing.entry.visible ? "EyeHidden" : "EyeVisible"}
tooltip={listing.entry.visible ? "Hide" : "Show"}
/>
</LayoutRow>
{/each}
Expand All @@ -443,15 +463,26 @@
min-width: 300px;
}
// Blend mode selector and opacity slider
.dropdown-input,
.number-input {
flex: 1 1 auto;
}
// Blend mode selector
.dropdown-input {
max-width: 120px;
flex-basis: 120px;
}
// Blend mode selector and opacity slider
.dropdown-input,
// Opacity slider
.number-input {
flex: 1 1 auto;
max-width: 180px;
flex-basis: 180px;
+ .separator ~ .separator {
flex-grow: 1;
}
}
}
Expand All @@ -464,11 +495,15 @@
flex: 0 0 auto;
align-items: center;
position: relative;
border-bottom: 1px solid var(--color-2-mildblack);
border-radius: 2px;
height: 32px;
margin: 0 4px;
padding-left: calc(4px + var(--layer-indent-levels) * 16px);
border-bottom: 1px solid var(--color-2-mildblack);
border-radius: 2px;
&.nesting-layer {
padding-left: calc(var(--layer-indent-levels) * 16px);
}
&.selected {
background: var(--color-4-dimgray);
Expand All @@ -493,23 +528,28 @@
justify-content: center;
border-radius: 2px;
&:hover {
background: var(--color-5-dullgray);
}
&::after {
content: "";
position: absolute;
width: 0;
height: 0;
border-style: solid;
border-width: 3px 0 3px 6px;
border-color: transparent transparent transparent var(--color-e-nearwhite);
width: 8px;
height: 8px;
background: var(--icon-expand-collapse-arrow);
}
&[disabled]::after {
background: var(--icon-expand-collapse-arrow-disabled);
}
&:hover:not([disabled]) {
background: var(--color-5-dullgray);
&::after {
background: var(--icon-expand-collapse-arrow-hover);
}
}
&.expanded::after {
border-width: 6px 3px 0 3px;
border-color: var(--color-e-nearwhite) transparent transparent transparent;
transform: rotate(90deg);
}
}
Expand Down Expand Up @@ -573,11 +613,15 @@
}
}
.visibility {
.status-toggle {
flex: 0 0 auto;
align-items: center;
height: 100%;
&.inactive {
background-image: var(--background-inactive-stripes);
}
.icon-button {
height: 100%;
width: calc(24px + 2 * 4px);
Expand Down

0 comments on commit fc355cf

Please sign in to comment.