Tidy up the UI with clearer Layers panel selection marks and removal of most "coming soon" UI elements

This commit is contained in:
Keavon Chambers 2024-12-14 20:49:21 -08:00
parent 287ef26f64
commit 1264ea8246
11 changed files with 143 additions and 126 deletions

View File

@ -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("%")

View File

@ -2250,13 +2250,17 @@ impl NodeGraphMessageHandler {
.map(|layer| layer.to_node())
.collect::<HashSet<_>>();
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 });
}

View File

@ -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<NodeId>,
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)]

View File

@ -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;

View File

@ -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::<Vec<_>>())
})
.collect::<Vec<_>>())
.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<ToolAvailability>> {
],
vec![
// Raster tool group
// ToolAvailability::Available(Box::<imaginate_tool::ImaginateTool>::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::<brush_tool::BrushTool>::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::<imaginate_tool::ImaginateTool>::default()), // TODO: Fix and reenable
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Heal, "RasterImaginateTool").tooltip("Coming Soon: Imaginate Tool")),
],
]
}

View File

@ -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,\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" width="6px" height="6px" fill="%23444"><rect width="1" height="1" /><rect x="3" y="3" width="1" height="1" /></svg>\
');
// Array of 2x3 dots (fill: --color-e-nearwhite)
--icon-drag-grip: url('data:image/svg+xml;utf8,\

View File

@ -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;

View File

@ -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 @@
<LayoutRow class="list-area" scrollableY={true}>
<LayoutCol class="list" data-layer-panel bind:this={list} on:click={() => 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}
<LayoutRow
class="layer"
classes={{
selected: fakeHighlight !== undefined ? fakeHighlight === listing.entry.id : listing.entry.selected,
"full-highlight": listing.entry.inSelectedNetwork && !listing.entry.selectedParent,
selected,
"ancestor-of-selected": listing.entry.ancestorOfSelected,
"descendant-of-selected": listing.entry.descendantOfSelected,
"selected-but-not-in-selected-network": selected && !listing.entry.inSelectedNetwork,
"insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertParentId === listing.entry.id,
}}
styles={{ "--layer-indent-levels": `${listing.entry.depth - 1}` }}
@ -429,6 +432,7 @@
class="expand-arrow"
class:expanded={listing.entry.expanded}
disabled={!listing.entry.childrenPresent}
title={listing.entry.expanded ? "Collapse" : `Expand${listing.entry.ancestorOfSelected ? "\n(A selected layer is contained within)" : ""}`}
on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.id)}
tabindex="0"
/>
@ -457,22 +461,22 @@
{#if !listing.entry.unlocked || !listing.entry.parentsUnlocked}
<IconButton
class={"status-toggle"}
classes={{ inactive: !listing.entry.parentsUnlocked }}
classes={{ inherited: !listing.entry.parentsUnlocked }}
action={(e) => (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}
<IconButton
class={"status-toggle"}
classes={{ inactive: !listing.entry.parentsVisible }}
classes={{ inherited: !listing.entry.parentsVisible }}
action={(e) => (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)" : "")}
/>
</LayoutRow>
{/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 {

View File

@ -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;

View File

@ -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);

View File

@ -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 {}