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
@@ -7,7 +7,6 @@ use crate::messages::portfolio::document::node_graph::utility_types::{
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData;
use glam::IVec2;
use graph_craft::document::NodeId;
use graphene_std::raster::Image;
@@ -257,10 +256,6 @@ pub enum FrontendMessage {
UpdateGraphFadeArtwork {
percentage: f64,
},
UpdateInputHints {
#[serde(rename = "hintData")]
hint_data: HintData,
},
UpdateLayersPanelControlBarLeftLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
@@ -335,6 +330,16 @@ pub enum FrontendMessage {
#[serde(rename = "wirePath")]
wire_path: Option<WirePath>,
},
UpdateWelcomeScreenButtonsLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
diff: Vec<WidgetDiff>,
},
UpdateStatusBarHintsLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
diff: Vec<WidgetDiff>,
},
UpdateWorkingColorsLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
@@ -4,10 +4,18 @@ use bitflags::bitflags;
use std::fmt::{self, Display, Formatter};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
// ===========
// StorageType
// ===========
// TODO: Increase size of type
/// Edit this to specify the storage type used.
pub type StorageType = u128;
// =========
// KeyStates
// =========
// Base-2 logarithm of the storage type used to represents how many bits you need to fully address every bit in that storage type
const STORAGE_SIZE: u32 = (std::mem::size_of::<StorageType>() * 8).trailing_zeros();
const STORAGE_SIZE_BITS: usize = 1 << STORAGE_SIZE;
@@ -23,11 +31,19 @@ pub fn all_required_modifiers_pressed(keyboard_state: &KeyStates, modifiers: &Ke
all_modifiers_without_pressed_modifiers.is_empty()
}
// ===========
// KeyPosition
// ===========
pub enum KeyPosition {
Pressed,
Released,
}
// ============
// ModifierKeys
// ============
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[repr(transparent)]
@@ -40,6 +56,10 @@ bitflags! {
}
}
// ===
// Key
// ===
// Currently this is mostly based on the JS `KeyboardEvent.code` list: <https://www.w3.org/TR/uievents-code/>
// But in the future, especially once users can customize keyboard mappings, we should deviate more from this so we have actual symbols
// like `+` (which doesn't exist because it's the shifted version of `=` on the US keyboard, after which these scan codes are named).
@@ -198,14 +218,19 @@ pub enum Key {
// Other keys that aren't part of the W3C spec
//
/// "Cmd" on Mac (not present on other platforms)
/// "Cmd" on Mac (not present on other platforms).
Command,
/// "Ctrl" on Windows/Linux, "Cmd" on Mac
/// "Ctrl" on Windows/Linux, "Cmd" on Mac.
Accel,
/// Left mouse button click (LMB).
MouseLeft,
/// Right mouse button click (RMB).
MouseRight,
/// Middle mouse button click (MMB).
MouseMiddle,
/// Mouse backward navigation button (typically on the side of the mouse).
MouseBack,
/// Mouse forward navigation button (typically on the side of the mouse).
MouseForward,
// Fake keys for displaying special labels in the UI
@@ -225,11 +250,11 @@ impl fmt::Display for Key {
// Writing system keys
const DIGIT_PREFIX: &str = "Digit";
if key_name.len() == DIGIT_PREFIX.len() + 1 && &key_name[0..DIGIT_PREFIX.len()] == "Digit" {
if key_name.len() == DIGIT_PREFIX.len() + 1 && &key_name[0..DIGIT_PREFIX.len()] == DIGIT_PREFIX {
return write!(f, "{}", key_name.chars().skip(DIGIT_PREFIX.len()).collect::<String>());
}
const KEY_PREFIX: &str = "Key";
if key_name.len() == KEY_PREFIX.len() + 1 && &key_name[0..KEY_PREFIX.len()] == "Key" {
if key_name.len() == KEY_PREFIX.len() + 1 && &key_name[0..KEY_PREFIX.len()] == KEY_PREFIX {
return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>());
}
@@ -313,26 +338,12 @@ impl fmt::Display for Key {
}
}
impl From<Key> for LayoutKey {
fn from(key: Key) -> Self {
Self { key, label: key.to_string() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LayoutKey {
key: Key,
label: String,
}
impl LayoutKey {
pub fn key(&self) -> Key {
self.key
}
}
pub const NUMBER_OF_KEYS: usize = Key::_KeysVariantCount as usize - 1;
// =========
// KeysGroup
// =========
/// Only `Key`s that exist on a physical keyboard should be used.
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct KeysGroup(pub Vec<Key>);
@@ -365,21 +376,25 @@ impl fmt::Display for KeysGroup {
}
}
impl From<KeysGroup> for String {
fn from(keys: KeysGroup) -> Self {
let layout_keys: LayoutKeysGroup = keys.into();
serde_json::to_string(&layout_keys).expect("Failed to serialize KeysGroup")
// ==========
// LabeledKey
// ==========
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LabeledKey {
key: Key,
label: String,
}
impl LabeledKey {
pub fn key(&self) -> Key {
self.key
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LayoutKeysGroup(pub Vec<LayoutKey>);
impl From<KeysGroup> for LayoutKeysGroup {
fn from(keys_group: KeysGroup) -> Self {
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
}
}
// ===========
// MouseMotion
// ===========
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum MouseMotion {
@@ -397,6 +412,45 @@ pub enum MouseMotion {
MmbDrag,
}
// =======================
// LabeledKeyOrMouseMotion
// =======================
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
#[serde(untagged)]
pub enum LabeledKeyOrMouseMotion {
Key(LabeledKey),
MouseMotion(MouseMotion),
}
impl From<Key> for LabeledKeyOrMouseMotion {
fn from(key: Key) -> Self {
match key {
Key::MouseLeft => Self::MouseMotion(MouseMotion::Lmb),
Key::MouseRight => Self::MouseMotion(MouseMotion::Rmb),
Key::MouseMiddle => Self::MouseMotion(MouseMotion::Mmb),
_ => Self::Key(LabeledKey { key, label: key.to_string() }),
}
}
}
// ===============
// LabeledShortcut
// ===============
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LabeledShortcut(pub Vec<LabeledKeyOrMouseMotion>);
impl From<KeysGroup> for LabeledShortcut {
fn from(keys_group: KeysGroup) -> Self {
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
}
}
// =========
// BitVector
// =========
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BitVector<const LENGTH: usize>([StorageType; LENGTH]);
@@ -117,14 +117,23 @@ macro_rules! mapping {
}};
}
/// Constructs an `ActionKeys` macro with a certain `Action` variant, conveniently wrapped in `Some()`.
macro_rules! action_keys {
/// Constructs an `ActionShortcut` macro with a certain `Action` variant, conveniently wrapped in `Some()`.
macro_rules! action_shortcut {
($action:expr_2021) => {
Some(crate::messages::input_mapper::utility_types::misc::ActionKeys::Action($action.into()))
Some(crate::messages::input_mapper::utility_types::misc::ActionShortcut::Action($action.into()))
};
}
pub(crate) use action_keys;
macro_rules! action_shortcut_manual {
($($keys:expr),*) => {
Some(crate::messages::input_mapper::utility_types::misc::ActionShortcut::Shortcut(
crate::messages::input_mapper::utility_types::input_keyboard::LabeledShortcut(vec![$($keys.into()),*]).into(),
))
};
}
pub(crate) use action_shortcut;
pub(crate) use action_shortcut_manual;
pub(crate) use entry;
pub(crate) use mapping;
pub(crate) use modifiers;
@@ -1,4 +1,4 @@
use super::input_keyboard::{KeysGroup, LayoutKeysGroup, all_required_modifiers_pressed};
use super::input_keyboard::{KeysGroup, LabeledShortcut, all_required_modifiers_pressed};
use crate::messages::input_mapper::key_mapping::MappingVariant;
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
use crate::messages::input_mapper::utility_types::input_mouse::NUMBER_OF_MOUSE_BUTTONS;
@@ -128,28 +128,24 @@ pub struct MappingEntry {
}
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ActionKeys {
pub enum ActionShortcut {
Action(MessageDiscriminant),
#[serde(rename = "keys")]
Keys(LayoutKeysGroup),
#[serde(rename = "shortcut")]
Shortcut(LabeledShortcut),
}
impl ActionKeys {
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) -> String {
impl ActionShortcut {
pub fn realize_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
match self {
Self::Action(action) => {
if let Some(keys) = action_input_mapping(action) {
let description = keys.to_string();
*self = Self::Keys(keys.into());
description
*self = Self::Shortcut(keys.into());
} else {
*self = Self::Keys(KeysGroup::default().into());
String::new()
*self = Self::Shortcut(KeysGroup::default().into());
}
}
Self::Keys(keys) => {
warn!("Calling `.to_keys()` on a `ActionKeys::Keys` is a mistake/bug. Keys are: {keys:?}.");
String::new()
Self::Shortcut(shortcut) => {
warn!("Calling `.to_keys()` on a `ActionShortcut::Shortcut` is a mistake/bug. Shortcut is: {shortcut:?}.");
}
}
}
@@ -12,6 +12,9 @@ pub enum LayoutMessage {
layout: Layout,
layout_target: LayoutTarget,
},
DestroyLayout {
layout_target: LayoutTarget,
},
WidgetValueCommit {
layout_target: LayoutTarget,
widget_id: WidgetId,
@@ -39,6 +39,11 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
LayoutMessage::SendLayout { layout, layout_target } => {
self.diff_and_send_layout_to_frontend(layout_target, layout, responses, action_input_mapping);
}
LayoutMessage::DestroyLayout { layout_target } => {
if let Some(layout) = self.layouts.get_mut(layout_target as usize) {
*layout = Default::default();
}
}
LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
}
@@ -78,7 +83,7 @@ impl LayoutMessageHandler {
LayoutGroup::Section { layout, .. } => {
stack.extend(layout.iter().enumerate().map(|(index, val)| ([widget_path.as_slice(), &[index]].concat(), val)));
}
LayoutGroup::Table { rows } => {
LayoutGroup::Table { rows, .. } => {
for (row_index, row) in rows.iter().enumerate() {
for (cell_index, cell) in row.iter().enumerate() {
// Return if this is the correct ID
@@ -310,6 +315,7 @@ impl LayoutMessageHandler {
responses.add(callback_message);
}
Widget::ImageLabel(_) => {}
Widget::ShortcutLabel(_) => {}
Widget::IconLabel(_) => {}
Widget::NodeCatalog(node_type_input) => match action {
WidgetValueAction::Commit => {
@@ -509,8 +515,10 @@ impl LayoutMessageHandler {
LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout { layout_target, diff },
LayoutTarget::NodeGraphControlBar => FrontendMessage::UpdateNodeGraphControlBarLayout { layout_target, diff },
LayoutTarget::PropertiesPanel => FrontendMessage::UpdatePropertiesPanelLayout { layout_target, diff },
LayoutTarget::StatusBarHints => FrontendMessage::UpdateStatusBarHintsLayout { layout_target, diff },
LayoutTarget::ToolOptions => FrontendMessage::UpdateToolOptionsLayout { layout_target, diff },
LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout { layout_target, diff },
LayoutTarget::WelcomeScreenButtons => FrontendMessage::UpdateWelcomeScreenButtonsLayout { layout_target, diff },
LayoutTarget::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout { layout_target, diff },
LayoutTarget::LayoutTargetLength => panic!("`LayoutTargetLength` is not a valid Layout Target and is used for array indexing"),
@@ -3,7 +3,6 @@ use super::widgets::input_widgets::*;
use super::widgets::label_widgets::*;
use crate::application::generate_uuid;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::prelude::*;
use std::sync::Arc;
@@ -20,6 +19,8 @@ impl core::fmt::Display for WidgetId {
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
#[repr(u8)]
pub enum LayoutTarget {
/// The spreadsheet panel allows for the visualisation of data in the graph.
DataPanel,
/// Contains the action buttons at the bottom of the dialog. Must be shown with the `FrontendMessage::DisplayDialog` message.
DialogButtons,
/// Contains the contents of the dialog's primary column. Must be shown with the `FrontendMessage::DisplayDialog` message.
@@ -30,24 +31,26 @@ pub enum LayoutTarget {
DocumentBar,
/// Contains the dropdown for design / select / guide mode found on the top left of the canvas.
DocumentMode,
/// Controls for adding, grouping, and deleting layers at the bottom of the Layers panel.
LayersPanelBottomBar,
/// Blending options at the top of the Layers panel.
LayersPanelControlLeftBar,
/// Selected layer status (locked/hidden) at the top of the Layers panel.
LayersPanelControlRightBar,
/// Controls for adding, grouping, and deleting layers at the bottom of the Layers panel.
LayersPanelBottomBar,
/// The dropdown menu at the very top of the application: File, Edit, etc.
MenuBar,
/// Bar at the top of the node graph containing the location and the "Preview" and "Hide" buttons.
NodeGraphControlBar,
/// The body of the Properties panel containing many collapsable sections.
PropertiesPanel,
/// The spredsheet panel allows for the visualisation of data in the graph.
DataPanel,
/// The contextual input key/mouse combination shortcuts shown in the status bar at the bottom of the window.
StatusBarHints,
/// The bar directly above the canvas, left-aligned and to the right of the document mode dropdown.
ToolOptions,
/// The vertical buttons for all of the tools on the left of the canvas.
ToolShelf,
/// The quick access buttons found on the welcome screen, shown when no documents are open.
WelcomeScreenButtons,
/// The color swatch for the working colors and a flip and reset button found at the bottom of the tool shelf.
WorkingColors,
@@ -198,7 +201,7 @@ impl<'a> Iterator for WidgetIter<'a> {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Table { rows }) => {
Some(LayoutGroup::Table { rows, .. }) => {
self.table.extend(rows.iter().flatten().rev());
self.next()
}
@@ -248,7 +251,7 @@ impl<'a> Iterator for WidgetIterMut<'a> {
self.current_slice = Some(widgets);
self.next()
}
Some(LayoutGroup::Table { rows }) => {
Some(LayoutGroup::Table { rows, .. }) => {
self.table.extend(rows.iter_mut().flatten().rev());
self.next()
}
@@ -281,6 +284,7 @@ pub enum LayoutGroup {
Table {
#[serde(rename = "tableWidgets")]
rows: Vec<Vec<WidgetHolder>>,
unstyled: bool,
},
#[serde(rename = "section")]
Section {
@@ -332,7 +336,7 @@ impl LayoutGroup {
Widget::TextInput(x) => &mut x.tooltip_label,
Widget::TextLabel(x) => &mut x.tooltip_label,
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_label,
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::ShortcutLabel(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
};
if val.is_empty() {
val.clone_from(&label);
@@ -368,7 +372,7 @@ impl LayoutGroup {
Widget::TextInput(x) => &mut x.tooltip_description,
Widget::TextLabel(x) => &mut x.tooltip_description,
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_description,
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::ShortcutLabel(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
};
if val.is_empty() {
val.clone_from(&description);
@@ -506,7 +510,7 @@ impl WidgetHolder {
&& button1.icon == button2.icon
&& button1.tooltip_label == button2.tooltip_label
&& button1.tooltip_description == button2.tooltip_description
&& button1.shortcut_keys == button2.shortcut_keys
&& button1.tooltip_shortcut == button2.tooltip_shortcut
&& button1.popover_min_width == button2.popover_min_width
{
let mut new_widget_path = widget_path.to_vec();
@@ -564,6 +568,7 @@ pub enum Widget {
IconLabel(IconLabel),
ImageButton(ImageButton),
ImageLabel(ImageLabel),
ShortcutLabel(ShortcutLabel),
NodeCatalog(NodeCatalog),
NumberInput(NumberInput),
ParameterExposeButton(ParameterExposeButton),
@@ -607,30 +612,22 @@ pub enum DiffUpdate {
impl DiffUpdate {
/// Append the keyboard shortcut to the tooltip where applicable
pub fn apply_keyboard_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
let apply_shortcut_to_tooltip = |shortcut_keys: &mut ActionKeys, tooltip_shortcut: &mut String| {
let shortcut_text = shortcut_keys.to_keys(action_input_mapping);
if matches!(shortcut_keys, ActionKeys::Keys(_)) && !shortcut_text.is_empty() {
tooltip_shortcut.push_str(&shortcut_text);
}
};
// Go through each widget to convert `ActionKeys::Action` to `ActionKeys::Keys` and append the key combination to the widget tooltip
// Go through each widget to convert `ActionShortcut::Action` to `ActionShortcut::Shortcut` and append the key combination to the widget tooltip
let convert_tooltip = |widget_holder: &mut WidgetHolder| {
// Handle all the widgets that have tooltips
let mut shortcut_keys = match &mut widget_holder.widget {
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::ColorInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::DropdownInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::FontInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::IconButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::NumberInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::PopoverButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::TextButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
Widget::ImageButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
let tooltip_shortcut = match &mut widget_holder.widget {
Widget::BreadcrumbTrailButtons(widget) => widget.tooltip_shortcut.as_mut(),
Widget::CheckboxInput(widget) => widget.tooltip_shortcut.as_mut(),
Widget::ColorInput(widget) => widget.tooltip_shortcut.as_mut(),
Widget::DropdownInput(widget) => widget.tooltip_shortcut.as_mut(),
Widget::FontInput(widget) => widget.tooltip_shortcut.as_mut(),
Widget::IconButton(widget) => widget.tooltip_shortcut.as_mut(),
Widget::NumberInput(widget) => widget.tooltip_shortcut.as_mut(),
Widget::ParameterExposeButton(widget) => widget.tooltip_shortcut.as_mut(),
Widget::PopoverButton(widget) => widget.tooltip_shortcut.as_mut(),
Widget::TextButton(widget) => widget.tooltip_shortcut.as_mut(),
Widget::ImageButton(widget) => widget.tooltip_shortcut.as_mut(),
Widget::ShortcutLabel(widget) => widget.shortcut.as_mut(),
Widget::IconLabel(_)
| Widget::ImageLabel(_)
| Widget::CurveInput(_)
@@ -643,34 +640,32 @@ impl DiffUpdate {
| Widget::TextLabel(_)
| Widget::WorkingColorsInput(_) => None,
};
if let Some((tooltip_shortcut, Some(shortcut_keys))) = &mut shortcut_keys {
apply_shortcut_to_tooltip(shortcut_keys, tooltip_shortcut);
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
if let Some(tooltip_shortcut) = tooltip_shortcut {
tooltip_shortcut.realize_shortcut(action_input_mapping);
}
// Handle RadioInput separately because its tooltips are children of the widget
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
for radio_entry_data in &mut radio_input.entries {
if let RadioEntryData {
tooltip_shortcut,
shortcut_keys: Some(shortcut_keys),
..
} = radio_entry_data
{
apply_shortcut_to_tooltip(shortcut_keys, tooltip_shortcut);
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
if let Some(tooltip_shortcut) = radio_entry_data.tooltip_shortcut.as_mut() {
tooltip_shortcut.realize_shortcut(action_input_mapping);
}
}
}
};
// Recursively fill menu list entries with their realized shortcut keys specific to the current bindings and platform
let apply_action_keys_to_menu_lists = |entry_sections: &mut MenuListEntrySections| {
let apply_action_shortcut_to_menu_lists = |entry_sections: &mut MenuListEntrySections| {
struct RecursiveWrapper<'a>(&'a dyn Fn(&mut MenuListEntrySections, &RecursiveWrapper));
let recursive_wrapper = RecursiveWrapper(&|entry_sections: &mut MenuListEntrySections, recursive_wrapper| {
for entries in entry_sections {
for entry in entries {
// Convert the shortcut actions to keys for this menu entry
if let Some(shortcut_keys) = &mut entry.shortcut_keys {
shortcut_keys.to_keys(action_input_mapping);
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
if let Some(tooltip_shortcut) = &mut entry.tooltip_shortcut {
tooltip_shortcut.realize_shortcut(action_input_mapping);
}
// Recursively call this inner closure on the menu's children
@@ -683,8 +678,8 @@ impl DiffUpdate {
// Apply shortcut conversions to all widgets that have menu lists
let convert_menu_lists = |widget_holder: &mut WidgetHolder| match &mut widget_holder.widget {
Widget::DropdownInput(dropdown_input) => apply_action_keys_to_menu_lists(&mut dropdown_input.entries),
Widget::TextButton(text_button) => apply_action_keys_to_menu_lists(&mut text_button.menu_list_children),
Widget::DropdownInput(dropdown_input) => apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries),
Widget::TextButton(text_button) => apply_action_shortcut_to_menu_lists(&mut text_button.menu_list_children),
_ => {}
};
@@ -1,4 +1,4 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
@@ -29,10 +29,7 @@ pub struct IconButton {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -63,10 +60,7 @@ pub struct PopoverButton {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "popoverLayout")]
pub popover_layout: SubLayout,
@@ -104,10 +98,7 @@ pub struct ParameterExposeButton {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -148,10 +139,7 @@ pub struct TextButton {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "menuListChildren")]
pub menu_list_children: MenuListEntrySections,
@@ -183,10 +171,7 @@ pub struct ImageButton {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -228,10 +213,7 @@ pub struct ColorInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -258,10 +240,7 @@ pub struct BreadcrumbTrailButtons {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -1,4 +1,4 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use derivative::*;
use graphene_std::Color;
@@ -23,15 +23,12 @@ pub struct CheckboxInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "forLabel")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub for_label: CheckboxId,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
// Callbacks
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
@@ -51,7 +48,6 @@ impl Default for CheckboxInput {
tooltip_label: Default::default(),
tooltip_description: Default::default(),
tooltip_shortcut: Default::default(),
shortcut_keys: Default::default(),
for_label: CheckboxId::new(),
on_update: Default::default(),
on_commit: Default::default(),
@@ -106,10 +102,7 @@ pub struct DropdownInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Styling
#[serde(rename = "minWidth")]
@@ -146,14 +139,7 @@ pub struct MenuListEntry {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
// TODO: Make this serde(skip)
#[serde(rename = "shortcutKeys")]
pub shortcut_keys: Option<ActionKeys>,
#[serde(rename = "shortcutRequiresLock")]
pub shortcut_requires_lock: bool,
pub tooltip_shortcut: Option<ActionShortcut>,
pub children: MenuListEntrySections,
@@ -190,10 +176,7 @@ pub struct FontInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -218,10 +201,7 @@ pub struct NumberInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Disabled
pub disabled: bool,
@@ -394,10 +374,7 @@ pub struct RadioEntryData {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
#[serde(skip)]
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -436,7 +413,7 @@ pub struct TextAreaInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -467,7 +444,7 @@ pub struct TextInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
pub centered: bool,
@@ -502,7 +479,7 @@ pub struct CurveInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -529,7 +506,7 @@ pub struct ReferencePointInput {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
// Callbacks
#[serde(skip)]
@@ -1,4 +1,5 @@
use super::input_widgets::CheckboxId;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use derivative::*;
use graphite_proc_macros::WidgetBuilder;
@@ -16,7 +17,7 @@ pub struct IconLabel {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
@@ -74,7 +75,7 @@ pub struct TextLabel {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
#[serde(rename = "forCheckbox")]
#[derivative(PartialEq = "ignore")]
@@ -102,7 +103,13 @@ pub struct ImageLabel {
pub tooltip_description: String,
#[serde(rename = "tooltipShortcut")]
pub tooltip_shortcut: String,
pub tooltip_shortcut: Option<ActionShortcut>,
}
// TODO: Add UserInputLabel
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq)]
pub struct ShortcutLabel {
// This is wrapped in an Option to satisfy the requirement that widgets implement Default
#[widget_builder(constructor)]
pub shortcut: Option<ActionShortcut>,
}
@@ -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 {
@@ -1,5 +1,5 @@
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
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::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
@@ -68,7 +68,7 @@ impl LayoutHolder for MenuBarMessageHandler {
let preferences = MenuListEntry::new("Preferences…")
.label("Preferences…")
.icon("Settings")
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog))
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestPreferencesDialog))
.on_commit(|_| DialogMessage::RequestPreferencesDialog.into());
let menu_bar_buttons = vec![
@@ -89,21 +89,21 @@ impl LayoutHolder for MenuBarMessageHandler {
vec![
MenuListEntry::new("Hide Graphite")
.label("Hide Graphite")
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::Hide))
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Hide))
.on_commit(|_| AppWindowMessage::Hide.into()),
MenuListEntry::new("Hide Others")
.label("Hide Others")
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::HideOthers))
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::HideOthers))
.on_commit(|_| AppWindowMessage::HideOthers.into()),
MenuListEntry::new("Show All")
.label("Show All")
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::ShowAll))
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::ShowAll))
.on_commit(|_| AppWindowMessage::ShowAll.into()),
],
vec![
MenuListEntry::new("Quit Graphite")
.label("Quit Graphite")
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::Close))
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Close))
.on_commit(|_| AppWindowMessage::Close.into()),
],
])
@@ -117,11 +117,11 @@ impl LayoutHolder for MenuBarMessageHandler {
.label("New…")
.icon("File")
.on_commit(|_| DialogMessage::RequestNewDocumentDialog.into())
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestNewDocumentDialog)),
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestNewDocumentDialog)),
MenuListEntry::new("Open…")
.label("Open…")
.icon("Folder")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::OpenDocument))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::OpenDocument))
.on_commit(|_| PortfolioMessage::OpenDocument.into()),
MenuListEntry::new("Open Demo Artwork…")
.label("Open Demo Artwork…")
@@ -132,13 +132,13 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Close")
.label("Close")
.icon("Close")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation))
.on_commit(|_| PortfolioMessage::CloseActiveDocumentWithConfirmation.into())
.disabled(no_active_document),
MenuListEntry::new("Close All")
.label("Close All")
.icon("CloseAll")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::CloseAllDocumentsWithConfirmation))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::CloseAllDocumentsWithConfirmation))
.on_commit(|_| PortfolioMessage::CloseAllDocumentsWithConfirmation.into())
.disabled(no_active_document),
],
@@ -146,14 +146,14 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Save")
.label("Save")
.icon("Save")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SaveDocument))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SaveDocument))
.on_commit(|_| DocumentMessage::SaveDocument.into())
.disabled(no_active_document),
#[cfg(not(target_family = "wasm"))]
MenuListEntry::new("Save As…")
.label("Save As…")
.icon("Save")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SaveDocumentAs))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SaveDocumentAs))
.on_commit(|_| DocumentMessage::SaveDocumentAs.into())
.disabled(no_active_document),
],
@@ -161,12 +161,12 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Import…")
.label("Import…")
.icon("FileImport")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Import))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Import))
.on_commit(|_| PortfolioMessage::Import.into()),
MenuListEntry::new("Export…")
.label("Export…")
.icon("FileExport")
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestExportDialog))
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestExportDialog))
.on_commit(|_| DialogMessage::RequestExportDialog.into())
.disabled(no_active_document),
],
@@ -182,13 +182,13 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Undo")
.label("Undo")
.icon("HistoryUndo")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::Undo))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::Undo))
.on_commit(|_| DocumentMessage::Undo.into())
.disabled(no_active_document),
MenuListEntry::new("Redo")
.label("Redo")
.icon("HistoryRedo")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::Redo))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::Redo))
.on_commit(|_| DocumentMessage::Redo.into())
.disabled(no_active_document),
],
@@ -196,19 +196,19 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Cut")
.label("Cut")
.icon("Cut")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Cut))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Cut))
.on_commit(|_| PortfolioMessage::Cut { clipboard: Clipboard::Device }.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Copy")
.label("Copy")
.icon("Copy")
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Copy))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Copy))
.on_commit(|_| PortfolioMessage::Copy { clipboard: Clipboard::Device }.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Paste")
.label("Paste")
.icon("Paste")
.shortcut_keys(action_keys!(FrontendMessageDiscriminant::TriggerPaste))
.tooltip_shortcut(action_shortcut!(FrontendMessageDiscriminant::TriggerPaste))
.on_commit(|_| FrontendMessage::TriggerPaste.into())
.disabled(no_active_document),
],
@@ -216,13 +216,13 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Duplicate")
.label("Duplicate")
.icon("Copy")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DuplicateSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DuplicateSelectedLayers))
.on_commit(|_| DocumentMessage::DuplicateSelectedLayers.into())
.disabled(no_active_document || !has_selected_nodes),
MenuListEntry::new("Delete")
.label("Delete")
.icon("Trash")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
.on_commit(|_| DocumentMessage::DeleteSelectedLayers.into())
.disabled(no_active_document || !has_selected_nodes),
],
@@ -238,13 +238,12 @@ impl LayoutHolder for MenuBarMessageHandler {
TextButton::new("Layer")
.label("Layer")
.flush(true)
.disabled(no_active_document)
.menu_list_children(vec![
vec![
MenuListEntry::new("New")
.label("New")
.icon("NewLayer")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
.on_commit(|_| DocumentMessage::CreateEmptyFolder.into())
.disabled(no_active_document),
],
@@ -252,7 +251,7 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Group")
.label("Group")
.icon("Folder")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
.on_commit(|_| {
DocumentMessage::GroupSelectedLayers {
group_folder_type: GroupFolderType::Layer,
@@ -263,7 +262,7 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Ungroup")
.label("Ungroup")
.icon("FolderOpen")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::UngroupSelectedLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::UngroupSelectedLayers))
.on_commit(|_| DocumentMessage::UngroupSelectedLayers.into())
.disabled(no_active_document || !has_selected_layers),
],
@@ -271,13 +270,13 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Hide/Show")
.label("Hide/Show")
.icon("EyeHide")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
.on_commit(|_| DocumentMessage::ToggleSelectedVisibility.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Lock/Unlock")
.label("Lock/Unlock")
.icon("PadlockLocked")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedLocked))
.on_commit(|_| DocumentMessage::ToggleSelectedLocked.into())
.disabled(no_active_document || !has_selected_layers),
],
@@ -285,19 +284,19 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Grab")
.label("Grab")
.icon("TransformationGrab")
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginGrab))
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginGrab))
.on_commit(|_| TransformLayerMessage::BeginGrab.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Rotate")
.label("Rotate")
.icon("TransformationRotate")
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginRotate))
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginRotate))
.on_commit(|_| TransformLayerMessage::BeginRotate.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Scale")
.label("Scale")
.icon("TransformationScale")
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginScale))
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginScale))
.on_commit(|_| TransformLayerMessage::BeginScale.into())
.disabled(no_active_document || !has_selected_layers),
],
@@ -311,25 +310,25 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Raise To Front")
.label("Raise To Front")
.icon("Stack")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront))
.on_commit(|_| DocumentMessage::SelectedLayersRaiseToFront.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Raise")
.label("Raise")
.icon("StackRaise")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersRaise))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersRaise))
.on_commit(|_| DocumentMessage::SelectedLayersRaise.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Lower")
.label("Lower")
.icon("StackLower")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersLower))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersLower))
.on_commit(|_| DocumentMessage::SelectedLayersLower.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Lower to Back")
.label("Lower to Back")
.icon("StackBottom")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersLowerToBack))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersLowerToBack))
.on_commit(|_| DocumentMessage::SelectedLayersLowerToBack.into())
.disabled(no_active_document || !has_selected_layers),
],
@@ -508,25 +507,24 @@ impl LayoutHolder for MenuBarMessageHandler {
TextButton::new("Select")
.label("Select")
.flush(true)
.disabled(no_active_document)
.menu_list_children(vec![
vec![
MenuListEntry::new("Select All")
.label("Select All")
.icon("SelectAll")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectAllLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectAllLayers))
.on_commit(|_| DocumentMessage::SelectAllLayers.into())
.disabled(no_active_document),
MenuListEntry::new("Deselect All")
.label("Deselect All")
.icon("DeselectAll")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeselectAllLayers))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeselectAllLayers))
.on_commit(|_| DocumentMessage::DeselectAllLayers.into())
.disabled(no_active_document || !has_selected_nodes),
MenuListEntry::new("Select Parent")
.label("Select Parent")
.icon("SelectParent")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectParentLayer))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectParentLayer))
.on_commit(|_| DocumentMessage::SelectParentLayer.into())
.disabled(no_active_document || !has_selected_nodes),
],
@@ -534,13 +532,13 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Previous Selection")
.label("Previous Selection")
.icon("HistoryUndo")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectionStepBack))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectionStepBack))
.on_commit(|_| DocumentMessage::SelectionStepBack.into())
.disabled(!has_selection_history.0),
MenuListEntry::new("Next Selection")
.label("Next Selection")
.icon("HistoryRedo")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectionStepForward))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectionStepForward))
.on_commit(|_| DocumentMessage::SelectionStepForward.into())
.disabled(!has_selection_history.1),
],
@@ -549,19 +547,18 @@ impl LayoutHolder for MenuBarMessageHandler {
TextButton::new("View")
.label("View")
.flush(true)
.disabled(no_active_document)
.menu_list_children(vec![
vec![
MenuListEntry::new("Tilt")
.label("Tilt")
.icon("Tilt")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::BeginCanvasTilt))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::BeginCanvasTilt))
.on_commit(|_| NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: true }.into())
.disabled(no_active_document || node_graph_open),
MenuListEntry::new("Reset Tilt")
.label("Reset Tilt")
.icon("TiltReset")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasTiltSet))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasTiltSet))
.on_commit(|_| NavigationMessage::CanvasTiltSet { angle_radians: 0.into() }.into())
.disabled(no_active_document || node_graph_open || !self.canvas_tilted),
],
@@ -569,37 +566,37 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Zoom In")
.label("Zoom In")
.icon("ZoomIn")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomIncrease))
.on_commit(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
.disabled(no_active_document),
MenuListEntry::new("Zoom Out")
.label("Zoom Out")
.icon("ZoomOut")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomDecrease))
.on_commit(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
.disabled(no_active_document),
MenuListEntry::new("Zoom to Selection")
.label("Zoom to Selection")
.icon("FrameSelected")
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::FitViewportToSelection))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::FitViewportToSelection))
.on_commit(|_| NavigationMessage::FitViewportToSelection.into())
.disabled(no_active_document || !has_selected_layers),
MenuListEntry::new("Zoom to Fit")
.label("Zoom to Fit")
.icon("FrameAll")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasToFitAll))
.on_commit(|_| DocumentMessage::ZoomCanvasToFitAll.into())
.disabled(no_active_document),
MenuListEntry::new("Zoom to 100%")
.label("Zoom to 100%")
.icon("Zoom1x")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent))
.on_commit(|_| DocumentMessage::ZoomCanvasTo100Percent.into())
.disabled(no_active_document),
MenuListEntry::new("Zoom to 200%")
.label("Zoom to 200%")
.icon("Zoom2x")
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent))
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent))
.on_commit(|_| DocumentMessage::ZoomCanvasTo200Percent.into())
.disabled(no_active_document),
],
@@ -607,7 +604,7 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Flip")
.label("Flip")
.icon(if self.canvas_flipped { "CheckboxChecked" } else { "CheckboxUnchecked" })
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasFlip))
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasFlip))
.on_commit(|_| NavigationMessage::CanvasFlip.into())
.disabled(no_active_document || node_graph_open),
],
@@ -615,7 +612,7 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Rulers")
.label("Rulers")
.icon(if self.rulers_visible { "CheckboxChecked" } else { "CheckboxUnchecked" })
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::ToggleRulers))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleRulers))
.on_commit(|_| PortfolioMessage::ToggleRulers.into())
.disabled(no_active_document),
],
@@ -629,19 +626,19 @@ impl LayoutHolder for MenuBarMessageHandler {
MenuListEntry::new("Properties")
.label("Properties")
.icon(if self.properties_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::TogglePropertiesPanelOpen))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::TogglePropertiesPanelOpen))
.on_commit(|_| PortfolioMessage::TogglePropertiesPanelOpen.into()),
MenuListEntry::new("Layers")
.label("Layers")
.icon(if self.layers_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::ToggleLayersPanelOpen))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleLayersPanelOpen))
.on_commit(|_| PortfolioMessage::ToggleLayersPanelOpen.into()),
],
vec![
MenuListEntry::new("Data")
.label("Data")
.icon(if self.data_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::ToggleDataPanelOpen))
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::ToggleDataPanelOpen))
.on_commit(|_| PortfolioMessage::ToggleDataPanelOpen.into()),
],
])
@@ -702,7 +699,7 @@ impl LayoutHolder for MenuBarMessageHandler {
"CheckboxChecked".to_string()
}
}).unwrap_or_default())
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageOff))
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageOff))
.on_commit(|_| DebugMessage::MessageOff.into()),
MenuListEntry::new("Print Messages: Only Names")
.label("Print Messages: Only Names")
@@ -716,7 +713,7 @@ impl LayoutHolder for MenuBarMessageHandler {
"CheckboxChecked".to_string()
}
}).unwrap_or_default())
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageNames))
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageNames))
.on_commit(|_| DebugMessage::MessageNames.into()),
MenuListEntry::new("Print Messages: Full Contents")
.label("Print Messages: Full Contents")
@@ -730,7 +727,7 @@ impl LayoutHolder for MenuBarMessageHandler {
"CheckboxChecked".to_string()
}
}).unwrap_or_default())
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageContents))
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageContents))
.on_commit(|_| DebugMessage::MessageContents.into()),
],
vec![MenuListEntry::new("Trigger a Crash").label("Trigger a Crash").icon("Warning").on_commit(|_| panic!())],
@@ -109,6 +109,7 @@ pub enum PortfolioMessage {
parent_and_insert_index: Option<(LayerNodeIdentifier, usize)>,
},
PrevDocument,
RequestWelcomeScreenButtonsLayout,
SetActivePanel {
panel: PanelType,
},
@@ -7,6 +7,7 @@ use crate::messages::animation::TimingInformation;
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::{DocumentDetails, OpenDocument};
use crate::messages::input_mapper::utility_types::macros::action_shortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::DocumentMessageContext;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
@@ -20,7 +21,7 @@ use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed;
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
use crate::messages::tool::utility_types::{HintData, ToolType};
use crate::messages::viewport::ToPhysical;
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
use derivative::*;
@@ -218,8 +219,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayersPanel);
responses.add(DataPanelMessage::ClearLayout);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
HintData::clear_layout(responses);
}
for document_id in &self.document_ids {
@@ -243,8 +243,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayersPanel);
responses.add(DataPanelMessage::ClearLayout);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
HintData::clear_layout(responses);
}
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
@@ -882,6 +881,53 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(PortfolioMessage::SelectDocument { document_id: prev_id });
}
}
PortfolioMessage::RequestWelcomeScreenButtonsLayout => {
let donate = "https://graphite.rs/donate/";
let table = LayoutGroup::Table {
unstyled: true,
rows: vec![
vec![
TextButton::new("New Document")
.icon(Some("File".into()))
.flush(true)
.on_commit(|_| DialogMessage::RequestNewDocumentDialog.into())
.widget_holder(),
ShortcutLabel::new(action_shortcut!(DialogMessageDiscriminant::RequestNewDocumentDialog)).widget_holder(),
],
vec![
TextButton::new("Open Document")
.icon(Some("Folder".into()))
.flush(true)
.on_commit(|_| PortfolioMessage::OpenDocument.into())
.widget_holder(),
ShortcutLabel::new(action_shortcut!(PortfolioMessageDiscriminant::OpenDocument)).widget_holder(),
],
vec![
TextButton::new("Open Demo Artwork")
.icon(Some("Image".into()))
.flush(true)
.on_commit(|_| DialogMessage::RequestDemoArtworkDialog.into())
.widget_holder(),
],
vec![
TextButton::new("Support the Development Fund")
.icon(Some("Heart".into()))
.flush(true)
.on_commit(move |_| FrontendMessage::TriggerVisitLink { url: donate.to_string() }.into())
.widget_holder(),
],
],
};
responses.add(LayoutMessage::DestroyLayout {
layout_target: LayoutTarget::WelcomeScreenButtons,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![table])),
layout_target: LayoutTarget::WelcomeScreenButtons,
});
}
PortfolioMessage::SetActivePanel { panel } => {
self.active_panel = panel;
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
@@ -41,6 +41,7 @@ pub enum KeyboardPlatformLayout {
pub enum PanelType {
#[default]
Document,
Welcome,
Layers,
Properties,
DataPanel,
@@ -50,6 +51,7 @@ impl From<String> for PanelType {
fn from(value: String) -> Self {
match value.as_str() {
"Document" => PanelType::Document,
"Welcome" => PanelType::Welcome,
"Layers" => PanelType::Layers,
"Properties" => PanelType::Properties,
"Data" => PanelType::DataPanel,
@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayProvid
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use crate::messages::tool::transform_layer::transform_layer_message_handler::TransformLayerMessageContext;
use crate::messages::tool::utility_types::ToolType;
use crate::messages::tool::utility_types::{HintData, ToolType};
use crate::node_graph_executor::NodeGraphExecutor;
use graphene_std::raster::color::Color;
@@ -190,7 +190,7 @@ impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler
responses.add(OverlaysMessage::RemoveProvider { provider: ARTBOARD_OVERLAY_PROVIDER });
responses.add(FrontendMessage::UpdateInputHints { hint_data: Default::default() });
HintData::clear_layout(responses);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: Default::default() });
self.tool_is_active = false;
@@ -601,7 +601,7 @@ impl Fsm for ArtboardToolFsmState {
]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -464,7 +464,7 @@ impl Fsm for BrushToolFsmState {
BrushToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -138,7 +138,7 @@ impl Fsm for EyedropperToolFsmState {
}
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -158,7 +158,7 @@ impl Fsm for FillToolFsmState {
FillToolFsmState::Filling => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -346,7 +346,7 @@ impl Fsm for FreehandToolFsmState {
FreehandToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -530,7 +530,7 @@ impl Fsm for GradientToolFsmState {
]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -166,7 +166,7 @@ impl Fsm for NavigateToolFsmState {
]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -4,7 +4,7 @@ use crate::consts::{
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GRAY, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DEFAULT_STROKE_WIDTH, DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD,
DRILL_THROUGH_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE,
};
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::macros::action_shortcut_manual;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
@@ -270,7 +270,7 @@ impl LayoutHolder for PathTool {
.icon("Dot")
.tooltip_label("Point Editing Mode")
.tooltip_description("To multi-select modes, perform the shortcut shown.")
.tooltip_shortcut(KeysGroup(vec![Key::Shift, Key::MouseLeft]).to_string())
.tooltip_shortcut(action_shortcut_manual!(Key::Shift, Key::MouseLeft))
.on_update(|_| PathToolMessage::TogglePointEditing.into())
.widget_holder();
let segment_editing_mode = CheckboxInput::new(self.options.path_editing_mode.segment_editing_mode)
@@ -278,7 +278,7 @@ impl LayoutHolder for PathTool {
.icon("Remove")
.tooltip_label("Segment Editing Mode")
.tooltip_description("To multi-select modes, perform the shortcut shown.")
.tooltip_shortcut(KeysGroup(vec![Key::Shift, Key::MouseLeft]).to_string())
.tooltip_shortcut(action_shortcut_manual!(Key::Shift, Key::MouseLeft))
.on_update(|_| PathToolMessage::ToggleSegmentEditing.into())
.widget_holder();
@@ -3589,5 +3589,5 @@ fn update_dynamic_hints(
]),
PathToolFsmState::SlidingPoint => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
@@ -2288,7 +2288,7 @@ impl Fsm for PenToolFsmState {
}
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -1728,7 +1728,7 @@ impl Fsm for SelectToolFsmState {
HintInfo::keys([Key::Control, Key::KeyD], "Duplicate").add_mac_keys([Key::Command, Key::KeyD]),
]),
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::Dragging { axis, using_compass, has_dragged, .. } if *has_dragged => {
let mut hint_data = vec![
@@ -1743,7 +1743,7 @@ impl Fsm for SelectToolFsmState {
hint_data.push(HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")]));
};
let hint_data = HintData(hint_data);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::Drawing { has_drawn, .. } if *has_drawn => {
let hint_data = HintData(vec![
@@ -1753,7 +1753,7 @@ impl Fsm for SelectToolFsmState {
// TODO: (See https://discord.com/channels/731730685944922173/1216976541947531264/1321360311298818048)
// HintGroup(vec![HintInfo::keys([Key::Shift], "Extend")])
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::Drawing { .. } | SelectToolFsmState::Dragging { .. } => {}
SelectToolFsmState::ResizingBounds => {
@@ -1761,25 +1761,25 @@ impl Fsm for SelectToolFsmState {
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Alt], "From Pivot"), HintInfo::keys([Key::Shift], "Preserve Aspect Ratio")]),
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::RotatingBounds => {
let 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")]),
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::SkewingBounds { .. } => {
let hint_data = HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Control], "Unlock Slide")]),
]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
SelectToolFsmState::DraggingPivot => {
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
}
}
@@ -1187,5 +1187,5 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
]),
ShapeToolFsmState::ModifyingGizmo => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
@@ -503,7 +503,7 @@ impl Fsm for SplineToolFsmState {
SplineToolFsmState::MergingEndpoints => HintData(vec![]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
@@ -939,7 +939,7 @@ impl Fsm for TextToolFsmState {
]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });
hint_data.send_layout(responses);
}
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
+64 -17
View File
@@ -4,9 +4,9 @@ use super::common_functionality::shape_editor::ShapeState;
use super::tool_messages::*;
use crate::messages::broadcast::BroadcastMessage;
use crate::messages::broadcast::event::EventMessage;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LayoutKeysGroup, MouseMotion};
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, LabeledKeyOrMouseMotion, LabeledShortcut, MouseMotion};
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
use crate::messages::preferences::PreferencesMessageHandler;
@@ -125,12 +125,12 @@ impl DocumentToolData {
widgets: vec![
IconButton::new("SwapVertical", 16)
.tooltip_label("Swap")
.shortcut_keys(action_keys!(ToolMessageDiscriminant::SwapColors))
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::SwapColors))
.on_update(|_| ToolMessage::SwapColors.into())
.widget_holder(),
IconButton::new("WorkingColors", 16)
.tooltip_label("Reset")
.shortcut_keys(action_keys!(ToolMessageDiscriminant::ResetColors))
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::ResetColors))
.on_update(|_| ToolMessage::ResetColors.into())
.widget_holder(),
],
@@ -245,12 +245,12 @@ impl LayoutHolder for ToolData {
ToolAvailability::Available(tool) =>
ToolEntry::new(tool.tool_type(), tool.icon_name())
.tooltip_label(tool.tooltip_label())
.shortcut_keys(action_keys!(tool_type_to_activate_tool_message(tool.tool_type()))),
.tooltip_shortcut(action_shortcut!(tool_type_to_activate_tool_message(tool.tool_type()))),
ToolAvailability::AvailableAsShape(shape) =>
ToolEntry::new(shape.tool_type(), shape.icon_name())
.tooltip_label(shape.tooltip_label())
.tooltip_description(shape.tooltip_description())
.shortcut_keys(action_keys!(tool_type_to_activate_tool_message(shape.tool_type()))),
.tooltip_shortcut(action_shortcut!(tool_type_to_activate_tool_message(shape.tool_type()))),
ToolAvailability::ComingSoon(tool) => tool.clone(),
}
})
@@ -258,7 +258,7 @@ impl LayoutHolder for ToolData {
)
.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_label, tooltip_description, tooltip_shortcut, shortcut_keys, tool_type, icon_name }| {
let buttons = group.into_iter().map(|ToolEntry { tooltip_label, tooltip_description, tooltip_shortcut, tool_type, icon_name }| {
let coming_soon = tooltip_description.contains("Coming soon.");
IconButton::new(icon_name, 32)
@@ -270,7 +270,6 @@ impl LayoutHolder for ToolData {
.tooltip_label(tooltip_label.clone())
.tooltip_description(tooltip_description)
.tooltip_shortcut(tooltip_shortcut)
.shortcut_keys(shortcut_keys)
.on_update(move |_| {
match tool_type {
ToolType::Line => ToolMessage::ActivateToolShapeLine.into(),
@@ -306,8 +305,7 @@ pub struct ToolEntry {
pub icon_name: String,
pub tooltip_label: String,
pub tooltip_description: String,
pub tooltip_shortcut: String,
pub shortcut_keys: Option<ActionKeys>,
pub tooltip_shortcut: Option<ActionShortcut>,
}
#[derive(Debug)]
@@ -429,26 +427,26 @@ fn list_tools_in_groups() -> Vec<Vec<ToolAvailability>> {
ToolEntry::new(ToolType::Heal, "RasterHealTool")
.tooltip_label("Heal Tool")
.tooltip_description("Coming soon.")
.tooltip_shortcut(Key::KeyJ.to_string()),
.tooltip_shortcut(action_shortcut_manual!(Key::KeyJ)),
),
ToolAvailability::ComingSoon(
ToolEntry::new(ToolType::Clone, "RasterCloneTool")
.tooltip_label("Clone Tool")
.tooltip_description("Coming soon.")
.tooltip_shortcut(Key::KeyC.to_string()),
.tooltip_shortcut(action_shortcut_manual!(Key::KeyC)),
),
ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Patch, "RasterPatchTool").tooltip_label("Patch Tool").tooltip_description("Coming soon.")),
ToolAvailability::ComingSoon(
ToolEntry::new(ToolType::Detail, "RasterDetailTool")
.tooltip_label("Detail Tool")
.tooltip_description("Coming soon.")
.tooltip_shortcut(Key::KeyD.to_string()),
.tooltip_shortcut(action_shortcut_manual!(Key::KeyD)),
),
ToolAvailability::ComingSoon(
ToolEntry::new(ToolType::Relight, "RasterRelightTool")
.tooltip_label("Relight Tool")
.tooltip_description("Coming soon.")
.tooltip_shortcut(Key::KeyO.to_string()),
.tooltip_shortcut(action_shortcut_manual!(Key::KeyO)),
),
],
]
@@ -518,6 +516,55 @@ pub fn tool_type_to_activate_tool_message(tool_type: ToolType) -> ToolMessageDis
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct HintData(pub Vec<HintGroup>);
impl HintData {
pub fn to_layout(&self) -> Layout {
let mut widgets = Vec::new();
for (index, hint_group) in self.0.iter().enumerate() {
if index > 0 {
widgets.push(Separator::new(SeparatorType::Section).widget_holder());
}
for hint in &hint_group.0 {
if hint.plus {
widgets.push(TextLabel::new("+").bold(true).widget_holder());
}
if hint.slash {
widgets.push(TextLabel::new("/").bold(true).widget_holder());
}
for shortcut in &hint.key_groups {
widgets.push(ShortcutLabel::new(Some(ActionShortcut::Shortcut(shortcut.clone()))).widget_holder());
}
if let Some(mouse_movement) = &hint.mouse {
let mouse_movement = LabeledShortcut(vec![LabeledKeyOrMouseMotion::MouseMotion(mouse_movement.clone())]);
let shortcut = ActionShortcut::Shortcut(mouse_movement);
widgets.push(ShortcutLabel::new(Some(shortcut)).widget_holder());
}
if !hint.label.is_empty() {
widgets.push(TextLabel::new(hint.label.clone()).widget_holder());
}
}
}
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
}
pub fn send_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: self.to_layout(),
layout_target: LayoutTarget::StatusBarHints,
});
}
pub fn clear_layout(responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::StatusBarHints,
});
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct HintGroup(pub Vec<HintInfo>);
@@ -526,10 +573,10 @@ pub struct HintInfo {
/// A `KeysGroup` specifies all the keys pressed simultaneously to perform an action (like "Ctrl C" to copy).
/// Usually at most one is given, but less commonly, multiple can be used to describe additional hotkeys not used simultaneously (like the four different arrow keys to nudge a layer).
#[serde(rename = "keyGroups")]
pub key_groups: Vec<LayoutKeysGroup>,
pub key_groups: Vec<LabeledShortcut>,
/// `None` means that the regular `key_groups` should be used for all platforms, `Some` is an override for a Mac-only input hint.
#[serde(rename = "keyGroupsMac")]
pub key_groups_mac: Option<Vec<LayoutKeysGroup>>,
pub key_groups_mac: Option<Vec<LabeledShortcut>>,
/// An optional `MouseMotion` that can indicate the mouse action, like which mouse button is used and whether a drag occurs.
/// No such icon is shown if `None` is given, and it can be combined with `key_groups` if desired.
pub mouse: Option<MouseMotion>,