Skip to content

Commit 899ed5a

Browse files
committed
Tidy up the Layers panel with a new bottom bar
1 parent 66a297d commit 899ed5a

File tree

23 files changed

+284
-90
lines changed

23 files changed

+284
-90
lines changed

editor/src/messages/frontend/frontend_message.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,17 @@ pub enum FrontendMessage {
240240
#[serde(rename = "hintData")]
241241
hint_data: HintData,
242242
},
243-
UpdateLayersPanelControlBarLayout {
243+
UpdateLayersPanelControlBarLeftLayout {
244+
#[serde(rename = "layoutTarget")]
245+
layout_target: LayoutTarget,
246+
diff: Vec<WidgetDiff>,
247+
},
248+
UpdateLayersPanelControlBarRightLayout {
249+
#[serde(rename = "layoutTarget")]
250+
layout_target: LayoutTarget,
251+
diff: Vec<WidgetDiff>,
252+
},
253+
UpdateLayersPanelBottomBarLayout {
244254
#[serde(rename = "layoutTarget")]
245255
layout_target: LayoutTarget,
246256
diff: Vec<WidgetDiff>,

editor/src/messages/layout/layout_message_handler.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,9 @@ impl LayoutMessageHandler {
424424
LayoutTarget::DialogColumn2 => FrontendMessage::UpdateDialogColumn2 { layout_target, diff },
425425
LayoutTarget::DocumentBar => FrontendMessage::UpdateDocumentBarLayout { layout_target, diff },
426426
LayoutTarget::DocumentMode => FrontendMessage::UpdateDocumentModeLayout { layout_target, diff },
427-
LayoutTarget::LayersPanelControlBar => FrontendMessage::UpdateLayersPanelControlBarLayout { layout_target, diff },
427+
LayoutTarget::LayersPanelControlLeftBar => FrontendMessage::UpdateLayersPanelControlBarLeftLayout { layout_target, diff },
428+
LayoutTarget::LayersPanelControlRightBar => FrontendMessage::UpdateLayersPanelControlBarRightLayout { layout_target, diff },
429+
LayoutTarget::LayersPanelBottomBar => FrontendMessage::UpdateLayersPanelBottomBarLayout { layout_target, diff },
428430
LayoutTarget::MenuBar => unreachable!("Menu bar is not diffed"),
429431
LayoutTarget::NodeGraphControlBar => FrontendMessage::UpdateNodeGraphControlBarLayout { layout_target, diff },
430432
LayoutTarget::PropertiesSections => FrontendMessage::UpdatePropertyPanelSectionsLayout { layout_target, diff },

editor/src/messages/layout/utility_types/layout_widget.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ pub enum LayoutTarget {
3131
DocumentBar,
3232
/// Contains the dropdown for design / select / guide mode found on the top left of the canvas.
3333
DocumentMode,
34-
/// Options for opacity seen at the top of the Layers panel.
35-
LayersPanelControlBar,
34+
/// Blending options at the top of the Layers panel.
35+
LayersPanelControlLeftBar,
36+
/// Selected layer status (locked/hidden) at the top of the Layers panel.
37+
LayersPanelControlRightBar,
38+
/// Controls for adding, grouping, and deleting layers at the bottom of the Layers panel.
39+
LayersPanelBottomBar,
3640
/// The dropdown menu at the very top of the application: File, Edit, etc.
3741
MenuBar,
3842
/// Bar at the top of the node graph containing the location and the "Preview" and "Hide" buttons.

editor/src/messages/layout/utility_types/widgets/button_widgets.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub struct IconButton {
4242
pub struct PopoverButton {
4343
pub style: Option<String>,
4444

45+
#[serde(rename = "menuDirection")]
46+
pub menu_direction: Option<MenuDirection>,
47+
4548
pub icon: Option<String>,
4649

4750
pub disabled: bool,
@@ -58,6 +61,20 @@ pub struct PopoverButton {
5861
pub popover_min_width: Option<u32>,
5962
}
6063

64+
#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
65+
pub enum MenuDirection {
66+
Top,
67+
#[default]
68+
Bottom,
69+
Left,
70+
Right,
71+
TopLeft,
72+
TopRight,
73+
BottomLeft,
74+
BottomRight,
75+
Center,
76+
}
77+
6178
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
6279
#[derivative(Debug, PartialEq)]
6380
pub struct ParameterExposeButton {

editor/src/messages/layout/utility_types/widgets/input_widgets.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ pub struct DropdownInput {
6767

6868
#[serde(skip)]
6969
pub tooltip_shortcut: Option<ActionKeys>,
70+
71+
// Styling
72+
#[serde(rename = "minWidth")]
73+
pub min_width: u32,
74+
75+
#[serde(rename = "maxWidth")]
76+
pub max_width: u32,
7077
//
7178
// Callbacks
7279
// `on_update` exists on the `MenuListEntry`, not this parent `DropdownInput`
@@ -208,6 +215,9 @@ pub struct NumberInput {
208215
#[serde(rename = "minWidth")]
209216
pub min_width: u32,
210217

218+
#[serde(rename = "maxWidth")]
219+
pub max_width: u32,
220+
211221
// Callbacks
212222
#[serde(skip)]
213223
#[derivative(Debug = "ignore", PartialEq = "ignore")]

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,17 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
299299
// Clear the control bar
300300
responses.add(LayoutMessage::SendLayout {
301301
layout: Layout::WidgetLayout(Default::default()),
302-
layout_target: LayoutTarget::LayersPanelControlBar,
302+
layout_target: LayoutTarget::LayersPanelControlLeftBar,
303+
});
304+
responses.add(LayoutMessage::SendLayout {
305+
layout: Layout::WidgetLayout(Default::default()),
306+
layout_target: LayoutTarget::LayersPanelControlRightBar,
307+
});
308+
309+
// Clear the bottom bar
310+
responses.add(LayoutMessage::SendLayout {
311+
layout: Layout::WidgetLayout(Default::default()),
312+
layout_target: LayoutTarget::LayersPanelBottomBar,
303313
});
304314
}
305315
DocumentMessage::CreateEmptyFolder => {
@@ -344,6 +354,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
344354
DocumentMessage::DocumentHistoryForward => self.redo_with_history(ipp, responses),
345355
DocumentMessage::DocumentStructureChanged => {
346356
self.update_layers_panel_control_bar_widgets(responses);
357+
self.update_layers_panel_bottom_bar_widgets(responses);
347358

348359
self.network_interface.load_structure();
349360
let data_buffer: RawBuffer = self.serialize_root();
@@ -2509,12 +2520,14 @@ impl DocumentMessageHandler {
25092520
.selected_index(blend_mode.and_then(|blend_mode| blend_mode.index_in_list_svg_subset()).map(|index| index as u32))
25102521
.disabled(disabled)
25112522
.draw_icon(false)
2523+
.max_width(100)
2524+
.tooltip("Blend Mode")
25122525
.widget_holder(),
25132526
Separator::new(SeparatorType::Related).widget_holder(),
25142527
NumberInput::new(opacity)
25152528
.label("Opacity")
25162529
.unit("%")
2517-
.display_decimal_places(2)
2530+
.display_decimal_places(0)
25182531
.disabled(disabled)
25192532
.min(0.)
25202533
.max(100.)
@@ -2529,15 +2542,71 @@ impl DocumentMessageHandler {
25292542
}
25302543
})
25312544
.on_commit(|_| DocumentMessage::AddTransaction.into())
2545+
.max_width(100)
2546+
.tooltip("Opacity")
25322547
.widget_holder(),
2533-
//
2534-
Separator::new(SeparatorType::Unrelated).widget_holder(),
2535-
//
2536-
IconButton::new("NewLayer", 24)
2537-
.tooltip("New Layer")
2538-
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
2539-
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
2548+
];
2549+
let layers_panel_control_bar_left = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
2550+
2551+
let widgets = vec![
2552+
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
2553+
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
2554+
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
2555+
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
2556+
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
2557+
.disabled(!has_selection)
25402558
.widget_holder(),
2559+
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
2560+
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
2561+
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
2562+
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
2563+
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
2564+
.disabled(!has_selection)
2565+
.widget_holder(),
2566+
];
2567+
let layers_panel_control_bar_right = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
2568+
2569+
responses.add(LayoutMessage::SendLayout {
2570+
layout: Layout::WidgetLayout(layers_panel_control_bar_left),
2571+
layout_target: LayoutTarget::LayersPanelControlLeftBar,
2572+
});
2573+
responses.add(LayoutMessage::SendLayout {
2574+
layout: Layout::WidgetLayout(layers_panel_control_bar_right),
2575+
layout_target: LayoutTarget::LayersPanelControlRightBar,
2576+
});
2577+
}
2578+
2579+
pub fn update_layers_panel_bottom_bar_widgets(&self, responses: &mut VecDeque<Message>) {
2580+
let selected_nodes = self.network_interface.selected_nodes();
2581+
let mut selected_layers = selected_nodes.selected_layers(self.metadata());
2582+
let selected_layer = selected_layers.next();
2583+
let has_selection = selected_layer.is_some();
2584+
let has_multiple_selection = selected_layers.next().is_some();
2585+
2586+
let widgets = vec![
2587+
PopoverButton::new()
2588+
.icon(Some("Node".to_string()))
2589+
.menu_direction(Some(MenuDirection::Top))
2590+
.tooltip("Add an operation to the end of this layer's chain of nodes")
2591+
.disabled(!has_selection || has_multiple_selection)
2592+
.popover_layout({
2593+
let node_chooser = NodeCatalog::new()
2594+
.on_update(move |node_type| {
2595+
if let Some(layer) = selected_layer {
2596+
NodeGraphMessage::CreateNodeInLayerWithTransaction {
2597+
node_type: node_type.clone(),
2598+
layer: LayerNodeIdentifier::new_unchecked(layer.to_node()),
2599+
}
2600+
.into()
2601+
} else {
2602+
Message::NoOp
2603+
}
2604+
})
2605+
.widget_holder();
2606+
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
2607+
})
2608+
.widget_holder(),
2609+
Separator::new(SeparatorType::Unrelated).widget_holder(),
25412610
IconButton::new("Folder", 24)
25422611
.tooltip("Group Selected")
25432612
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
@@ -2547,35 +2616,23 @@ impl DocumentMessageHandler {
25472616
})
25482617
.disabled(!has_selection)
25492618
.widget_holder(),
2619+
IconButton::new("NewLayer", 24)
2620+
.tooltip("New Layer")
2621+
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
2622+
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
2623+
.widget_holder(),
25502624
IconButton::new("Trash", 24)
25512625
.tooltip("Delete Selected")
25522626
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
25532627
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
25542628
.disabled(!has_selection)
25552629
.widget_holder(),
2556-
//
2557-
Separator::new(SeparatorType::Unrelated).widget_holder(),
2558-
//
2559-
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
2560-
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
2561-
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
2562-
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
2563-
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
2564-
.disabled(!has_selection)
2565-
.widget_holder(),
2566-
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
2567-
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
2568-
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
2569-
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
2570-
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
2571-
.disabled(!has_selection)
2572-
.widget_holder(),
25732630
];
2574-
let layers_panel_control_bar = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
2631+
let layers_panel_bottom_bar = WidgetLayout::new(vec![LayoutGroup::Row { widgets }]);
25752632

25762633
responses.add(LayoutMessage::SendLayout {
2577-
layout: Layout::WidgetLayout(layers_panel_control_bar),
2578-
layout_target: LayoutTarget::LayersPanelControlBar,
2634+
layout: Layout::WidgetLayout(layers_panel_bottom_bar),
2635+
layout_target: LayoutTarget::LayersPanelBottomBar,
25792636
});
25802637
}
25812638

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,12 +2009,38 @@ impl NodeGraphMessageHandler {
20092009
}
20102010

20112011
// Next, we decide what to display based on the number of layers and nodes selected
2012-
match layers.len() {
2012+
match *layers.as_slice() {
20132013
// If no layers are selected, show properties for all selected nodes
2014-
0 => {
2014+
[] => {
20152015
let selected_nodes = nodes.iter().map(|node_id| node_properties::generate_node_properties(*node_id, context)).collect::<Vec<_>>();
20162016
if !selected_nodes.is_empty() {
2017-
return selected_nodes;
2017+
let mut properties = Vec::new();
2018+
2019+
if let [node_id] = *nodes.as_slice() {
2020+
properties.push(LayoutGroup::Row {
2021+
widgets: vec![
2022+
Separator::new(SeparatorType::Related).widget_holder(),
2023+
IconLabel::new("Node").tooltip("Name of the selected node").widget_holder(),
2024+
Separator::new(SeparatorType::Related).widget_holder(),
2025+
TextInput::new(context.network_interface.display_name(&node_id, context.selection_network_path))
2026+
.tooltip("Name of the selected node")
2027+
.on_update(move |text_input| {
2028+
NodeGraphMessage::SetDisplayName {
2029+
node_id,
2030+
alias: text_input.value.clone(),
2031+
skip_adding_history_step: false,
2032+
}
2033+
.into()
2034+
})
2035+
.widget_holder(),
2036+
Separator::new(SeparatorType::Related).widget_holder(),
2037+
],
2038+
});
2039+
}
2040+
2041+
properties.extend(selected_nodes);
2042+
2043+
return properties;
20182044
}
20192045

20202046
// TODO: Display properties for encapsulating node when no nodes are selected in a nested network
@@ -2056,8 +2082,7 @@ impl NodeGraphMessageHandler {
20562082
properties
20572083
}
20582084
// If one layer is selected, filter out all selected nodes that are not upstream of it. If there are no nodes left, show properties for the layer. Otherwise, show nothing.
2059-
1 => {
2060-
let layer = layers[0];
2085+
[layer] => {
20612086
let nodes_not_upstream_of_layer = nodes.into_iter().filter(|&selected_node_id| {
20622087
!context
20632088
.network_interface

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,7 @@ impl PortfolioMessageHandler {
14311431
self.document_ids.push_back(document_id);
14321432
}
14331433
new_document.update_layers_panel_control_bar_widgets(responses);
1434+
new_document.update_layers_panel_bottom_bar_widgets(responses);
14341435

14351436
self.documents.insert(document_id, new_document);
14361437

Lines changed: 3 additions & 0 deletions
Loading

frontend/src/components/floating-menus/ColorPicker.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
44
import type { Editor } from "@graphite/editor";
55
import type { HSV, RGB, FillChoice } from "@graphite/messages";
6+
import type { MenuDirection } from "@graphite/messages";
67
import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages";
78
import { clamp } from "@graphite/utility-functions/math";
89
9-
import FloatingMenu, { type MenuDirection } from "@graphite/components/layout/FloatingMenu.svelte";
10+
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
1011
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
1112
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
1213
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";

0 commit comments

Comments
 (0)