Restyle and refactor shortcut labels to send hints bar and welcome screen layouts from Rust (#3447)

* Restyle UserInputLabel and refactor its usages to have all input its data sent from Rust

* Replace the welcome screen quick buttons with ones sent by backend

* Add the ShortcutLabel widget to the backend

* Replace hints bar with a backend-controlled layout; show mouse icons in place of mouse labels
This commit is contained in:
Keavon Chambers
2025-12-04 01:04:14 -08:00
committed by GitHub
parent 6ed42d06bb
commit 810ce40e9b
78 changed files with 981 additions and 997 deletions
@@ -232,7 +232,7 @@ impl<T: TableRowLayout> TableRowLayout for Vec<T> {
rows.insert(0, column_headings(&["", "element"]));
vec![LayoutGroup::Table { rows }]
vec![LayoutGroup::Table { rows, unstyled: false }]
}
}
@@ -274,7 +274,7 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
rows.insert(0, column_headings(&["", "element", "transform", "alpha_blending", "source_node_id"]));
vec![LayoutGroup::Table { rows }]
vec![LayoutGroup::Table { rows, unstyled: false }]
}
}
@@ -490,7 +490,7 @@ impl TableRowLayout for Vector {
}
}
vec![LayoutGroup::Row { widgets: table_tabs }, LayoutGroup::Table { rows: table_rows }]
vec![LayoutGroup::Row { widgets: table_tabs }, LayoutGroup::Table { rows: table_rows, unstyled: false }]
}
}
@@ -7,7 +7,7 @@ use super::utility_types::network_interface::{self, NodeNetworkInterface, Transa
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
use crate::application::{GRAPHITE_GIT_COMMIT_HASH, generate_uuid};
use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME, FILE_EXTENSION, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
@@ -2212,20 +2212,20 @@ impl DocumentMessageHandler {
let mut widgets = vec![
IconButton::new("PlaybackToStart", 24)
.tooltip_label("Restart Animation")
.shortcut_keys(action_keys!(AnimationMessageDiscriminant::RestartAnimation))
.tooltip_shortcut(action_shortcut!(AnimationMessageDiscriminant::RestartAnimation))
.on_update(|_| AnimationMessage::RestartAnimation.into())
.disabled(time == Duration::ZERO)
.widget_holder(),
IconButton::new(if animation_is_playing { "PlaybackPause" } else { "PlaybackPlay" }, 24)
.tooltip_label(if animation_is_playing { "Pause Animation" } else { "Play Animation" })
.shortcut_keys(action_keys!(AnimationMessageDiscriminant::ToggleLivePreview))
.tooltip_shortcut(action_shortcut!(AnimationMessageDiscriminant::ToggleLivePreview))
.on_update(|_| AnimationMessage::ToggleLivePreview.into())
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
CheckboxInput::new(self.overlays_visibility_settings.all)
.icon("Overlays")
.tooltip_label("Overlays")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
.on_update(|optional_input: &CheckboxInput| {
DocumentMessage::SetOverlaysVisibility {
visible: optional_input.checked,
@@ -2474,7 +2474,7 @@ impl DocumentMessageHandler {
CheckboxInput::new(snapping_state.snapping_enabled)
.icon("Snapping")
.tooltip_label("Snapping")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSnapping))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSnapping))
.on_update(move |optional_input: &CheckboxInput| {
DocumentMessage::SetSnapping {
closure: Some(|snapping_state| &mut snapping_state.snapping_enabled),
@@ -2544,7 +2544,7 @@ impl DocumentMessageHandler {
CheckboxInput::new(self.snapping_state.grid_snapping)
.icon("Grid")
.tooltip_label("Grid")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleGridVisibility))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleGridVisibility))
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
.widget_holder(),
PopoverButton::new()
@@ -2626,7 +2626,7 @@ impl DocumentMessageHandler {
.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_label(if self.graph_view_overlay_open { "Hide Node Graph" } else { "Show Node Graph" })
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
.widget_holder(),
]);
@@ -2778,14 +2778,14 @@ impl DocumentMessageHandler {
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedLocked))
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.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_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
.disabled(!has_selection)
.widget_holder(),
@@ -2850,7 +2850,7 @@ impl DocumentMessageHandler {
Separator::new(SeparatorType::Unrelated).widget_holder(),
IconButton::new("Folder", 24)
.tooltip_label("Group Selected")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
.on_update(|_| {
let group_folder_type = GroupFolderType::Layer;
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
@@ -2859,12 +2859,12 @@ impl DocumentMessageHandler {
.widget_holder(),
IconButton::new("NewLayer", 24)
.tooltip_label("New Layer")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
.widget_holder(),
IconButton::new("Trash", 24)
.tooltip_label("Delete Selected")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
.disabled(!has_selection)
.widget_holder(),
@@ -3148,17 +3148,17 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
let mut list = vec![
IconButton::new("ZoomIn", 24)
.tooltip_label("Zoom In")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomIncrease))
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
.widget_holder(),
IconButton::new("ZoomOut", 24)
.tooltip_label("Zoom Out")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomDecrease))
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
.widget_holder(),
IconButton::new("ZoomReset", 24)
.tooltip_label("Reset Tilt and Zoom to 100%")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
.disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
.widget_holder(),
@@ -3168,7 +3168,7 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
IconButton::new("Reverse", 24)
.tooltip_label("Unflip Canvas")
.tooltip_description("Flip the canvas back to its standard orientation.")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasFlip))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasFlip))
.on_update(|_| NavigationMessage::CanvasFlip.into())
.widget_holder(),
);
@@ -77,9 +77,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
NavigationMessage::BeginCanvasPan => {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
responses.add(FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
});
HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]).send_layout(responses);
self.mouse_position = ipp.mouse.position;
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
@@ -96,12 +94,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
responses.add(NavigationMessage::BeginCanvasPan);
} else {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
responses.add(FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
]),
});
HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
])
.send_layout(responses);
self.navigation_operation = NavigationOperation::Tilt {
tilt_original_for_abort: ptz.tilt(),
@@ -119,12 +116,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
};
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
responses.add(FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Shift], "Increments")]),
]),
});
HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Shift], "Increments")]),
])
.send_layout(responses);
self.navigation_operation = NavigationOperation::Zoom {
zoom_raw_not_snapped: ptz.zoom(),
@@ -1,8 +1,7 @@
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
use super::{document_node_definitions, node_properties};
use crate::consts::GRID_SIZE;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
@@ -2108,7 +2107,7 @@ impl NodeGraphMessageHandler {
.icon(Some("Node".to_string()))
.tooltip_label("New Node")
.tooltip_description("To add a node at the pointer location, perform the shortcut in an open area of the graph.")
.tooltip_shortcut(Key::MouseRight.to_string())
.tooltip_shortcut(action_shortcut_manual!(Key::MouseRight))
.popover_layout({
// Showing only compatible types
let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) {
@@ -2154,7 +2153,7 @@ impl NodeGraphMessageHandler {
//
IconButton::new("Folder", 24)
.tooltip_label("Group Selected")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
.on_update(|_| {
let group_folder_type = GroupFolderType::Layer;
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
@@ -2163,12 +2162,12 @@ impl NodeGraphMessageHandler {
.widget_holder(),
IconButton::new("NewLayer", 24)
.tooltip_label("New Layer")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
.widget_holder(),
IconButton::new("Trash", 24)
.tooltip_label("Delete Selected")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
.disabled(!has_selection)
.widget_holder(),
@@ -2178,14 +2177,14 @@ impl NodeGraphMessageHandler {
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
.shortcut_keys(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
.disabled(!has_selection || !selection_includes_layers)
.widget_holder(),
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
.shortcut_keys(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
.tooltip_shortcut(action_shortcut!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
.disabled(!has_selection)
.widget_holder(),
@@ -2225,7 +2224,7 @@ impl NodeGraphMessageHandler {
.icon(Some("FrameAll".to_string()))
.tooltip_label("Preview")
.tooltip_description("Temporarily set the graph output to the selected node or layer. Perform the shortcut on a node or layer for quick access.")
.tooltip_shortcut(KeysGroup(vec![Key::Alt, Key::MouseLeft]).to_string())
.tooltip_shortcut(action_shortcut_manual!(Key::Alt, Key::MouseLeft))
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
.widget_holder();
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
@@ -2284,7 +2283,7 @@ impl NodeGraphMessageHandler {
.icon(Some("GraphViewOpen".into()))
.hover_icon(Some("GraphViewClosed".into()))
.tooltip_label("Hide Node Graph")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
.widget_holder(),
]);
@@ -2720,8 +2719,7 @@ impl NodeGraphMessageHandler {
// Cancel the ongoing action
if wiring || dragging_nodes || dragging_box_selection {
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]).send_layout(responses);
return;
}
@@ -2749,7 +2747,7 @@ impl NodeGraphMessageHandler {
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDouble, "Enter Node Subgraph")]),
HintGroup(vec![HintInfo::keys_and_mouse([Key::Alt], MouseMotion::Lmb, "Preview Node Output")]),
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
}
@@ -452,8 +452,7 @@ impl TransformOperation {
}
hint_groups.push(HintGroup(typing_hints));
let hint_data = HintData(hint_groups);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
HintData(hint_groups).send_layout(responses);
}
pub fn is_constraint_to_axis(&self) -> bool {