mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 12:18:12 +08:00
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:
@@ -3,8 +3,8 @@ pub(crate) mod menu {
|
|||||||
use base64::engine::Engine;
|
use base64::engine::Engine;
|
||||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||||
|
|
||||||
use graphite_editor::messages::input_mapper::utility_types::input_keyboard::{Key, LayoutKey, LayoutKeysGroup};
|
use graphite_editor::messages::input_mapper::utility_types::input_keyboard::{Key, LabeledKey, LabeledShortcut};
|
||||||
use graphite_editor::messages::input_mapper::utility_types::misc::ActionKeys;
|
use graphite_editor::messages::input_mapper::utility_types::misc::ActionShortcut;
|
||||||
use graphite_editor::messages::layout::LayoutMessage;
|
use graphite_editor::messages::layout::LayoutMessage;
|
||||||
use graphite_editor::messages::tool::tool_messages::tool_prelude::{LayoutGroup, LayoutTarget, MenuListEntry, SubLayout, Widget, WidgetId};
|
use graphite_editor::messages::tool::tool_messages::tool_prelude::{LayoutGroup, LayoutTarget, MenuListEntry, SubLayout, Widget, WidgetId};
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ pub(crate) mod menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let shortcut = match shortcut_keys {
|
let shortcut = match shortcut_keys {
|
||||||
Some(ActionKeys::Keys(LayoutKeysGroup(keys))) => convert_layout_keys_to_shortcut(keys),
|
Some(ActionShortcut::Shortcut(LabeledShortcut(shortcut))) => convert_labeled_keys_to_shortcut(shortcut),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,11 +126,11 @@ pub(crate) mod menu {
|
|||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_layout_keys_to_shortcut(layout_keys: &Vec<LayoutKey>) -> Option<Shortcut> {
|
fn convert_labeled_keys_to_shortcut(labeled_keys: &Vec<LabeledKey>) -> Option<Shortcut> {
|
||||||
let mut key: Option<KeyCode> = None;
|
let mut key: Option<KeyCode> = None;
|
||||||
let mut modifiers = Modifiers::default();
|
let mut modifiers = Modifiers::default();
|
||||||
for layout_key in layout_keys {
|
for labeled_key in labeled_keys {
|
||||||
match layout_key.key() {
|
match labeled_key.key() {
|
||||||
Key::Shift => modifiers |= Modifiers::SHIFT,
|
Key::Shift => modifiers |= Modifiers::SHIFT,
|
||||||
Key::Control => modifiers |= Modifiers::CONTROL,
|
Key::Control => modifiers |= Modifiers::CONTROL,
|
||||||
Key::Alt => modifiers |= Modifiers::ALT,
|
Key::Alt => modifiers |= Modifiers::ALT,
|
||||||
|
|||||||
@@ -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::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::HintData;
|
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
@@ -257,10 +256,6 @@ pub enum FrontendMessage {
|
|||||||
UpdateGraphFadeArtwork {
|
UpdateGraphFadeArtwork {
|
||||||
percentage: f64,
|
percentage: f64,
|
||||||
},
|
},
|
||||||
UpdateInputHints {
|
|
||||||
#[serde(rename = "hintData")]
|
|
||||||
hint_data: HintData,
|
|
||||||
},
|
|
||||||
UpdateLayersPanelControlBarLeftLayout {
|
UpdateLayersPanelControlBarLeftLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
@@ -335,6 +330,16 @@ pub enum FrontendMessage {
|
|||||||
#[serde(rename = "wirePath")]
|
#[serde(rename = "wirePath")]
|
||||||
wire_path: Option<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 {
|
UpdateWorkingColorsLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
|
|||||||
@@ -4,10 +4,18 @@ use bitflags::bitflags;
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// StorageType
|
||||||
|
// ===========
|
||||||
|
|
||||||
// TODO: Increase size of type
|
// TODO: Increase size of type
|
||||||
/// Edit this to specify the storage type used.
|
/// Edit this to specify the storage type used.
|
||||||
pub type StorageType = u128;
|
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
|
// 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: u32 = (std::mem::size_of::<StorageType>() * 8).trailing_zeros();
|
||||||
const STORAGE_SIZE_BITS: usize = 1 << STORAGE_SIZE;
|
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()
|
all_modifiers_without_pressed_modifiers.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// KeyPosition
|
||||||
|
// ===========
|
||||||
|
|
||||||
pub enum KeyPosition {
|
pub enum KeyPosition {
|
||||||
Pressed,
|
Pressed,
|
||||||
Released,
|
Released,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// ModifierKeys
|
||||||
|
// ============
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(transparent)]
|
#[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/>
|
// 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
|
// 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).
|
// 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
|
// 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,
|
Command,
|
||||||
/// "Ctrl" on Windows/Linux, "Cmd" on Mac
|
/// "Ctrl" on Windows/Linux, "Cmd" on Mac.
|
||||||
Accel,
|
Accel,
|
||||||
|
/// Left mouse button click (LMB).
|
||||||
MouseLeft,
|
MouseLeft,
|
||||||
|
/// Right mouse button click (RMB).
|
||||||
MouseRight,
|
MouseRight,
|
||||||
|
/// Middle mouse button click (MMB).
|
||||||
MouseMiddle,
|
MouseMiddle,
|
||||||
|
/// Mouse backward navigation button (typically on the side of the mouse).
|
||||||
MouseBack,
|
MouseBack,
|
||||||
|
/// Mouse forward navigation button (typically on the side of the mouse).
|
||||||
MouseForward,
|
MouseForward,
|
||||||
|
|
||||||
// Fake keys for displaying special labels in the UI
|
// Fake keys for displaying special labels in the UI
|
||||||
@@ -225,11 +250,11 @@ impl fmt::Display for Key {
|
|||||||
|
|
||||||
// Writing system keys
|
// Writing system keys
|
||||||
const DIGIT_PREFIX: &str = "Digit";
|
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>());
|
return write!(f, "{}", key_name.chars().skip(DIGIT_PREFIX.len()).collect::<String>());
|
||||||
}
|
}
|
||||||
const KEY_PREFIX: &str = "Key";
|
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>());
|
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;
|
pub const NUMBER_OF_KEYS: usize = Key::_KeysVariantCount as usize - 1;
|
||||||
|
|
||||||
|
// =========
|
||||||
|
// KeysGroup
|
||||||
|
// =========
|
||||||
|
|
||||||
/// Only `Key`s that exist on a physical keyboard should be used.
|
/// Only `Key`s that exist on a physical keyboard should be used.
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KeysGroup(pub Vec<Key>);
|
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 {
|
// LabeledKey
|
||||||
let layout_keys: LayoutKeysGroup = keys.into();
|
// ==========
|
||||||
serde_json::to_string(&layout_keys).expect("Failed to serialize KeysGroup")
|
|
||||||
|
#[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>);
|
// MouseMotion
|
||||||
|
// ===========
|
||||||
impl From<KeysGroup> for LayoutKeysGroup {
|
|
||||||
fn from(keys_group: KeysGroup) -> Self {
|
|
||||||
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub enum MouseMotion {
|
pub enum MouseMotion {
|
||||||
@@ -397,6 +412,45 @@ pub enum MouseMotion {
|
|||||||
MmbDrag,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct BitVector<const LENGTH: usize>([StorageType; LENGTH]);
|
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()`.
|
/// Constructs an `ActionShortcut` macro with a certain `Action` variant, conveniently wrapped in `Some()`.
|
||||||
macro_rules! action_keys {
|
macro_rules! action_shortcut {
|
||||||
($action:expr_2021) => {
|
($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 entry;
|
||||||
pub(crate) use mapping;
|
pub(crate) use mapping;
|
||||||
pub(crate) use modifiers;
|
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::key_mapping::MappingVariant;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
|
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;
|
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)]
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub enum ActionKeys {
|
pub enum ActionShortcut {
|
||||||
Action(MessageDiscriminant),
|
Action(MessageDiscriminant),
|
||||||
#[serde(rename = "keys")]
|
#[serde(rename = "shortcut")]
|
||||||
Keys(LayoutKeysGroup),
|
Shortcut(LabeledShortcut),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionKeys {
|
impl ActionShortcut {
|
||||||
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) -> String {
|
pub fn realize_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
|
||||||
match self {
|
match self {
|
||||||
Self::Action(action) => {
|
Self::Action(action) => {
|
||||||
if let Some(keys) = action_input_mapping(action) {
|
if let Some(keys) = action_input_mapping(action) {
|
||||||
let description = keys.to_string();
|
*self = Self::Shortcut(keys.into());
|
||||||
*self = Self::Keys(keys.into());
|
|
||||||
description
|
|
||||||
} else {
|
} else {
|
||||||
*self = Self::Keys(KeysGroup::default().into());
|
*self = Self::Shortcut(KeysGroup::default().into());
|
||||||
String::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Keys(keys) => {
|
Self::Shortcut(shortcut) => {
|
||||||
warn!("Calling `.to_keys()` on a `ActionKeys::Keys` is a mistake/bug. Keys are: {keys:?}.");
|
warn!("Calling `.to_keys()` on a `ActionShortcut::Shortcut` is a mistake/bug. Shortcut is: {shortcut:?}.");
|
||||||
String::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ pub enum LayoutMessage {
|
|||||||
layout: Layout,
|
layout: Layout,
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
},
|
},
|
||||||
|
DestroyLayout {
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
},
|
||||||
WidgetValueCommit {
|
WidgetValueCommit {
|
||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
widget_id: WidgetId,
|
widget_id: WidgetId,
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
|
|||||||
LayoutMessage::SendLayout { layout, layout_target } => {
|
LayoutMessage::SendLayout { layout, layout_target } => {
|
||||||
self.diff_and_send_layout_to_frontend(layout_target, layout, responses, action_input_mapping);
|
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 } => {
|
LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
|
||||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
|
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
|
||||||
}
|
}
|
||||||
@@ -78,7 +83,7 @@ impl LayoutMessageHandler {
|
|||||||
LayoutGroup::Section { layout, .. } => {
|
LayoutGroup::Section { layout, .. } => {
|
||||||
stack.extend(layout.iter().enumerate().map(|(index, val)| ([widget_path.as_slice(), &[index]].concat(), val)));
|
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 (row_index, row) in rows.iter().enumerate() {
|
||||||
for (cell_index, cell) in row.iter().enumerate() {
|
for (cell_index, cell) in row.iter().enumerate() {
|
||||||
// Return if this is the correct ID
|
// Return if this is the correct ID
|
||||||
@@ -310,6 +315,7 @@ impl LayoutMessageHandler {
|
|||||||
responses.add(callback_message);
|
responses.add(callback_message);
|
||||||
}
|
}
|
||||||
Widget::ImageLabel(_) => {}
|
Widget::ImageLabel(_) => {}
|
||||||
|
Widget::ShortcutLabel(_) => {}
|
||||||
Widget::IconLabel(_) => {}
|
Widget::IconLabel(_) => {}
|
||||||
Widget::NodeCatalog(node_type_input) => match action {
|
Widget::NodeCatalog(node_type_input) => match action {
|
||||||
WidgetValueAction::Commit => {
|
WidgetValueAction::Commit => {
|
||||||
@@ -509,8 +515,10 @@ impl LayoutMessageHandler {
|
|||||||
LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout { layout_target, diff },
|
LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout { layout_target, diff },
|
||||||
LayoutTarget::NodeGraphControlBar => FrontendMessage::UpdateNodeGraphControlBarLayout { layout_target, diff },
|
LayoutTarget::NodeGraphControlBar => FrontendMessage::UpdateNodeGraphControlBarLayout { layout_target, diff },
|
||||||
LayoutTarget::PropertiesPanel => FrontendMessage::UpdatePropertiesPanelLayout { 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::ToolOptions => FrontendMessage::UpdateToolOptionsLayout { layout_target, diff },
|
||||||
LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout { 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::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout { layout_target, diff },
|
||||||
|
|
||||||
LayoutTarget::LayoutTargetLength => panic!("`LayoutTargetLength` is not a valid Layout Target and is used for array indexing"),
|
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 super::widgets::label_widgets::*;
|
||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use std::sync::Arc;
|
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)]
|
#[derive(PartialEq, Clone, Debug, Hash, Eq, Copy, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum LayoutTarget {
|
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.
|
/// Contains the action buttons at the bottom of the dialog. Must be shown with the `FrontendMessage::DisplayDialog` message.
|
||||||
DialogButtons,
|
DialogButtons,
|
||||||
/// Contains the contents of the dialog's primary column. Must be shown with the `FrontendMessage::DisplayDialog` message.
|
/// 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,
|
DocumentBar,
|
||||||
/// Contains the dropdown for design / select / guide mode found on the top left of the canvas.
|
/// Contains the dropdown for design / select / guide mode found on the top left of the canvas.
|
||||||
DocumentMode,
|
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.
|
/// Blending options at the top of the Layers panel.
|
||||||
LayersPanelControlLeftBar,
|
LayersPanelControlLeftBar,
|
||||||
/// Selected layer status (locked/hidden) at the top of the Layers panel.
|
/// Selected layer status (locked/hidden) at the top of the Layers panel.
|
||||||
LayersPanelControlRightBar,
|
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.
|
/// The dropdown menu at the very top of the application: File, Edit, etc.
|
||||||
MenuBar,
|
MenuBar,
|
||||||
/// Bar at the top of the node graph containing the location and the "Preview" and "Hide" buttons.
|
/// Bar at the top of the node graph containing the location and the "Preview" and "Hide" buttons.
|
||||||
NodeGraphControlBar,
|
NodeGraphControlBar,
|
||||||
/// The body of the Properties panel containing many collapsable sections.
|
/// The body of the Properties panel containing many collapsable sections.
|
||||||
PropertiesPanel,
|
PropertiesPanel,
|
||||||
/// The spredsheet panel allows for the visualisation of data in the graph.
|
/// The contextual input key/mouse combination shortcuts shown in the status bar at the bottom of the window.
|
||||||
DataPanel,
|
StatusBarHints,
|
||||||
/// The bar directly above the canvas, left-aligned and to the right of the document mode dropdown.
|
/// The bar directly above the canvas, left-aligned and to the right of the document mode dropdown.
|
||||||
ToolOptions,
|
ToolOptions,
|
||||||
/// The vertical buttons for all of the tools on the left of the canvas.
|
/// The vertical buttons for all of the tools on the left of the canvas.
|
||||||
ToolShelf,
|
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.
|
/// The color swatch for the working colors and a flip and reset button found at the bottom of the tool shelf.
|
||||||
WorkingColors,
|
WorkingColors,
|
||||||
|
|
||||||
@@ -198,7 +201,7 @@ impl<'a> Iterator for WidgetIter<'a> {
|
|||||||
self.current_slice = Some(widgets);
|
self.current_slice = Some(widgets);
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Table { rows }) => {
|
Some(LayoutGroup::Table { rows, .. }) => {
|
||||||
self.table.extend(rows.iter().flatten().rev());
|
self.table.extend(rows.iter().flatten().rev());
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
@@ -248,7 +251,7 @@ impl<'a> Iterator for WidgetIterMut<'a> {
|
|||||||
self.current_slice = Some(widgets);
|
self.current_slice = Some(widgets);
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
Some(LayoutGroup::Table { rows }) => {
|
Some(LayoutGroup::Table { rows, .. }) => {
|
||||||
self.table.extend(rows.iter_mut().flatten().rev());
|
self.table.extend(rows.iter_mut().flatten().rev());
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
@@ -281,6 +284,7 @@ pub enum LayoutGroup {
|
|||||||
Table {
|
Table {
|
||||||
#[serde(rename = "tableWidgets")]
|
#[serde(rename = "tableWidgets")]
|
||||||
rows: Vec<Vec<WidgetHolder>>,
|
rows: Vec<Vec<WidgetHolder>>,
|
||||||
|
unstyled: bool,
|
||||||
},
|
},
|
||||||
#[serde(rename = "section")]
|
#[serde(rename = "section")]
|
||||||
Section {
|
Section {
|
||||||
@@ -332,7 +336,7 @@ impl LayoutGroup {
|
|||||||
Widget::TextInput(x) => &mut x.tooltip_label,
|
Widget::TextInput(x) => &mut x.tooltip_label,
|
||||||
Widget::TextLabel(x) => &mut x.tooltip_label,
|
Widget::TextLabel(x) => &mut x.tooltip_label,
|
||||||
Widget::BreadcrumbTrailButtons(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() {
|
if val.is_empty() {
|
||||||
val.clone_from(&label);
|
val.clone_from(&label);
|
||||||
@@ -368,7 +372,7 @@ impl LayoutGroup {
|
|||||||
Widget::TextInput(x) => &mut x.tooltip_description,
|
Widget::TextInput(x) => &mut x.tooltip_description,
|
||||||
Widget::TextLabel(x) => &mut x.tooltip_description,
|
Widget::TextLabel(x) => &mut x.tooltip_description,
|
||||||
Widget::BreadcrumbTrailButtons(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() {
|
if val.is_empty() {
|
||||||
val.clone_from(&description);
|
val.clone_from(&description);
|
||||||
@@ -506,7 +510,7 @@ impl WidgetHolder {
|
|||||||
&& button1.icon == button2.icon
|
&& button1.icon == button2.icon
|
||||||
&& button1.tooltip_label == button2.tooltip_label
|
&& button1.tooltip_label == button2.tooltip_label
|
||||||
&& button1.tooltip_description == button2.tooltip_description
|
&& 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
|
&& button1.popover_min_width == button2.popover_min_width
|
||||||
{
|
{
|
||||||
let mut new_widget_path = widget_path.to_vec();
|
let mut new_widget_path = widget_path.to_vec();
|
||||||
@@ -564,6 +568,7 @@ pub enum Widget {
|
|||||||
IconLabel(IconLabel),
|
IconLabel(IconLabel),
|
||||||
ImageButton(ImageButton),
|
ImageButton(ImageButton),
|
||||||
ImageLabel(ImageLabel),
|
ImageLabel(ImageLabel),
|
||||||
|
ShortcutLabel(ShortcutLabel),
|
||||||
NodeCatalog(NodeCatalog),
|
NodeCatalog(NodeCatalog),
|
||||||
NumberInput(NumberInput),
|
NumberInput(NumberInput),
|
||||||
ParameterExposeButton(ParameterExposeButton),
|
ParameterExposeButton(ParameterExposeButton),
|
||||||
@@ -607,30 +612,22 @@ pub enum DiffUpdate {
|
|||||||
impl DiffUpdate {
|
impl DiffUpdate {
|
||||||
/// Append the keyboard shortcut to the tooltip where applicable
|
/// Append the keyboard shortcut to the tooltip where applicable
|
||||||
pub fn apply_keyboard_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
|
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
|
// Go through each widget to convert `ActionShortcut::Action` to `ActionShortcut::Shortcut` and append the key combination to the widget 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
|
|
||||||
let convert_tooltip = |widget_holder: &mut WidgetHolder| {
|
let convert_tooltip = |widget_holder: &mut WidgetHolder| {
|
||||||
// Handle all the widgets that have tooltips
|
// Handle all the widgets that have tooltips
|
||||||
let mut shortcut_keys = match &mut widget_holder.widget {
|
let tooltip_shortcut = match &mut widget_holder.widget {
|
||||||
Widget::BreadcrumbTrailButtons(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::BreadcrumbTrailButtons(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::CheckboxInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::CheckboxInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::ColorInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::ColorInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::DropdownInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::DropdownInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::FontInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::FontInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::IconButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::IconButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::NumberInput(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::NumberInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::ParameterExposeButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::PopoverButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::PopoverButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::TextButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::TextButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
Widget::ImageButton(widget) => Some((&mut widget.tooltip_shortcut, &mut widget.shortcut_keys)),
|
Widget::ImageButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||||
|
Widget::ShortcutLabel(widget) => widget.shortcut.as_mut(),
|
||||||
Widget::IconLabel(_)
|
Widget::IconLabel(_)
|
||||||
| Widget::ImageLabel(_)
|
| Widget::ImageLabel(_)
|
||||||
| Widget::CurveInput(_)
|
| Widget::CurveInput(_)
|
||||||
@@ -643,34 +640,32 @@ impl DiffUpdate {
|
|||||||
| Widget::TextLabel(_)
|
| Widget::TextLabel(_)
|
||||||
| Widget::WorkingColorsInput(_) => None,
|
| 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
|
// Handle RadioInput separately because its tooltips are children of the widget
|
||||||
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
|
if let Widget::RadioInput(radio_input) = &mut widget_holder.widget {
|
||||||
for radio_entry_data in &mut radio_input.entries {
|
for radio_entry_data in &mut radio_input.entries {
|
||||||
if let RadioEntryData {
|
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
|
||||||
tooltip_shortcut,
|
if let Some(tooltip_shortcut) = radio_entry_data.tooltip_shortcut.as_mut() {
|
||||||
shortcut_keys: Some(shortcut_keys),
|
tooltip_shortcut.realize_shortcut(action_input_mapping);
|
||||||
..
|
|
||||||
} = radio_entry_data
|
|
||||||
{
|
|
||||||
apply_shortcut_to_tooltip(shortcut_keys, tooltip_shortcut);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Recursively fill menu list entries with their realized shortcut keys specific to the current bindings and platform
|
// 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));
|
struct RecursiveWrapper<'a>(&'a dyn Fn(&mut MenuListEntrySections, &RecursiveWrapper));
|
||||||
let recursive_wrapper = RecursiveWrapper(&|entry_sections: &mut MenuListEntrySections, recursive_wrapper| {
|
let recursive_wrapper = RecursiveWrapper(&|entry_sections: &mut MenuListEntrySections, recursive_wrapper| {
|
||||||
for entries in entry_sections {
|
for entries in entry_sections {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
// Convert the shortcut actions to keys for this menu entry
|
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
|
||||||
if let Some(shortcut_keys) = &mut entry.shortcut_keys {
|
if let Some(tooltip_shortcut) = &mut entry.tooltip_shortcut {
|
||||||
shortcut_keys.to_keys(action_input_mapping);
|
tooltip_shortcut.realize_shortcut(action_input_mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively call this inner closure on the menu's children
|
// 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
|
// Apply shortcut conversions to all widgets that have menu lists
|
||||||
let convert_menu_lists = |widget_holder: &mut WidgetHolder| match &mut widget_holder.widget {
|
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::DropdownInput(dropdown_input) => apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries),
|
||||||
Widget::TextButton(text_button) => apply_action_keys_to_menu_lists(&mut text_button.menu_list_children),
|
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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
|
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
|
||||||
@@ -29,10 +29,7 @@ pub struct IconButton {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -63,10 +60,7 @@ pub struct PopoverButton {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
#[serde(rename = "popoverLayout")]
|
#[serde(rename = "popoverLayout")]
|
||||||
pub popover_layout: SubLayout,
|
pub popover_layout: SubLayout,
|
||||||
@@ -104,10 +98,7 @@ pub struct ParameterExposeButton {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -148,10 +139,7 @@ pub struct TextButton {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
#[serde(rename = "menuListChildren")]
|
#[serde(rename = "menuListChildren")]
|
||||||
pub menu_list_children: MenuListEntrySections,
|
pub menu_list_children: MenuListEntrySections,
|
||||||
@@ -183,10 +171,7 @@ pub struct ImageButton {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -228,10 +213,7 @@ pub struct ColorInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -258,10 +240,7 @@ pub struct BreadcrumbTrailButtons {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[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 crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
@@ -23,15 +23,12 @@ pub struct CheckboxInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(rename = "forLabel")]
|
#[serde(rename = "forLabel")]
|
||||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
pub for_label: CheckboxId,
|
pub for_label: CheckboxId,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
@@ -51,7 +48,6 @@ impl Default for CheckboxInput {
|
|||||||
tooltip_label: Default::default(),
|
tooltip_label: Default::default(),
|
||||||
tooltip_description: Default::default(),
|
tooltip_description: Default::default(),
|
||||||
tooltip_shortcut: Default::default(),
|
tooltip_shortcut: Default::default(),
|
||||||
shortcut_keys: Default::default(),
|
|
||||||
for_label: CheckboxId::new(),
|
for_label: CheckboxId::new(),
|
||||||
on_update: Default::default(),
|
on_update: Default::default(),
|
||||||
on_commit: Default::default(),
|
on_commit: Default::default(),
|
||||||
@@ -106,10 +102,7 @@ pub struct DropdownInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Styling
|
// Styling
|
||||||
#[serde(rename = "minWidth")]
|
#[serde(rename = "minWidth")]
|
||||||
@@ -146,14 +139,7 @@ pub struct MenuListEntry {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
// TODO: Make this serde(skip)
|
|
||||||
#[serde(rename = "shortcutKeys")]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
#[serde(rename = "shortcutRequiresLock")]
|
|
||||||
pub shortcut_requires_lock: bool,
|
|
||||||
|
|
||||||
pub children: MenuListEntrySections,
|
pub children: MenuListEntrySections,
|
||||||
|
|
||||||
@@ -190,10 +176,7 @@ pub struct FontInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -218,10 +201,7 @@ pub struct NumberInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
@@ -394,10 +374,7 @@ pub struct RadioEntryData {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -436,7 +413,7 @@ pub struct TextAreaInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -467,7 +444,7 @@ pub struct TextInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
pub centered: bool,
|
pub centered: bool,
|
||||||
|
|
||||||
@@ -502,7 +479,7 @@ pub struct CurveInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@@ -529,7 +506,7 @@ pub struct ReferencePointInput {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use super::input_widgets::CheckboxId;
|
use super::input_widgets::CheckboxId;
|
||||||
|
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
use graphite_proc_macros::WidgetBuilder;
|
use graphite_proc_macros::WidgetBuilder;
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ pub struct IconLabel {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[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)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
|
||||||
@@ -74,7 +75,7 @@ pub struct TextLabel {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[serde(rename = "tooltipShortcut")]
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
|
|
||||||
#[serde(rename = "forCheckbox")]
|
#[serde(rename = "forCheckbox")]
|
||||||
#[derivative(PartialEq = "ignore")]
|
#[derivative(PartialEq = "ignore")]
|
||||||
@@ -102,7 +103,13 @@ pub struct ImageLabel {
|
|||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
|
|
||||||
#[serde(rename = "tooltipShortcut")]
|
#[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"]));
|
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"]));
|
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 super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
||||||
use crate::application::{GRAPHITE_GIT_COMMIT_HASH, generate_uuid};
|
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::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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
|
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
@@ -2212,20 +2212,20 @@ impl DocumentMessageHandler {
|
|||||||
let mut widgets = vec![
|
let mut widgets = vec![
|
||||||
IconButton::new("PlaybackToStart", 24)
|
IconButton::new("PlaybackToStart", 24)
|
||||||
.tooltip_label("Restart Animation")
|
.tooltip_label("Restart Animation")
|
||||||
.shortcut_keys(action_keys!(AnimationMessageDiscriminant::RestartAnimation))
|
.tooltip_shortcut(action_shortcut!(AnimationMessageDiscriminant::RestartAnimation))
|
||||||
.on_update(|_| AnimationMessage::RestartAnimation.into())
|
.on_update(|_| AnimationMessage::RestartAnimation.into())
|
||||||
.disabled(time == Duration::ZERO)
|
.disabled(time == Duration::ZERO)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new(if animation_is_playing { "PlaybackPause" } else { "PlaybackPlay" }, 24)
|
IconButton::new(if animation_is_playing { "PlaybackPause" } else { "PlaybackPlay" }, 24)
|
||||||
.tooltip_label(if animation_is_playing { "Pause Animation" } else { "Play Animation" })
|
.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())
|
.on_update(|_| AnimationMessage::ToggleLivePreview.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CheckboxInput::new(self.overlays_visibility_settings.all)
|
CheckboxInput::new(self.overlays_visibility_settings.all)
|
||||||
.icon("Overlays")
|
.icon("Overlays")
|
||||||
.tooltip_label("Overlays")
|
.tooltip_label("Overlays")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleOverlaysVisibility))
|
||||||
.on_update(|optional_input: &CheckboxInput| {
|
.on_update(|optional_input: &CheckboxInput| {
|
||||||
DocumentMessage::SetOverlaysVisibility {
|
DocumentMessage::SetOverlaysVisibility {
|
||||||
visible: optional_input.checked,
|
visible: optional_input.checked,
|
||||||
@@ -2474,7 +2474,7 @@ impl DocumentMessageHandler {
|
|||||||
CheckboxInput::new(snapping_state.snapping_enabled)
|
CheckboxInput::new(snapping_state.snapping_enabled)
|
||||||
.icon("Snapping")
|
.icon("Snapping")
|
||||||
.tooltip_label("Snapping")
|
.tooltip_label("Snapping")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSnapping))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSnapping))
|
||||||
.on_update(move |optional_input: &CheckboxInput| {
|
.on_update(move |optional_input: &CheckboxInput| {
|
||||||
DocumentMessage::SetSnapping {
|
DocumentMessage::SetSnapping {
|
||||||
closure: Some(|snapping_state| &mut snapping_state.snapping_enabled),
|
closure: Some(|snapping_state| &mut snapping_state.snapping_enabled),
|
||||||
@@ -2544,7 +2544,7 @@ impl DocumentMessageHandler {
|
|||||||
CheckboxInput::new(self.snapping_state.grid_snapping)
|
CheckboxInput::new(self.snapping_state.grid_snapping)
|
||||||
.icon("Grid")
|
.icon("Grid")
|
||||||
.tooltip_label("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())
|
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
PopoverButton::new()
|
PopoverButton::new()
|
||||||
@@ -2626,7 +2626,7 @@ impl DocumentMessageHandler {
|
|||||||
.icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into()))
|
.icon(Some((if self.graph_view_overlay_open { "GraphViewOpen" } else { "GraphViewClosed" }).into()))
|
||||||
.hover_icon(Some((if self.graph_view_overlay_open { "GraphViewClosed" } else { "GraphViewOpen" }).into()))
|
.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" })
|
.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())
|
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
]);
|
]);
|
||||||
@@ -2778,14 +2778,14 @@ impl DocumentMessageHandler {
|
|||||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
.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())
|
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
.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())
|
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -2850,7 +2850,7 @@ impl DocumentMessageHandler {
|
|||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
IconButton::new("Folder", 24)
|
IconButton::new("Folder", 24)
|
||||||
.tooltip_label("Group Selected")
|
.tooltip_label("Group Selected")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||||
.on_update(|_| {
|
.on_update(|_| {
|
||||||
let group_folder_type = GroupFolderType::Layer;
|
let group_folder_type = GroupFolderType::Layer;
|
||||||
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
||||||
@@ -2859,12 +2859,12 @@ impl DocumentMessageHandler {
|
|||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("NewLayer", 24)
|
IconButton::new("NewLayer", 24)
|
||||||
.tooltip_label("New Layer")
|
.tooltip_label("New Layer")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||||
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("Trash", 24)
|
IconButton::new("Trash", 24)
|
||||||
.tooltip_label("Delete Selected")
|
.tooltip_label("Delete Selected")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||||
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -3148,17 +3148,17 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
|
|||||||
let mut list = vec![
|
let mut list = vec![
|
||||||
IconButton::new("ZoomIn", 24)
|
IconButton::new("ZoomIn", 24)
|
||||||
.tooltip_label("Zoom In")
|
.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())
|
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("ZoomOut", 24)
|
IconButton::new("ZoomOut", 24)
|
||||||
.tooltip_label("Zoom Out")
|
.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())
|
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("ZoomReset", 24)
|
IconButton::new("ZoomReset", 24)
|
||||||
.tooltip_label("Reset Tilt and Zoom to 100%")
|
.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())
|
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||||
.disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
|
.disabled(ptz.tilt().abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -3168,7 +3168,7 @@ pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHand
|
|||||||
IconButton::new("Reverse", 24)
|
IconButton::new("Reverse", 24)
|
||||||
.tooltip_label("Unflip Canvas")
|
.tooltip_label("Unflip Canvas")
|
||||||
.tooltip_description("Flip the canvas back to its standard orientation.")
|
.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())
|
.on_update(|_| NavigationMessage::CanvasFlip.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
|
|||||||
NavigationMessage::BeginCanvasPan => {
|
NavigationMessage::BeginCanvasPan => {
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]).send_layout(responses);
|
||||||
hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
self.mouse_position = ipp.mouse.position;
|
||||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
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);
|
responses.add(NavigationMessage::BeginCanvasPan);
|
||||||
} else {
|
} else {
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
HintData(vec![
|
||||||
hint_data: HintData(vec![
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
|
||||||
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
|
])
|
||||||
]),
|
.send_layout(responses);
|
||||||
});
|
|
||||||
|
|
||||||
self.navigation_operation = NavigationOperation::Tilt {
|
self.navigation_operation = NavigationOperation::Tilt {
|
||||||
tilt_original_for_abort: ptz.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::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
HintData(vec![
|
||||||
hint_data: HintData(vec![
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
HintGroup(vec![HintInfo::keys([Key::Shift], "Increments")]),
|
||||||
HintGroup(vec![HintInfo::keys([Key::Shift], "Increments")]),
|
])
|
||||||
]),
|
.send_layout(responses);
|
||||||
});
|
|
||||||
|
|
||||||
self.navigation_operation = NavigationOperation::Zoom {
|
self.navigation_operation = NavigationOperation::Zoom {
|
||||||
zoom_raw_not_snapped: ptz.zoom(),
|
zoom_raw_not_snapped: ptz.zoom(),
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
|
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
|
||||||
use super::{document_node_definitions, node_properties};
|
use super::{document_node_definitions, node_properties};
|
||||||
use crate::consts::GRID_SIZE;
|
use crate::consts::GRID_SIZE;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
@@ -2108,7 +2107,7 @@ impl NodeGraphMessageHandler {
|
|||||||
.icon(Some("Node".to_string()))
|
.icon(Some("Node".to_string()))
|
||||||
.tooltip_label("New Node")
|
.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_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({
|
.popover_layout({
|
||||||
// Showing only compatible types
|
// Showing only compatible types
|
||||||
let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) {
|
let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) {
|
||||||
@@ -2154,7 +2153,7 @@ impl NodeGraphMessageHandler {
|
|||||||
//
|
//
|
||||||
IconButton::new("Folder", 24)
|
IconButton::new("Folder", 24)
|
||||||
.tooltip_label("Group Selected")
|
.tooltip_label("Group Selected")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||||
.on_update(|_| {
|
.on_update(|_| {
|
||||||
let group_folder_type = GroupFolderType::Layer;
|
let group_folder_type = GroupFolderType::Layer;
|
||||||
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
DocumentMessage::GroupSelectedLayers { group_folder_type }.into()
|
||||||
@@ -2163,12 +2162,12 @@ impl NodeGraphMessageHandler {
|
|||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("NewLayer", 24)
|
IconButton::new("NewLayer", 24)
|
||||||
.tooltip_label("New Layer")
|
.tooltip_label("New Layer")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||||
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("Trash", 24)
|
IconButton::new("Trash", 24)
|
||||||
.tooltip_label("Delete Selected")
|
.tooltip_label("Delete Selected")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||||
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -2178,14 +2177,14 @@ impl NodeGraphMessageHandler {
|
|||||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||||
.tooltip_label(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
.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())
|
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||||
.disabled(!has_selection || !selection_includes_layers)
|
.disabled(!has_selection || !selection_includes_layers)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||||
.tooltip_label(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
.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())
|
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -2225,7 +2224,7 @@ impl NodeGraphMessageHandler {
|
|||||||
.icon(Some("FrameAll".to_string()))
|
.icon(Some("FrameAll".to_string()))
|
||||||
.tooltip_label("Preview")
|
.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_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())
|
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
||||||
@@ -2284,7 +2283,7 @@ impl NodeGraphMessageHandler {
|
|||||||
.icon(Some("GraphViewOpen".into()))
|
.icon(Some("GraphViewOpen".into()))
|
||||||
.hover_icon(Some("GraphViewClosed".into()))
|
.hover_icon(Some("GraphViewClosed".into()))
|
||||||
.tooltip_label("Hide Node Graph")
|
.tooltip_label("Hide Node Graph")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
]);
|
]);
|
||||||
@@ -2720,8 +2719,7 @@ impl NodeGraphMessageHandler {
|
|||||||
|
|
||||||
// Cancel the ongoing action
|
// Cancel the ongoing action
|
||||||
if wiring || dragging_nodes || dragging_box_selection {
|
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()])]);
|
HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]).send_layout(responses);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2749,7 +2747,7 @@ impl NodeGraphMessageHandler {
|
|||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDouble, "Enter Node Subgraph")]),
|
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDouble, "Enter Node Subgraph")]),
|
||||||
HintGroup(vec![HintInfo::keys_and_mouse([Key::Alt], MouseMotion::Lmb, "Preview Node Output")]),
|
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));
|
hint_groups.push(HintGroup(typing_hints));
|
||||||
|
|
||||||
let hint_data = HintData(hint_groups);
|
HintData(hint_groups).send_layout(responses);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_constraint_to_axis(&self) -> bool {
|
pub fn is_constraint_to_axis(&self) -> bool {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
|
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…")
|
let preferences = MenuListEntry::new("Preferences…")
|
||||||
.label("Preferences…")
|
.label("Preferences…")
|
||||||
.icon("Settings")
|
.icon("Settings")
|
||||||
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog))
|
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestPreferencesDialog))
|
||||||
.on_commit(|_| DialogMessage::RequestPreferencesDialog.into());
|
.on_commit(|_| DialogMessage::RequestPreferencesDialog.into());
|
||||||
|
|
||||||
let menu_bar_buttons = vec![
|
let menu_bar_buttons = vec![
|
||||||
@@ -89,21 +89,21 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("Hide Graphite")
|
MenuListEntry::new("Hide Graphite")
|
||||||
.label("Hide Graphite")
|
.label("Hide Graphite")
|
||||||
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::Hide))
|
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Hide))
|
||||||
.on_commit(|_| AppWindowMessage::Hide.into()),
|
.on_commit(|_| AppWindowMessage::Hide.into()),
|
||||||
MenuListEntry::new("Hide Others")
|
MenuListEntry::new("Hide Others")
|
||||||
.label("Hide Others")
|
.label("Hide Others")
|
||||||
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::HideOthers))
|
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::HideOthers))
|
||||||
.on_commit(|_| AppWindowMessage::HideOthers.into()),
|
.on_commit(|_| AppWindowMessage::HideOthers.into()),
|
||||||
MenuListEntry::new("Show All")
|
MenuListEntry::new("Show All")
|
||||||
.label("Show All")
|
.label("Show All")
|
||||||
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::ShowAll))
|
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::ShowAll))
|
||||||
.on_commit(|_| AppWindowMessage::ShowAll.into()),
|
.on_commit(|_| AppWindowMessage::ShowAll.into()),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("Quit Graphite")
|
MenuListEntry::new("Quit Graphite")
|
||||||
.label("Quit Graphite")
|
.label("Quit Graphite")
|
||||||
.shortcut_keys(action_keys!(AppWindowMessageDiscriminant::Close))
|
.tooltip_shortcut(action_shortcut!(AppWindowMessageDiscriminant::Close))
|
||||||
.on_commit(|_| AppWindowMessage::Close.into()),
|
.on_commit(|_| AppWindowMessage::Close.into()),
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -117,11 +117,11 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
.label("New…")
|
.label("New…")
|
||||||
.icon("File")
|
.icon("File")
|
||||||
.on_commit(|_| DialogMessage::RequestNewDocumentDialog.into())
|
.on_commit(|_| DialogMessage::RequestNewDocumentDialog.into())
|
||||||
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestNewDocumentDialog)),
|
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestNewDocumentDialog)),
|
||||||
MenuListEntry::new("Open…")
|
MenuListEntry::new("Open…")
|
||||||
.label("Open…")
|
.label("Open…")
|
||||||
.icon("Folder")
|
.icon("Folder")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::OpenDocument))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::OpenDocument))
|
||||||
.on_commit(|_| PortfolioMessage::OpenDocument.into()),
|
.on_commit(|_| PortfolioMessage::OpenDocument.into()),
|
||||||
MenuListEntry::new("Open Demo Artwork…")
|
MenuListEntry::new("Open Demo Artwork…")
|
||||||
.label("Open Demo Artwork…")
|
.label("Open Demo Artwork…")
|
||||||
@@ -132,13 +132,13 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Close")
|
MenuListEntry::new("Close")
|
||||||
.label("Close")
|
.label("Close")
|
||||||
.icon("Close")
|
.icon("Close")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::CloseActiveDocumentWithConfirmation))
|
||||||
.on_commit(|_| PortfolioMessage::CloseActiveDocumentWithConfirmation.into())
|
.on_commit(|_| PortfolioMessage::CloseActiveDocumentWithConfirmation.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Close All")
|
MenuListEntry::new("Close All")
|
||||||
.label("Close All")
|
.label("Close All")
|
||||||
.icon("CloseAll")
|
.icon("CloseAll")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::CloseAllDocumentsWithConfirmation))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::CloseAllDocumentsWithConfirmation))
|
||||||
.on_commit(|_| PortfolioMessage::CloseAllDocumentsWithConfirmation.into())
|
.on_commit(|_| PortfolioMessage::CloseAllDocumentsWithConfirmation.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -146,14 +146,14 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Save")
|
MenuListEntry::new("Save")
|
||||||
.label("Save")
|
.label("Save")
|
||||||
.icon("Save")
|
.icon("Save")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SaveDocument))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SaveDocument))
|
||||||
.on_commit(|_| DocumentMessage::SaveDocument.into())
|
.on_commit(|_| DocumentMessage::SaveDocument.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
MenuListEntry::new("Save As…")
|
MenuListEntry::new("Save As…")
|
||||||
.label("Save As…")
|
.label("Save As…")
|
||||||
.icon("Save")
|
.icon("Save")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SaveDocumentAs))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SaveDocumentAs))
|
||||||
.on_commit(|_| DocumentMessage::SaveDocumentAs.into())
|
.on_commit(|_| DocumentMessage::SaveDocumentAs.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -161,12 +161,12 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Import…")
|
MenuListEntry::new("Import…")
|
||||||
.label("Import…")
|
.label("Import…")
|
||||||
.icon("FileImport")
|
.icon("FileImport")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Import))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Import))
|
||||||
.on_commit(|_| PortfolioMessage::Import.into()),
|
.on_commit(|_| PortfolioMessage::Import.into()),
|
||||||
MenuListEntry::new("Export…")
|
MenuListEntry::new("Export…")
|
||||||
.label("Export…")
|
.label("Export…")
|
||||||
.icon("FileExport")
|
.icon("FileExport")
|
||||||
.shortcut_keys(action_keys!(DialogMessageDiscriminant::RequestExportDialog))
|
.tooltip_shortcut(action_shortcut!(DialogMessageDiscriminant::RequestExportDialog))
|
||||||
.on_commit(|_| DialogMessage::RequestExportDialog.into())
|
.on_commit(|_| DialogMessage::RequestExportDialog.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -182,13 +182,13 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Undo")
|
MenuListEntry::new("Undo")
|
||||||
.label("Undo")
|
.label("Undo")
|
||||||
.icon("HistoryUndo")
|
.icon("HistoryUndo")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::Undo))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::Undo))
|
||||||
.on_commit(|_| DocumentMessage::Undo.into())
|
.on_commit(|_| DocumentMessage::Undo.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Redo")
|
MenuListEntry::new("Redo")
|
||||||
.label("Redo")
|
.label("Redo")
|
||||||
.icon("HistoryRedo")
|
.icon("HistoryRedo")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::Redo))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::Redo))
|
||||||
.on_commit(|_| DocumentMessage::Redo.into())
|
.on_commit(|_| DocumentMessage::Redo.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -196,19 +196,19 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Cut")
|
MenuListEntry::new("Cut")
|
||||||
.label("Cut")
|
.label("Cut")
|
||||||
.icon("Cut")
|
.icon("Cut")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Cut))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Cut))
|
||||||
.on_commit(|_| PortfolioMessage::Cut { clipboard: Clipboard::Device }.into())
|
.on_commit(|_| PortfolioMessage::Cut { clipboard: Clipboard::Device }.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Copy")
|
MenuListEntry::new("Copy")
|
||||||
.label("Copy")
|
.label("Copy")
|
||||||
.icon("Copy")
|
.icon("Copy")
|
||||||
.shortcut_keys(action_keys!(PortfolioMessageDiscriminant::Copy))
|
.tooltip_shortcut(action_shortcut!(PortfolioMessageDiscriminant::Copy))
|
||||||
.on_commit(|_| PortfolioMessage::Copy { clipboard: Clipboard::Device }.into())
|
.on_commit(|_| PortfolioMessage::Copy { clipboard: Clipboard::Device }.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Paste")
|
MenuListEntry::new("Paste")
|
||||||
.label("Paste")
|
.label("Paste")
|
||||||
.icon("Paste")
|
.icon("Paste")
|
||||||
.shortcut_keys(action_keys!(FrontendMessageDiscriminant::TriggerPaste))
|
.tooltip_shortcut(action_shortcut!(FrontendMessageDiscriminant::TriggerPaste))
|
||||||
.on_commit(|_| FrontendMessage::TriggerPaste.into())
|
.on_commit(|_| FrontendMessage::TriggerPaste.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -216,13 +216,13 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Duplicate")
|
MenuListEntry::new("Duplicate")
|
||||||
.label("Duplicate")
|
.label("Duplicate")
|
||||||
.icon("Copy")
|
.icon("Copy")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DuplicateSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DuplicateSelectedLayers))
|
||||||
.on_commit(|_| DocumentMessage::DuplicateSelectedLayers.into())
|
.on_commit(|_| DocumentMessage::DuplicateSelectedLayers.into())
|
||||||
.disabled(no_active_document || !has_selected_nodes),
|
.disabled(no_active_document || !has_selected_nodes),
|
||||||
MenuListEntry::new("Delete")
|
MenuListEntry::new("Delete")
|
||||||
.label("Delete")
|
.label("Delete")
|
||||||
.icon("Trash")
|
.icon("Trash")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||||
.on_commit(|_| DocumentMessage::DeleteSelectedLayers.into())
|
.on_commit(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||||
.disabled(no_active_document || !has_selected_nodes),
|
.disabled(no_active_document || !has_selected_nodes),
|
||||||
],
|
],
|
||||||
@@ -238,13 +238,12 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
TextButton::new("Layer")
|
TextButton::new("Layer")
|
||||||
.label("Layer")
|
.label("Layer")
|
||||||
.flush(true)
|
.flush(true)
|
||||||
.disabled(no_active_document)
|
|
||||||
.menu_list_children(vec![
|
.menu_list_children(vec![
|
||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("New")
|
MenuListEntry::new("New")
|
||||||
.label("New")
|
.label("New")
|
||||||
.icon("NewLayer")
|
.icon("NewLayer")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||||
.on_commit(|_| DocumentMessage::CreateEmptyFolder.into())
|
.on_commit(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -252,7 +251,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Group")
|
MenuListEntry::new("Group")
|
||||||
.label("Group")
|
.label("Group")
|
||||||
.icon("Folder")
|
.icon("Folder")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||||
.on_commit(|_| {
|
.on_commit(|_| {
|
||||||
DocumentMessage::GroupSelectedLayers {
|
DocumentMessage::GroupSelectedLayers {
|
||||||
group_folder_type: GroupFolderType::Layer,
|
group_folder_type: GroupFolderType::Layer,
|
||||||
@@ -263,7 +262,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Ungroup")
|
MenuListEntry::new("Ungroup")
|
||||||
.label("Ungroup")
|
.label("Ungroup")
|
||||||
.icon("FolderOpen")
|
.icon("FolderOpen")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::UngroupSelectedLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::UngroupSelectedLayers))
|
||||||
.on_commit(|_| DocumentMessage::UngroupSelectedLayers.into())
|
.on_commit(|_| DocumentMessage::UngroupSelectedLayers.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
],
|
],
|
||||||
@@ -271,13 +270,13 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Hide/Show")
|
MenuListEntry::new("Hide/Show")
|
||||||
.label("Hide/Show")
|
.label("Hide/Show")
|
||||||
.icon("EyeHide")
|
.icon("EyeHide")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||||
.on_commit(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
.on_commit(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Lock/Unlock")
|
MenuListEntry::new("Lock/Unlock")
|
||||||
.label("Lock/Unlock")
|
.label("Lock/Unlock")
|
||||||
.icon("PadlockLocked")
|
.icon("PadlockLocked")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||||
.on_commit(|_| DocumentMessage::ToggleSelectedLocked.into())
|
.on_commit(|_| DocumentMessage::ToggleSelectedLocked.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
],
|
],
|
||||||
@@ -285,19 +284,19 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Grab")
|
MenuListEntry::new("Grab")
|
||||||
.label("Grab")
|
.label("Grab")
|
||||||
.icon("TransformationGrab")
|
.icon("TransformationGrab")
|
||||||
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginGrab))
|
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginGrab))
|
||||||
.on_commit(|_| TransformLayerMessage::BeginGrab.into())
|
.on_commit(|_| TransformLayerMessage::BeginGrab.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Rotate")
|
MenuListEntry::new("Rotate")
|
||||||
.label("Rotate")
|
.label("Rotate")
|
||||||
.icon("TransformationRotate")
|
.icon("TransformationRotate")
|
||||||
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginRotate))
|
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginRotate))
|
||||||
.on_commit(|_| TransformLayerMessage::BeginRotate.into())
|
.on_commit(|_| TransformLayerMessage::BeginRotate.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Scale")
|
MenuListEntry::new("Scale")
|
||||||
.label("Scale")
|
.label("Scale")
|
||||||
.icon("TransformationScale")
|
.icon("TransformationScale")
|
||||||
.shortcut_keys(action_keys!(TransformLayerMessageDiscriminant::BeginScale))
|
.tooltip_shortcut(action_shortcut!(TransformLayerMessageDiscriminant::BeginScale))
|
||||||
.on_commit(|_| TransformLayerMessage::BeginScale.into())
|
.on_commit(|_| TransformLayerMessage::BeginScale.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
],
|
],
|
||||||
@@ -311,25 +310,25 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Raise To Front")
|
MenuListEntry::new("Raise To Front")
|
||||||
.label("Raise To Front")
|
.label("Raise To Front")
|
||||||
.icon("Stack")
|
.icon("Stack")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersRaiseToFront))
|
||||||
.on_commit(|_| DocumentMessage::SelectedLayersRaiseToFront.into())
|
.on_commit(|_| DocumentMessage::SelectedLayersRaiseToFront.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Raise")
|
MenuListEntry::new("Raise")
|
||||||
.label("Raise")
|
.label("Raise")
|
||||||
.icon("StackRaise")
|
.icon("StackRaise")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersRaise))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersRaise))
|
||||||
.on_commit(|_| DocumentMessage::SelectedLayersRaise.into())
|
.on_commit(|_| DocumentMessage::SelectedLayersRaise.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Lower")
|
MenuListEntry::new("Lower")
|
||||||
.label("Lower")
|
.label("Lower")
|
||||||
.icon("StackLower")
|
.icon("StackLower")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersLower))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersLower))
|
||||||
.on_commit(|_| DocumentMessage::SelectedLayersLower.into())
|
.on_commit(|_| DocumentMessage::SelectedLayersLower.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Lower to Back")
|
MenuListEntry::new("Lower to Back")
|
||||||
.label("Lower to Back")
|
.label("Lower to Back")
|
||||||
.icon("StackBottom")
|
.icon("StackBottom")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectedLayersLowerToBack))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectedLayersLowerToBack))
|
||||||
.on_commit(|_| DocumentMessage::SelectedLayersLowerToBack.into())
|
.on_commit(|_| DocumentMessage::SelectedLayersLowerToBack.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
],
|
],
|
||||||
@@ -508,25 +507,24 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
TextButton::new("Select")
|
TextButton::new("Select")
|
||||||
.label("Select")
|
.label("Select")
|
||||||
.flush(true)
|
.flush(true)
|
||||||
.disabled(no_active_document)
|
|
||||||
.menu_list_children(vec![
|
.menu_list_children(vec![
|
||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("Select All")
|
MenuListEntry::new("Select All")
|
||||||
.label("Select All")
|
.label("Select All")
|
||||||
.icon("SelectAll")
|
.icon("SelectAll")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectAllLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectAllLayers))
|
||||||
.on_commit(|_| DocumentMessage::SelectAllLayers.into())
|
.on_commit(|_| DocumentMessage::SelectAllLayers.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Deselect All")
|
MenuListEntry::new("Deselect All")
|
||||||
.label("Deselect All")
|
.label("Deselect All")
|
||||||
.icon("DeselectAll")
|
.icon("DeselectAll")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::DeselectAllLayers))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::DeselectAllLayers))
|
||||||
.on_commit(|_| DocumentMessage::DeselectAllLayers.into())
|
.on_commit(|_| DocumentMessage::DeselectAllLayers.into())
|
||||||
.disabled(no_active_document || !has_selected_nodes),
|
.disabled(no_active_document || !has_selected_nodes),
|
||||||
MenuListEntry::new("Select Parent")
|
MenuListEntry::new("Select Parent")
|
||||||
.label("Select Parent")
|
.label("Select Parent")
|
||||||
.icon("SelectParent")
|
.icon("SelectParent")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectParentLayer))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectParentLayer))
|
||||||
.on_commit(|_| DocumentMessage::SelectParentLayer.into())
|
.on_commit(|_| DocumentMessage::SelectParentLayer.into())
|
||||||
.disabled(no_active_document || !has_selected_nodes),
|
.disabled(no_active_document || !has_selected_nodes),
|
||||||
],
|
],
|
||||||
@@ -534,13 +532,13 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Previous Selection")
|
MenuListEntry::new("Previous Selection")
|
||||||
.label("Previous Selection")
|
.label("Previous Selection")
|
||||||
.icon("HistoryUndo")
|
.icon("HistoryUndo")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectionStepBack))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectionStepBack))
|
||||||
.on_commit(|_| DocumentMessage::SelectionStepBack.into())
|
.on_commit(|_| DocumentMessage::SelectionStepBack.into())
|
||||||
.disabled(!has_selection_history.0),
|
.disabled(!has_selection_history.0),
|
||||||
MenuListEntry::new("Next Selection")
|
MenuListEntry::new("Next Selection")
|
||||||
.label("Next Selection")
|
.label("Next Selection")
|
||||||
.icon("HistoryRedo")
|
.icon("HistoryRedo")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::SelectionStepForward))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::SelectionStepForward))
|
||||||
.on_commit(|_| DocumentMessage::SelectionStepForward.into())
|
.on_commit(|_| DocumentMessage::SelectionStepForward.into())
|
||||||
.disabled(!has_selection_history.1),
|
.disabled(!has_selection_history.1),
|
||||||
],
|
],
|
||||||
@@ -549,19 +547,18 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
TextButton::new("View")
|
TextButton::new("View")
|
||||||
.label("View")
|
.label("View")
|
||||||
.flush(true)
|
.flush(true)
|
||||||
.disabled(no_active_document)
|
|
||||||
.menu_list_children(vec![
|
.menu_list_children(vec![
|
||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("Tilt")
|
MenuListEntry::new("Tilt")
|
||||||
.label("Tilt")
|
.label("Tilt")
|
||||||
.icon("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())
|
.on_commit(|_| NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: true }.into())
|
||||||
.disabled(no_active_document || node_graph_open),
|
.disabled(no_active_document || node_graph_open),
|
||||||
MenuListEntry::new("Reset Tilt")
|
MenuListEntry::new("Reset Tilt")
|
||||||
.label("Reset Tilt")
|
.label("Reset Tilt")
|
||||||
.icon("TiltReset")
|
.icon("TiltReset")
|
||||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasTiltSet))
|
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasTiltSet))
|
||||||
.on_commit(|_| NavigationMessage::CanvasTiltSet { angle_radians: 0.into() }.into())
|
.on_commit(|_| NavigationMessage::CanvasTiltSet { angle_radians: 0.into() }.into())
|
||||||
.disabled(no_active_document || node_graph_open || !self.canvas_tilted),
|
.disabled(no_active_document || node_graph_open || !self.canvas_tilted),
|
||||||
],
|
],
|
||||||
@@ -569,37 +566,37 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Zoom In")
|
MenuListEntry::new("Zoom In")
|
||||||
.label("Zoom In")
|
.label("Zoom In")
|
||||||
.icon("ZoomIn")
|
.icon("ZoomIn")
|
||||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
||||||
.on_commit(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
.on_commit(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Zoom Out")
|
MenuListEntry::new("Zoom Out")
|
||||||
.label("Zoom Out")
|
.label("Zoom Out")
|
||||||
.icon("ZoomOut")
|
.icon("ZoomOut")
|
||||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
||||||
.on_commit(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
.on_commit(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Zoom to Selection")
|
MenuListEntry::new("Zoom to Selection")
|
||||||
.label("Zoom to Selection")
|
.label("Zoom to Selection")
|
||||||
.icon("FrameSelected")
|
.icon("FrameSelected")
|
||||||
.shortcut_keys(action_keys!(NavigationMessageDiscriminant::FitViewportToSelection))
|
.tooltip_shortcut(action_shortcut!(NavigationMessageDiscriminant::FitViewportToSelection))
|
||||||
.on_commit(|_| NavigationMessage::FitViewportToSelection.into())
|
.on_commit(|_| NavigationMessage::FitViewportToSelection.into())
|
||||||
.disabled(no_active_document || !has_selected_layers),
|
.disabled(no_active_document || !has_selected_layers),
|
||||||
MenuListEntry::new("Zoom to Fit")
|
MenuListEntry::new("Zoom to Fit")
|
||||||
.label("Zoom to Fit")
|
.label("Zoom to Fit")
|
||||||
.icon("FrameAll")
|
.icon("FrameAll")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasToFitAll))
|
||||||
.on_commit(|_| DocumentMessage::ZoomCanvasToFitAll.into())
|
.on_commit(|_| DocumentMessage::ZoomCanvasToFitAll.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Zoom to 100%")
|
MenuListEntry::new("Zoom to 100%")
|
||||||
.label("Zoom to 100%")
|
.label("Zoom to 100%")
|
||||||
.icon("Zoom1x")
|
.icon("Zoom1x")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent))
|
||||||
.on_commit(|_| DocumentMessage::ZoomCanvasTo100Percent.into())
|
.on_commit(|_| DocumentMessage::ZoomCanvasTo100Percent.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
MenuListEntry::new("Zoom to 200%")
|
MenuListEntry::new("Zoom to 200%")
|
||||||
.label("Zoom to 200%")
|
.label("Zoom to 200%")
|
||||||
.icon("Zoom2x")
|
.icon("Zoom2x")
|
||||||
.shortcut_keys(action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent))
|
.tooltip_shortcut(action_shortcut!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent))
|
||||||
.on_commit(|_| DocumentMessage::ZoomCanvasTo200Percent.into())
|
.on_commit(|_| DocumentMessage::ZoomCanvasTo200Percent.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -607,7 +604,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Flip")
|
MenuListEntry::new("Flip")
|
||||||
.label("Flip")
|
.label("Flip")
|
||||||
.icon(if self.canvas_flipped { "CheckboxChecked" } else { "CheckboxUnchecked" })
|
.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())
|
.on_commit(|_| NavigationMessage::CanvasFlip.into())
|
||||||
.disabled(no_active_document || node_graph_open),
|
.disabled(no_active_document || node_graph_open),
|
||||||
],
|
],
|
||||||
@@ -615,7 +612,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Rulers")
|
MenuListEntry::new("Rulers")
|
||||||
.label("Rulers")
|
.label("Rulers")
|
||||||
.icon(if self.rulers_visible { "CheckboxChecked" } else { "CheckboxUnchecked" })
|
.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())
|
.on_commit(|_| PortfolioMessage::ToggleRulers.into())
|
||||||
.disabled(no_active_document),
|
.disabled(no_active_document),
|
||||||
],
|
],
|
||||||
@@ -629,19 +626,19 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
MenuListEntry::new("Properties")
|
MenuListEntry::new("Properties")
|
||||||
.label("Properties")
|
.label("Properties")
|
||||||
.icon(if self.properties_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
|
.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()),
|
.on_commit(|_| PortfolioMessage::TogglePropertiesPanelOpen.into()),
|
||||||
MenuListEntry::new("Layers")
|
MenuListEntry::new("Layers")
|
||||||
.label("Layers")
|
.label("Layers")
|
||||||
.icon(if self.layers_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
|
.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()),
|
.on_commit(|_| PortfolioMessage::ToggleLayersPanelOpen.into()),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
MenuListEntry::new("Data")
|
MenuListEntry::new("Data")
|
||||||
.label("Data")
|
.label("Data")
|
||||||
.icon(if self.data_panel_open { "CheckboxChecked" } else { "CheckboxUnchecked" })
|
.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()),
|
.on_commit(|_| PortfolioMessage::ToggleDataPanelOpen.into()),
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -702,7 +699,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
"CheckboxChecked".to_string()
|
"CheckboxChecked".to_string()
|
||||||
}
|
}
|
||||||
}).unwrap_or_default())
|
}).unwrap_or_default())
|
||||||
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageOff))
|
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageOff))
|
||||||
.on_commit(|_| DebugMessage::MessageOff.into()),
|
.on_commit(|_| DebugMessage::MessageOff.into()),
|
||||||
MenuListEntry::new("Print Messages: Only Names")
|
MenuListEntry::new("Print Messages: Only Names")
|
||||||
.label("Print Messages: Only Names")
|
.label("Print Messages: Only Names")
|
||||||
@@ -716,7 +713,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
"CheckboxChecked".to_string()
|
"CheckboxChecked".to_string()
|
||||||
}
|
}
|
||||||
}).unwrap_or_default())
|
}).unwrap_or_default())
|
||||||
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageNames))
|
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageNames))
|
||||||
.on_commit(|_| DebugMessage::MessageNames.into()),
|
.on_commit(|_| DebugMessage::MessageNames.into()),
|
||||||
MenuListEntry::new("Print Messages: Full Contents")
|
MenuListEntry::new("Print Messages: Full Contents")
|
||||||
.label("Print Messages: Full Contents")
|
.label("Print Messages: Full Contents")
|
||||||
@@ -730,7 +727,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
"CheckboxChecked".to_string()
|
"CheckboxChecked".to_string()
|
||||||
}
|
}
|
||||||
}).unwrap_or_default())
|
}).unwrap_or_default())
|
||||||
.shortcut_keys(action_keys!(DebugMessageDiscriminant::MessageContents))
|
.tooltip_shortcut(action_shortcut!(DebugMessageDiscriminant::MessageContents))
|
||||||
.on_commit(|_| DebugMessage::MessageContents.into()),
|
.on_commit(|_| DebugMessage::MessageContents.into()),
|
||||||
],
|
],
|
||||||
vec![MenuListEntry::new("Trigger a Crash").label("Trigger a Crash").icon("Warning").on_commit(|_| panic!())],
|
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)>,
|
parent_and_insert_index: Option<(LayerNodeIdentifier, usize)>,
|
||||||
},
|
},
|
||||||
PrevDocument,
|
PrevDocument,
|
||||||
|
RequestWelcomeScreenButtonsLayout,
|
||||||
SetActivePanel {
|
SetActivePanel {
|
||||||
panel: PanelType,
|
panel: PanelType,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::messages::animation::TimingInformation;
|
|||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::dialog::simple_dialogs;
|
use crate::messages::dialog::simple_dialogs;
|
||||||
use crate::messages::frontend::utility_types::{DocumentDetails, OpenDocument};
|
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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::DocumentMessageContext;
|
use crate::messages::portfolio::document::DocumentMessageContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
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::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
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::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::messages::viewport::ToPhysical;
|
||||||
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
@@ -218,8 +219,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
responses.add(PropertiesPanelMessage::Clear);
|
responses.add(PropertiesPanelMessage::Clear);
|
||||||
responses.add(DocumentMessage::ClearLayersPanel);
|
responses.add(DocumentMessage::ClearLayersPanel);
|
||||||
responses.add(DataPanelMessage::ClearLayout);
|
responses.add(DataPanelMessage::ClearLayout);
|
||||||
let hint_data = HintData(vec![HintGroup(vec![])]);
|
HintData::clear_layout(responses);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for document_id in &self.document_ids {
|
for document_id in &self.document_ids {
|
||||||
@@ -243,8 +243,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
responses.add(PropertiesPanelMessage::Clear);
|
responses.add(PropertiesPanelMessage::Clear);
|
||||||
responses.add(DocumentMessage::ClearLayersPanel);
|
responses.add(DocumentMessage::ClearLayersPanel);
|
||||||
responses.add(DataPanelMessage::ClearLayout);
|
responses.add(DataPanelMessage::ClearLayout);
|
||||||
let hint_data = HintData(vec![HintGroup(vec![])]);
|
HintData::clear_layout(responses);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
// 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 });
|
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 } => {
|
PortfolioMessage::SetActivePanel { panel } => {
|
||||||
self.active_panel = panel;
|
self.active_panel = panel;
|
||||||
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
|
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ pub enum KeyboardPlatformLayout {
|
|||||||
pub enum PanelType {
|
pub enum PanelType {
|
||||||
#[default]
|
#[default]
|
||||||
Document,
|
Document,
|
||||||
|
Welcome,
|
||||||
Layers,
|
Layers,
|
||||||
Properties,
|
Properties,
|
||||||
DataPanel,
|
DataPanel,
|
||||||
@@ -50,6 +51,7 @@ impl From<String> for PanelType {
|
|||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
match value.as_str() {
|
match value.as_str() {
|
||||||
"Document" => PanelType::Document,
|
"Document" => PanelType::Document,
|
||||||
|
"Welcome" => PanelType::Welcome,
|
||||||
"Layers" => PanelType::Layers,
|
"Layers" => PanelType::Layers,
|
||||||
"Properties" => PanelType::Properties,
|
"Properties" => PanelType::Properties,
|
||||||
"Data" => PanelType::DataPanel,
|
"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::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::transform_layer::transform_layer_message_handler::TransformLayerMessageContext;
|
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 crate::node_graph_executor::NodeGraphExecutor;
|
||||||
use graphene_std::raster::color::Color;
|
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(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() });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: Default::default() });
|
||||||
|
|
||||||
self.tool_is_active = false;
|
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>) {
|
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()])]),
|
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>) {
|
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>) {
|
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()])]),
|
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>) {
|
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()])]),
|
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>) {
|
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>) {
|
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>) {
|
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,
|
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,
|
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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
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};
|
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
|
||||||
@@ -270,7 +270,7 @@ impl LayoutHolder for PathTool {
|
|||||||
.icon("Dot")
|
.icon("Dot")
|
||||||
.tooltip_label("Point Editing Mode")
|
.tooltip_label("Point Editing Mode")
|
||||||
.tooltip_description("To multi-select modes, perform the shortcut shown.")
|
.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())
|
.on_update(|_| PathToolMessage::TogglePointEditing.into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
let segment_editing_mode = CheckboxInput::new(self.options.path_editing_mode.segment_editing_mode)
|
let segment_editing_mode = CheckboxInput::new(self.options.path_editing_mode.segment_editing_mode)
|
||||||
@@ -278,7 +278,7 @@ impl LayoutHolder for PathTool {
|
|||||||
.icon("Remove")
|
.icon("Remove")
|
||||||
.tooltip_label("Segment Editing Mode")
|
.tooltip_label("Segment Editing Mode")
|
||||||
.tooltip_description("To multi-select modes, perform the shortcut shown.")
|
.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())
|
.on_update(|_| PathToolMessage::ToggleSegmentEditing.into())
|
||||||
.widget_holder();
|
.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()])]),
|
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>) {
|
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]),
|
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 => {
|
SelectToolFsmState::Dragging { axis, using_compass, has_dragged, .. } if *has_dragged => {
|
||||||
let mut hint_data = vec![
|
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")]));
|
hint_data.push(HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")]));
|
||||||
};
|
};
|
||||||
let hint_data = HintData(hint_data);
|
let hint_data = HintData(hint_data);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
hint_data.send_layout(responses);
|
||||||
}
|
}
|
||||||
SelectToolFsmState::Drawing { has_drawn, .. } if *has_drawn => {
|
SelectToolFsmState::Drawing { has_drawn, .. } if *has_drawn => {
|
||||||
let hint_data = HintData(vec![
|
let hint_data = HintData(vec![
|
||||||
@@ -1753,7 +1753,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
// TODO: (See https://discord.com/channels/731730685944922173/1216976541947531264/1321360311298818048)
|
// TODO: (See https://discord.com/channels/731730685944922173/1216976541947531264/1321360311298818048)
|
||||||
// HintGroup(vec![HintInfo::keys([Key::Shift], "Extend")])
|
// HintGroup(vec![HintInfo::keys([Key::Shift], "Extend")])
|
||||||
]);
|
]);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
hint_data.send_layout(responses);
|
||||||
}
|
}
|
||||||
SelectToolFsmState::Drawing { .. } | SelectToolFsmState::Dragging { .. } => {}
|
SelectToolFsmState::Drawing { .. } | SelectToolFsmState::Dragging { .. } => {}
|
||||||
SelectToolFsmState::ResizingBounds => {
|
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::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")]),
|
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 => {
|
SelectToolFsmState::RotatingBounds => {
|
||||||
let hint_data = HintData(vec![
|
let hint_data = HintData(vec![
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
|
HintGroup(vec![HintInfo::keys([Key::Shift], "15° Increments")]),
|
||||||
]);
|
]);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
hint_data.send_layout(responses);
|
||||||
}
|
}
|
||||||
SelectToolFsmState::SkewingBounds { .. } => {
|
SelectToolFsmState::SkewingBounds { .. } => {
|
||||||
let hint_data = HintData(vec![
|
let hint_data = HintData(vec![
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
HintGroup(vec![HintInfo::keys([Key::Control], "Unlock Slide")]),
|
HintGroup(vec![HintInfo::keys([Key::Control], "Unlock Slide")]),
|
||||||
]);
|
]);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
hint_data.send_layout(responses);
|
||||||
}
|
}
|
||||||
SelectToolFsmState::DraggingPivot => {
|
SelectToolFsmState::DraggingPivot => {
|
||||||
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
|
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()])]),
|
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![]),
|
SplineToolFsmState::MergingEndpoints => HintData(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
hint_data.send_layout(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
|
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>) {
|
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use super::common_functionality::shape_editor::ShapeState;
|
|||||||
use super::tool_messages::*;
|
use super::tool_messages::*;
|
||||||
use crate::messages::broadcast::BroadcastMessage;
|
use crate::messages::broadcast::BroadcastMessage;
|
||||||
use crate::messages::broadcast::event::EventMessage;
|
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::input_keyboard::{Key, KeysGroup, LabeledKeyOrMouseMotion, LabeledShortcut, MouseMotion};
|
||||||
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::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::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
|
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
|
||||||
use crate::messages::preferences::PreferencesMessageHandler;
|
use crate::messages::preferences::PreferencesMessageHandler;
|
||||||
@@ -125,12 +125,12 @@ impl DocumentToolData {
|
|||||||
widgets: vec![
|
widgets: vec![
|
||||||
IconButton::new("SwapVertical", 16)
|
IconButton::new("SwapVertical", 16)
|
||||||
.tooltip_label("Swap")
|
.tooltip_label("Swap")
|
||||||
.shortcut_keys(action_keys!(ToolMessageDiscriminant::SwapColors))
|
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::SwapColors))
|
||||||
.on_update(|_| ToolMessage::SwapColors.into())
|
.on_update(|_| ToolMessage::SwapColors.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("WorkingColors", 16)
|
IconButton::new("WorkingColors", 16)
|
||||||
.tooltip_label("Reset")
|
.tooltip_label("Reset")
|
||||||
.shortcut_keys(action_keys!(ToolMessageDiscriminant::ResetColors))
|
.tooltip_shortcut(action_shortcut!(ToolMessageDiscriminant::ResetColors))
|
||||||
.on_update(|_| ToolMessage::ResetColors.into())
|
.on_update(|_| ToolMessage::ResetColors.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
],
|
],
|
||||||
@@ -245,12 +245,12 @@ impl LayoutHolder for ToolData {
|
|||||||
ToolAvailability::Available(tool) =>
|
ToolAvailability::Available(tool) =>
|
||||||
ToolEntry::new(tool.tool_type(), tool.icon_name())
|
ToolEntry::new(tool.tool_type(), tool.icon_name())
|
||||||
.tooltip_label(tool.tooltip_label())
|
.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) =>
|
ToolAvailability::AvailableAsShape(shape) =>
|
||||||
ToolEntry::new(shape.tool_type(), shape.icon_name())
|
ToolEntry::new(shape.tool_type(), shape.icon_name())
|
||||||
.tooltip_label(shape.tooltip_label())
|
.tooltip_label(shape.tooltip_label())
|
||||||
.tooltip_description(shape.tooltip_description())
|
.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(),
|
ToolAvailability::ComingSoon(tool) => tool.clone(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -258,7 +258,7 @@ impl LayoutHolder for ToolData {
|
|||||||
)
|
)
|
||||||
.flat_map(|group| {
|
.flat_map(|group| {
|
||||||
let separator = std::iter::once(Separator::new(SeparatorType::Section).direction(SeparatorDirection::Vertical).widget_holder());
|
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.");
|
let coming_soon = tooltip_description.contains("Coming soon.");
|
||||||
|
|
||||||
IconButton::new(icon_name, 32)
|
IconButton::new(icon_name, 32)
|
||||||
@@ -270,7 +270,6 @@ impl LayoutHolder for ToolData {
|
|||||||
.tooltip_label(tooltip_label.clone())
|
.tooltip_label(tooltip_label.clone())
|
||||||
.tooltip_description(tooltip_description)
|
.tooltip_description(tooltip_description)
|
||||||
.tooltip_shortcut(tooltip_shortcut)
|
.tooltip_shortcut(tooltip_shortcut)
|
||||||
.shortcut_keys(shortcut_keys)
|
|
||||||
.on_update(move |_| {
|
.on_update(move |_| {
|
||||||
match tool_type {
|
match tool_type {
|
||||||
ToolType::Line => ToolMessage::ActivateToolShapeLine.into(),
|
ToolType::Line => ToolMessage::ActivateToolShapeLine.into(),
|
||||||
@@ -306,8 +305,7 @@ pub struct ToolEntry {
|
|||||||
pub icon_name: String,
|
pub icon_name: String,
|
||||||
pub tooltip_label: String,
|
pub tooltip_label: String,
|
||||||
pub tooltip_description: String,
|
pub tooltip_description: String,
|
||||||
pub tooltip_shortcut: String,
|
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||||
pub shortcut_keys: Option<ActionKeys>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -429,26 +427,26 @@ fn list_tools_in_groups() -> Vec<Vec<ToolAvailability>> {
|
|||||||
ToolEntry::new(ToolType::Heal, "RasterHealTool")
|
ToolEntry::new(ToolType::Heal, "RasterHealTool")
|
||||||
.tooltip_label("Heal Tool")
|
.tooltip_label("Heal Tool")
|
||||||
.tooltip_description("Coming soon.")
|
.tooltip_description("Coming soon.")
|
||||||
.tooltip_shortcut(Key::KeyJ.to_string()),
|
.tooltip_shortcut(action_shortcut_manual!(Key::KeyJ)),
|
||||||
),
|
),
|
||||||
ToolAvailability::ComingSoon(
|
ToolAvailability::ComingSoon(
|
||||||
ToolEntry::new(ToolType::Clone, "RasterCloneTool")
|
ToolEntry::new(ToolType::Clone, "RasterCloneTool")
|
||||||
.tooltip_label("Clone Tool")
|
.tooltip_label("Clone Tool")
|
||||||
.tooltip_description("Coming soon.")
|
.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::Patch, "RasterPatchTool").tooltip_label("Patch Tool").tooltip_description("Coming soon.")),
|
||||||
ToolAvailability::ComingSoon(
|
ToolAvailability::ComingSoon(
|
||||||
ToolEntry::new(ToolType::Detail, "RasterDetailTool")
|
ToolEntry::new(ToolType::Detail, "RasterDetailTool")
|
||||||
.tooltip_label("Detail Tool")
|
.tooltip_label("Detail Tool")
|
||||||
.tooltip_description("Coming soon.")
|
.tooltip_description("Coming soon.")
|
||||||
.tooltip_shortcut(Key::KeyD.to_string()),
|
.tooltip_shortcut(action_shortcut_manual!(Key::KeyD)),
|
||||||
),
|
),
|
||||||
ToolAvailability::ComingSoon(
|
ToolAvailability::ComingSoon(
|
||||||
ToolEntry::new(ToolType::Relight, "RasterRelightTool")
|
ToolEntry::new(ToolType::Relight, "RasterRelightTool")
|
||||||
.tooltip_label("Relight Tool")
|
.tooltip_label("Relight Tool")
|
||||||
.tooltip_description("Coming soon.")
|
.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)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct HintData(pub Vec<HintGroup>);
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct HintGroup(pub Vec<HintInfo>);
|
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).
|
/// 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).
|
/// 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")]
|
#[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.
|
/// `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")]
|
#[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.
|
/// 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.
|
/// No such icon is shown if `None` is given, and it can be combined with `key_groups` if desired.
|
||||||
pub mouse: Option<MouseMotion>,
|
pub mouse: Option<MouseMotion>,
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
|
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
|
||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
||||||
|
import ShortcutLabel from "@graphite/components/widgets/labels/ShortcutLabel.svelte";
|
||||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
import UserInputLabel from "@graphite/components/widgets/labels/UserInputLabel.svelte";
|
|
||||||
|
|
||||||
let self: FloatingMenu | undefined;
|
let self: FloatingMenu | undefined;
|
||||||
let scroller: LayoutCol | undefined;
|
let scroller: LayoutCol | undefined;
|
||||||
@@ -447,8 +447,8 @@
|
|||||||
|
|
||||||
<TextLabel class="entry-label" styles={{ "font-family": `${!entry.font ? "inherit" : entry.value}` }}>{entry.label}</TextLabel>
|
<TextLabel class="entry-label" styles={{ "font-family": `${!entry.font ? "inherit" : entry.value}` }}>{entry.label}</TextLabel>
|
||||||
|
|
||||||
{#if entry.shortcutKeys?.keys.length}
|
{#if entry.tooltipShortcut?.shortcut.length}
|
||||||
<UserInputLabel keysWithLabelsGroups={[entry.shortcutKeys.keys]} requiresLock={entry.shortcutRequiresLock} textOnly={true} />
|
<ShortcutLabel shortcut={entry.tooltipShortcut} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if entry.children?.length}
|
{#if entry.children?.length}
|
||||||
@@ -499,7 +499,7 @@
|
|||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
background: var(--color-4-dimgray);
|
background: var(--color-3-darkgray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +535,7 @@
|
|||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-input-label {
|
.shortcut-label {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
|
import type { LabeledShortcut } from "@graphite/messages";
|
||||||
import type { TooltipState } from "@graphite/state-providers/tooltip";
|
import type { TooltipState } from "@graphite/state-providers/tooltip";
|
||||||
|
|
||||||
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
|
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
|
import ShortcutLabel from "@graphite/components/widgets/labels/ShortcutLabel.svelte";
|
||||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
|
|
||||||
const tooltip = getContext<TooltipState>("tooltip");
|
const tooltip = getContext<TooltipState>("tooltip");
|
||||||
@@ -15,7 +17,15 @@
|
|||||||
|
|
||||||
$: label = filterTodo($tooltip.element?.getAttribute("data-tooltip-label")?.trim());
|
$: label = filterTodo($tooltip.element?.getAttribute("data-tooltip-label")?.trim());
|
||||||
$: description = filterTodo($tooltip.element?.getAttribute("data-tooltip-description")?.trim());
|
$: description = filterTodo($tooltip.element?.getAttribute("data-tooltip-description")?.trim());
|
||||||
$: shortcut = filterTodo($tooltip.element?.getAttribute("data-tooltip-shortcut")?.trim());
|
$: shortcutJSON = $tooltip.element?.getAttribute("data-tooltip-shortcut")?.trim();
|
||||||
|
$: shortcut = ((shortcutJSON) => {
|
||||||
|
if (!shortcutJSON) return undefined;
|
||||||
|
try {
|
||||||
|
return JSON.parse(shortcutJSON) as LabeledShortcut;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})(shortcutJSON);
|
||||||
|
|
||||||
// TODO: Once all TODOs are replaced with real text, remove this function
|
// TODO: Once all TODOs are replaced with real text, remove this function
|
||||||
function filterTodo(text: string | undefined): string | undefined {
|
function filterTodo(text: string | undefined): string | undefined {
|
||||||
@@ -24,8 +34,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tooltip" style:top={`${$tooltip.position.y}px`} style:left={`${$tooltip.position.x}px`}>
|
{#if label || description}
|
||||||
{#if label || description}
|
<div class="tooltip" style:top={`${$tooltip.position.y}px`} style:left={`${$tooltip.position.x}px`}>
|
||||||
<FloatingMenu open={true} type="Tooltip" direction="Bottom" bind:this={self}>
|
<FloatingMenu open={true} type="Tooltip" direction="Bottom" bind:this={self}>
|
||||||
{#if label || shortcut}
|
{#if label || shortcut}
|
||||||
<LayoutRow class="tooltip-header">
|
<LayoutRow class="tooltip-header">
|
||||||
@@ -33,7 +43,7 @@
|
|||||||
<TextLabel class="tooltip-label">{label}</TextLabel>
|
<TextLabel class="tooltip-label">{label}</TextLabel>
|
||||||
{/if}
|
{/if}
|
||||||
{#if shortcut}
|
{#if shortcut}
|
||||||
<TextLabel class="tooltip-shortcut">{shortcut}</TextLabel>
|
<ShortcutLabel shortcut={{ shortcut }} />
|
||||||
{/if}
|
{/if}
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -41,8 +51,8 @@
|
|||||||
<TextLabel class="tooltip-description">{description}</TextLabel>
|
<TextLabel class="tooltip-description">{description}</TextLabel>
|
||||||
{/if}
|
{/if}
|
||||||
</FloatingMenu>
|
</FloatingMenu>
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
.tooltip {
|
.tooltip {
|
||||||
@@ -62,17 +72,10 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-label + .tooltip-shortcut {
|
.text-label + .shortcut-label {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-shortcut {
|
|
||||||
color: var(--color-b-lightgray);
|
|
||||||
background: var(--color-3-darkgray);
|
|
||||||
padding: 0 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-description {
|
.tooltip-description {
|
||||||
color: var(--color-b-lightgray);
|
color: var(--color-b-lightgray);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -519,7 +519,7 @@
|
|||||||
.floating-menu-content {
|
.floating-menu-content {
|
||||||
background: var(--color-2-mildblack);
|
background: var(--color-2-mildblack);
|
||||||
box-shadow: rgba(var(--color-0-black-rgb), 0.5) 0 2px 4px;
|
box-shadow: rgba(var(--color-0-black-rgb), 0.5) 0 2px 4px;
|
||||||
border: 1px solid var(--color-4-dimgray);
|
border: 1px solid var(--color-3-darkgray);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: var(--color-e-nearwhite);
|
color: var(--color-e-nearwhite);
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
@@ -615,7 +615,7 @@
|
|||||||
&.top .tail,
|
&.top .tail,
|
||||||
&.topleft .tail,
|
&.topleft .tail,
|
||||||
&.topright .tail {
|
&.topright .tail {
|
||||||
border-color: var(--color-4-dimgray) transparent transparent transparent;
|
border-color: var(--color-3-darkgray) transparent transparent transparent;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
border-color: var(--color-2-mildblack) transparent transparent transparent;
|
border-color: var(--color-2-mildblack) transparent transparent transparent;
|
||||||
@@ -633,7 +633,7 @@
|
|||||||
&.bottom .tail,
|
&.bottom .tail,
|
||||||
&.bottomleft .tail,
|
&.bottomleft .tail,
|
||||||
&.bottomright .tail {
|
&.bottomright .tail {
|
||||||
border-color: transparent transparent var(--color-4-dimgray) transparent;
|
border-color: transparent transparent var(--color-3-darkgray) transparent;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
border-color: transparent transparent var(--color-2-mildblack) transparent;
|
border-color: transparent transparent var(--color-2-mildblack) transparent;
|
||||||
@@ -649,7 +649,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.left .tail {
|
&.left .tail {
|
||||||
border-color: transparent transparent transparent var(--color-4-dimgray);
|
border-color: transparent transparent transparent var(--color-3-darkgray);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
border-color: transparent transparent transparent var(--color-2-mildblack);
|
border-color: transparent transparent transparent var(--color-2-mildblack);
|
||||||
@@ -665,7 +665,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.right .tail {
|
&.right .tail {
|
||||||
border-color: transparent var(--color-4-dimgray) transparent transparent;
|
border-color: transparent var(--color-3-darkgray) transparent transparent;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
border-color: transparent var(--color-2-mildblack) transparent transparent;
|
border-color: transparent var(--color-2-mildblack) transparent transparent;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
let className = "";
|
let className = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
@@ -7,7 +9,7 @@
|
|||||||
export let styles: Record<string, string | number | undefined> = {};
|
export let styles: Record<string, string | number | undefined> = {};
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// TODO: Add middle-click drag scrolling
|
// TODO: Add middle-click drag scrolling
|
||||||
export let scrollableX = false;
|
export let scrollableX = false;
|
||||||
export let scrollableY = false;
|
export let scrollableY = false;
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
<div
|
<div
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
data-scrollable-x={scrollableX ? "" : undefined}
|
data-scrollable-x={scrollableX ? "" : undefined}
|
||||||
data-scrollable-y={scrollableY ? "" : undefined}
|
data-scrollable-y={scrollableY ? "" : undefined}
|
||||||
class={`layout-col ${className} ${extraClasses}`.trim()}
|
class={`layout-col ${className} ${extraClasses}`.trim()}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
let className = "";
|
let className = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
@@ -7,7 +9,7 @@
|
|||||||
export let styles: Record<string, string | number | undefined> = {};
|
export let styles: Record<string, string | number | undefined> = {};
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// TODO: Add middle-click drag scrolling
|
// TODO: Add middle-click drag scrolling
|
||||||
export let scrollableX = false;
|
export let scrollableX = false;
|
||||||
export let scrollableY = false;
|
export let scrollableY = false;
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
<div
|
<div
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
data-scrollable-x={scrollableX ? "" : undefined}
|
data-scrollable-x={scrollableX ? "" : undefined}
|
||||||
data-scrollable-y={scrollableY ? "" : undefined}
|
data-scrollable-y={scrollableY ? "" : undefined}
|
||||||
class={`layout-row ${className} ${extraClasses}`.trim()}
|
class={`layout-row ${className} ${extraClasses}`.trim()}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, onMount, onDestroy, tick } from "svelte";
|
import { getContext, onMount, onDestroy, tick } from "svelte";
|
||||||
|
|
||||||
|
import { shortcutAltClick } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import {
|
import {
|
||||||
defaultWidgetLayout,
|
defaultWidgetLayout,
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
UpdateLayersPanelControlBarRightLayout,
|
UpdateLayersPanelControlBarRightLayout,
|
||||||
UpdateLayersPanelBottomBarLayout,
|
UpdateLayersPanelBottomBarLayout,
|
||||||
} from "@graphite/messages";
|
} from "@graphite/messages";
|
||||||
import type { DataBuffer, LayerPanelEntry } from "@graphite/messages";
|
import type { ActionShortcut, DataBuffer, LayerPanelEntry } from "@graphite/messages";
|
||||||
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
|
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
|
||||||
import { operatingSystem } from "@graphite/utility-functions/platform";
|
import { operatingSystem } from "@graphite/utility-functions/platform";
|
||||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||||
@@ -73,6 +74,8 @@
|
|||||||
let layersPanelControlBarRightLayout = defaultWidgetLayout();
|
let layersPanelControlBarRightLayout = defaultWidgetLayout();
|
||||||
let layersPanelBottomBarLayout = defaultWidgetLayout();
|
let layersPanelBottomBarLayout = defaultWidgetLayout();
|
||||||
|
|
||||||
|
const altClickKeys: ActionShortcut = shortcutAltClick();
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => {
|
editor.subscriptions.subscribeJsMessage(UpdateLayersPanelControlBarLeftLayout, (updateLayersPanelControlBarLeftLayout) => {
|
||||||
patchWidgetLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
|
patchWidgetLayout(layersPanelControlBarLeftLayout, updateLayersPanelControlBarLeftLayout);
|
||||||
@@ -622,7 +625,7 @@
|
|||||||
? "Hide the layers nested within. (To affect all open descendants, perform the shortcut shown.)"
|
? "Hide the layers nested within. (To affect all open descendants, perform the shortcut shown.)"
|
||||||
: "Show the layers nested within. (To affect all closed descendants, perform the shortcut shown.)") +
|
: "Show the layers nested within. (To affect all closed descendants, perform the shortcut shown.)") +
|
||||||
(listing.entry.ancestorOfSelected && !listing.entry.expanded ? "\n\nNote: a selected layer is currently contained within.\n" : "")}
|
(listing.entry.ancestorOfSelected && !listing.entry.expanded ? "\n\nNote: a selected layer is currently contained within.\n" : "")}
|
||||||
data-tooltip-shortcut="Alt Click"
|
data-tooltip-shortcut={altClickKeys?.shortcut ? JSON.stringify(altClickKeys.shortcut) : undefined}
|
||||||
on:click={(e) => handleExpandArrowClickWithModifiers(e, listing.entry.id)}
|
on:click={(e) => handleExpandArrowClickWithModifiers(e, listing.entry.id)}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
></button>
|
></button>
|
||||||
@@ -634,7 +637,7 @@
|
|||||||
icon="Clipped"
|
icon="Clipped"
|
||||||
class="clipped-arrow"
|
class="clipped-arrow"
|
||||||
tooltipDescription="Clipping mask is active. To release it, perform the shortcut on the layer border."
|
tooltipDescription="Clipping mask is active. To release it, perform the shortcut on the layer border."
|
||||||
tooltipShortcut="Alt Click"
|
tooltipShortcut={altClickKeys}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getContext, onMount, onDestroy } from "svelte";
|
||||||
|
|
||||||
|
import type { Editor } from "@graphite/editor";
|
||||||
|
import { defaultWidgetLayout, patchWidgetLayout, UpdateWelcomeScreenButtonsLayout } from "@graphite/messages";
|
||||||
|
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||||
|
|
||||||
|
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||||
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
|
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
|
||||||
|
|
||||||
|
const editor = getContext<Editor>("editor");
|
||||||
|
|
||||||
|
let welcomePanelButtonsLayout = defaultWidgetLayout();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateWelcomeScreenButtonsLayout, (updateWelcomeScreenButtonsLayout) => {
|
||||||
|
patchWidgetLayout(welcomePanelButtonsLayout, updateWelcomeScreenButtonsLayout);
|
||||||
|
welcomePanelButtonsLayout = welcomePanelButtonsLayout;
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.handle.requestWelcomeScreenButtonsLayout();
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
editor.subscriptions.unsubscribeJsMessage(UpdateWelcomeScreenButtonsLayout);
|
||||||
|
});
|
||||||
|
|
||||||
|
function dropFile(e: DragEvent) {
|
||||||
|
if (!e.dataTransfer) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
Array.from(e.dataTransfer.items).forEach(async (item) => {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
if (file.type.includes("svg")) {
|
||||||
|
const svgData = await file.text();
|
||||||
|
editor.handle.pasteSvg(file.name, svgData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.type.startsWith("image")) {
|
||||||
|
const imageData = await extractPixelData(file);
|
||||||
|
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphiteFileSuffix = "." + editor.handle.fileExtension();
|
||||||
|
if (file.name.endsWith(graphiteFileSuffix)) {
|
||||||
|
const content = await file.text();
|
||||||
|
const documentName = file.name.slice(0, -graphiteFileSuffix.length);
|
||||||
|
editor.handle.openDocumentFile(documentName, content);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<LayoutCol class="welcome-panel" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}>
|
||||||
|
<LayoutCol class="top-spacer"></LayoutCol>
|
||||||
|
<LayoutCol class="content-container">
|
||||||
|
<LayoutCol class="content">
|
||||||
|
<LayoutRow class="logotype">
|
||||||
|
<IconLabel icon="GraphiteLogotypeSolid" />
|
||||||
|
</LayoutRow>
|
||||||
|
<LayoutRow class="actions">
|
||||||
|
<WidgetLayout layout={welcomePanelButtonsLayout} />
|
||||||
|
</LayoutRow>
|
||||||
|
</LayoutCol>
|
||||||
|
</LayoutCol>
|
||||||
|
<LayoutCol class="bottom-message">
|
||||||
|
{#if new Date().getFullYear() === 2025}
|
||||||
|
<TextLabel italic={true} disabled={true}>
|
||||||
|
September 2025 release — <a href="https://youtube.com/watch?v=Vl5BA4g3QXM" target="_blank">What's new? (video)</a>
|
||||||
|
— Note: some older documents may render differently and require manual fixes.
|
||||||
|
<a href="https://ec6796b4.graphite-editor.pages.dev/" target="_blank">Need the old version?</a>
|
||||||
|
</TextLabel>
|
||||||
|
{/if}
|
||||||
|
</LayoutCol>
|
||||||
|
</LayoutCol>
|
||||||
|
|
||||||
|
<style lang="scss" global>
|
||||||
|
.welcome-panel {
|
||||||
|
background: var(--color-2-mildblack);
|
||||||
|
margin: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.logotype {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: auto;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 8px;
|
||||||
|
margin: -8px;
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-spacer {
|
||||||
|
flex: 0 1 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-message {
|
||||||
|
flex: 0 0 48px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
.text-label {
|
||||||
|
white-space: wrap;
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -588,10 +588,11 @@
|
|||||||
data-visibility-button
|
data-visibility-button
|
||||||
size={24}
|
size={24}
|
||||||
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
||||||
|
hoverIcon={node.visible ? "EyeHide" : "EyeShow"}
|
||||||
action={() => {
|
action={() => {
|
||||||
/* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */
|
/* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */
|
||||||
}}
|
}}
|
||||||
tooltipLabel={node.visible ? "Visible" : "Hidden"}
|
tooltipLabel={node.visible ? "Hide" : "Show"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<svg class="border-mask" width="0" height="0">
|
<svg class="border-mask" width="0" height="0">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
{:else if isWidgetSection(layoutGroup)}
|
{:else if isWidgetSection(layoutGroup)}
|
||||||
<WidgetSection widgetData={layoutGroup} layoutTarget={layout.layoutTarget} class={className} {classes} />
|
<WidgetSection widgetData={layoutGroup} layoutTarget={layout.layoutTarget} class={className} {classes} />
|
||||||
{:else if isWidgetTable(layoutGroup)}
|
{:else if isWidgetTable(layoutGroup)}
|
||||||
<WidgetTable widgetData={layoutGroup} layoutTarget={layout.layoutTarget} />
|
<WidgetTable widgetData={layoutGroup} unstyled={layoutGroup.unstyled} layoutTarget={layout.layoutTarget} />
|
||||||
{:else}
|
{:else}
|
||||||
<TextLabel styles={{ color: "#d6536e" }}>Error: The widget layout that belongs here has an invalid layout group type</TextLabel>
|
<TextLabel styles={{ color: "#d6536e" }}>Error: The widget layout that belongs here has an invalid layout group type</TextLabel>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
import ImageLabel from "@graphite/components/widgets/labels/ImageLabel.svelte";
|
import ImageLabel from "@graphite/components/widgets/labels/ImageLabel.svelte";
|
||||||
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
||||||
|
import ShortcutLabel from "@graphite/components/widgets/labels/ShortcutLabel.svelte";
|
||||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
|
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
|
||||||
|
|
||||||
@@ -127,6 +128,11 @@
|
|||||||
{#if iconLabel}
|
{#if iconLabel}
|
||||||
<IconLabel {...exclude(iconLabel)} />
|
<IconLabel {...exclude(iconLabel)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
{@const shortcutLabel = narrowWidgetProps(component.props, "ShortcutLabel")}
|
||||||
|
{@const shortcutLabelShortcut = shortcutLabel?.shortcut ? { ...shortcutLabel, shortcut: shortcutLabel.shortcut } : undefined}
|
||||||
|
{#if shortcutLabel && shortcutLabelShortcut}
|
||||||
|
<ShortcutLabel {...exclude(shortcutLabelShortcut)} />
|
||||||
|
{/if}
|
||||||
{@const imageLabel = narrowWidgetProps(component.props, "ImageLabel")}
|
{@const imageLabel = narrowWidgetProps(component.props, "ImageLabel")}
|
||||||
{#if imageLabel}
|
{#if imageLabel}
|
||||||
<ImageLabel {...exclude(imageLabel)} />
|
<ImageLabel {...exclude(imageLabel)} />
|
||||||
|
|||||||
@@ -5,15 +5,18 @@
|
|||||||
|
|
||||||
export let widgetData: WidgetTableFromJsMessages;
|
export let widgetData: WidgetTableFromJsMessages;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export let layoutTarget: any; // TODO: Give this a real type
|
export let layoutTarget: any;
|
||||||
|
export let unstyled = false;
|
||||||
|
|
||||||
|
$: columns = widgetData.tableWidgets.length > 0 ? widgetData.tableWidgets[0].length : 0;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table>
|
<table class:unstyled>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each widgetData.tableWidgets as row}
|
{#each widgetData.tableWidgets as row}
|
||||||
<tr>
|
<tr>
|
||||||
{#each row as cell}
|
{#each row as cell}
|
||||||
<td>
|
<td colspan={row.length < columns ? columns - row.length + 1 : undefined}>
|
||||||
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} narrow={true} />
|
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} narrow={true} />
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -23,7 +26,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
table {
|
table:not(.unstyled) {
|
||||||
background: var(--color-3-darkgray);
|
background: var(--color-3-darkgray);
|
||||||
border: none;
|
border: none;
|
||||||
border-spacing: 4px;
|
border-spacing: 4px;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
||||||
|
|
||||||
@@ -6,7 +8,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// Callbacks
|
// Callbacks
|
||||||
export let action: (index: number) => void;
|
export let action: (index: number) => void;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type IconName, type IconSize } from "@graphite/icons";
|
import { type IconName, type IconSize } from "@graphite/icons";
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
export let active = false;
|
export let active = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// Callbacks
|
// Callbacks
|
||||||
export let action: (e?: MouseEvent) => void;
|
export let action: (e?: MouseEvent) => void;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
{disabled}
|
{disabled}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
tabindex={active ? -1 : 0}
|
tabindex={active ? -1 : 0}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
import { IMAGE_BASE64_STRINGS } from "@graphite/utility-functions/images";
|
import { IMAGE_BASE64_STRINGS } from "@graphite/utility-functions/images";
|
||||||
|
|
||||||
let className = "";
|
let className = "";
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
export let height: string | undefined;
|
export let height: string | undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// Callbacks
|
// Callbacks
|
||||||
export let action: (e?: MouseEvent) => void;
|
export let action: (e?: MouseEvent) => void;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
class={`image-button ${className} ${extraClasses}`.trim()}
|
class={`image-button ${className} ${extraClasses}`.trim()}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
alt=""
|
alt=""
|
||||||
on:click={action}
|
on:click={action}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { FrontendGraphDataType } from "@graphite/messages";
|
import type { FrontendGraphDataType, ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
export let dataType: FrontendGraphDataType;
|
export let dataType: FrontendGraphDataType;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
// Callbacks
|
// Callbacks
|
||||||
export let action: (e?: MouseEvent) => void;
|
export let action: (e?: MouseEvent) => void;
|
||||||
</script>
|
</script>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
on:click={action}
|
on:click={action}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
{#if !exposed}
|
{#if !exposed}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type IconName, type PopoverButtonStyle } from "@graphite/icons";
|
import { type IconName, type PopoverButtonStyle } from "@graphite/icons";
|
||||||
|
|
||||||
import type { MenuDirection } from "@graphite/messages";
|
import type { MenuDirection, ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
|
import FloatingMenu from "@graphite/components/layout/FloatingMenu.svelte";
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
export let icon: IconName | undefined = undefined;
|
export let icon: IconName | undefined = undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let popoverMinWidth = 1;
|
export let popoverMinWidth = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { IconName } from "@graphite/icons";
|
import type { IconName } from "@graphite/icons";
|
||||||
import type { MenuListEntry } from "@graphite/messages";
|
import type { MenuListEntry, ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
||||||
import ConditionalWrapper from "@graphite/components/layout/ConditionalWrapper.svelte";
|
import ConditionalWrapper from "@graphite/components/layout/ConditionalWrapper.svelte";
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
export let narrow = false;
|
export let narrow = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let menuListChildren: MenuListEntry[][] | undefined = undefined;
|
export let menuListChildren: MenuListEntry[][] | undefined = undefined;
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
style:min-width={minWidth > 0 ? `${minWidth}px` : undefined}
|
style:min-width={minWidth > 0 ? `${minWidth}px` : undefined}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
data-emphasized={emphasized || undefined}
|
data-emphasized={emphasized || undefined}
|
||||||
data-disabled={disabled || undefined}
|
data-disabled={disabled || undefined}
|
||||||
data-text-button
|
data-text-button
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { IconName } from "@graphite/icons";
|
import type { IconName } from "@graphite/icons";
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
export let icon: IconName = "Checkmark";
|
export let icon: IconName = "Checkmark";
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let forLabel: bigint | undefined = undefined;
|
export let forLabel: bigint | undefined = undefined;
|
||||||
|
|
||||||
let inputElement: HTMLInputElement | undefined;
|
let inputElement: HTMLInputElement | undefined;
|
||||||
@@ -55,7 +56,7 @@
|
|||||||
on:keydown={(e) => e.key === "Enter" && toggleCheckboxFromLabel(e)}
|
on:keydown={(e) => e.key === "Enter" && toggleCheckboxFromLabel(e)}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
>
|
>
|
||||||
<LayoutRow class="checkbox-box">
|
<LayoutRow class="checkbox-box">
|
||||||
<IconLabel icon={displayIcon} />
|
<IconLabel icon={displayIcon} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { FillChoice, MenuDirection } from "@graphite/messages";
|
import type { FillChoice, MenuDirection, ActionShortcut } from "@graphite/messages";
|
||||||
import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages";
|
import { Color, contrastingOutlineFactor, Gradient } from "@graphite/messages";
|
||||||
|
|
||||||
import ColorPicker from "@graphite/components/floating-menus/ColorPicker.svelte";
|
import ColorPicker from "@graphite/components/floating-menus/ColorPicker.svelte";
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
// export let allowTransparency = false; // TODO: Implement
|
// export let allowTransparency = false; // TODO: Implement
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
$: outlineFactor = contrastingOutlineFactor(value, ["--color-1-nearblack", "--color-3-darkgray"], 0.01);
|
$: outlineFactor = contrastingOutlineFactor(value, ["--color-1-nearblack", "--color-3-darkgray"], 0.01);
|
||||||
$: outlined = outlineFactor > 0.0001;
|
$: outlined = outlineFactor > 0.0001;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { Curve, CurveManipulatorGroup } from "@graphite/messages";
|
import type { Curve, CurveManipulatorGroup, ActionShortcut } from "@graphite/messages";
|
||||||
import { clamp } from "@graphite/utility-functions/math";
|
import { clamp } from "@graphite/utility-functions/math";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
const GRID_SIZE = 4;
|
const GRID_SIZE = 4;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { MenuListEntry } from "@graphite/messages";
|
import type { MenuListEntry, ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
export let narrow = false;
|
export let narrow = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let minWidth = 0;
|
export let minWidth = 0;
|
||||||
export let maxWidth = 0;
|
export let maxWidth = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
import { operatingSystem } from "@graphite/utility-functions/platform";
|
import { operatingSystem } from "@graphite/utility-functions/platform";
|
||||||
|
|
||||||
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
|
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
|
||||||
@@ -27,7 +28,7 @@
|
|||||||
export let textarea = false;
|
export let textarea = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let placeholder: string | undefined = undefined;
|
export let placeholder: string | undefined = undefined;
|
||||||
export let hideContextMenu = false;
|
export let hideContextMenu = false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, getContext, onMount, tick } from "svelte";
|
import { createEventDispatcher, getContext, onMount, tick } from "svelte";
|
||||||
|
|
||||||
import type { MenuListEntry } from "@graphite/messages";
|
import type { MenuListEntry, ActionShortcut } from "@graphite/messages";
|
||||||
import type { FontsState } from "@graphite/state-providers/fonts";
|
import type { FontsState } from "@graphite/state-providers/fonts";
|
||||||
|
|
||||||
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
import MenuList from "@graphite/components/floating-menus/MenuList.svelte";
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
let open = false;
|
let open = false;
|
||||||
let entries: MenuListEntry[] = [];
|
let entries: MenuListEntry[] = [];
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, onMount, onDestroy } from "svelte";
|
import { createEventDispatcher, onMount, onDestroy } from "svelte";
|
||||||
|
|
||||||
import { evaluateMathExpression } from "@graphite/../wasm/pkg/graphite_wasm.js";
|
import { evaluateMathExpression } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/io-managers/input";
|
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/io-managers/input";
|
||||||
import { type NumberInputMode, type NumberInputIncrementBehavior } from "@graphite/messages";
|
import type { NumberInputMode, NumberInputIncrementBehavior, ActionShortcut } from "@graphite/messages";
|
||||||
import { browserVersion, isDesktop } from "@graphite/utility-functions/platform";
|
import { browserVersion, isDesktop } from "@graphite/utility-functions/platform";
|
||||||
|
|
||||||
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
|
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
export let label: string | undefined = undefined;
|
export let label: string | undefined = undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
on:click={() => handleEntryClick(entry)}
|
on:click={() => handleEntryClick(entry)}
|
||||||
data-tooltip-label={entry.tooltipLabel}
|
data-tooltip-label={entry.tooltipLabel}
|
||||||
data-tooltip-description={entry.tooltipDescription}
|
data-tooltip-description={entry.tooltipDescription}
|
||||||
data-tooltip-shortcut={entry.tooltipShortcut}
|
data-tooltip-shortcut={entry.tooltipShortcut?.shortcut ? JSON.stringify(entry.tooltipShortcut.shortcut) : undefined}
|
||||||
tabindex={index === selectedIndex ? -1 : 0}
|
tabindex={index === selectedIndex ? -1 : 0}
|
||||||
{disabled}
|
{disabled}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import type { ReferencePoint } from "@graphite/messages";
|
import type { ReferencePoint, ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ value: ReferencePoint }>();
|
const dispatch = createEventDispatcher<{ value: ReferencePoint }>();
|
||||||
|
|
||||||
@@ -9,14 +9,20 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
function setValue(newValue: ReferencePoint) {
|
function setValue(newValue: ReferencePoint) {
|
||||||
dispatch("value", newValue);
|
dispatch("value", newValue);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="reference-point-input" class:disabled data-tooltip-label={tooltipLabel} data-tooltip-description={tooltipDescription} data-tooltip-shortcut={tooltipShortcut}>
|
<div
|
||||||
|
class="reference-point-input"
|
||||||
|
class:disabled
|
||||||
|
data-tooltip-label={tooltipLabel}
|
||||||
|
data-tooltip-description={tooltipDescription}
|
||||||
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
|
>
|
||||||
<button on:click={() => setValue("TopLeft")} class="row-1 col-1" class:active={value === "TopLeft"} tabindex="-1" {disabled}><div /></button>
|
<button on:click={() => setValue("TopLeft")} class="row-1 col-1" class:active={value === "TopLeft"} tabindex="-1" {disabled}><div /></button>
|
||||||
<button on:click={() => setValue("TopCenter")} class="row-1 col-2" class:active={value === "TopCenter"} tabindex="-1" {disabled}><div /></button>
|
<button on:click={() => setValue("TopCenter")} class="row-1 col-2" class:active={value === "TopCenter"} tabindex="-1" {disabled}><div /></button>
|
||||||
<button on:click={() => setValue("TopRight")} class="row-1 col-3" class:active={value === "TopRight"} tabindex="-1" {disabled}><div /></button>
|
<button on:click={() => setValue("TopRight")} class="row-1 col-3" class:active={value === "TopRight"} tabindex="-1" {disabled}><div /></button>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
// export let disabled = false;
|
// export let disabled = false;
|
||||||
// export let tooltipLabel: string | undefined = undefined;
|
// export let tooltipLabel: string | undefined = undefined;
|
||||||
// export let tooltipDescription: string | undefined = undefined;
|
// export let tooltipDescription: string | undefined = undefined;
|
||||||
// export let tooltipShortcut: string | undefined = undefined;
|
// export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
let markerTrack: LayoutRow | undefined = undefined;
|
let markerTrack: LayoutRow | undefined = undefined;
|
||||||
let positionRestore: number | undefined = undefined;
|
let positionRestore: number | undefined = undefined;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import FieldInput from "@graphite/components/widgets/inputs/FieldInput.svelte";
|
import FieldInput from "@graphite/components/widgets/inputs/FieldInput.svelte";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ commitText: string }>();
|
const dispatch = createEventDispatcher<{ commitText: string }>();
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
export let label: string | undefined = undefined;
|
export let label: string | undefined = undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
|
||||||
let self: FieldInput | undefined;
|
let self: FieldInput | undefined;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import FieldInput from "@graphite/components/widgets/inputs/FieldInput.svelte";
|
import FieldInput from "@graphite/components/widgets/inputs/FieldInput.svelte";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ commitText: string }>();
|
const dispatch = createEventDispatcher<{ commitText: string }>();
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
export let label: string | undefined = undefined;
|
export let label: string | undefined = undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let placeholder: string | undefined = undefined;
|
export let placeholder: string | undefined = undefined;
|
||||||
// Disabled
|
// Disabled
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type IconName, ICONS, ICON_SVG_STRINGS } from "@graphite/icons";
|
import { type IconName, ICONS, ICON_SVG_STRINGS } from "@graphite/icons";
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
$: iconSizeClass = ((icon: IconName) => {
|
$: iconSizeClass = ((icon: IconName) => {
|
||||||
const iconData = ICONS[icon];
|
const iconData = ICONS[icon];
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
let className = "";
|
let className = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
@@ -8,7 +10,7 @@
|
|||||||
export let height: string | undefined;
|
export let height: string | undefined;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
.flatMap(([className, stateName]) => (stateName ? [className] : []))
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
class={`image-label ${className} ${extraClasses}`.trim()}
|
class={`image-label ${className} ${extraClasses}`.trim()}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { IconName } from "@graphite/icons";
|
||||||
|
import type { ActionShortcut, KeyRaw, LabeledShortcut, MouseMotion } from "@graphite/messages";
|
||||||
|
import { operatingSystem } from "@graphite/utility-functions/platform";
|
||||||
|
|
||||||
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
|
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||||
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
|
|
||||||
|
export let shortcut: ActionShortcut;
|
||||||
|
|
||||||
|
function keyTextOrIconList(keyGroup: LabeledShortcut): { label?: string; icon?: IconName; mouseMotion?: MouseMotion }[] {
|
||||||
|
const list = keyGroup.map((labeledKeyOrMouseMotion) => {
|
||||||
|
// Use a mouse icon if it's a mouse motion instead of a key
|
||||||
|
if (typeof labeledKeyOrMouseMotion === "string") return { mouseMotion: labeledKeyOrMouseMotion };
|
||||||
|
|
||||||
|
// `key` is the name of the `Key` enum in Rust, while `label` is the localized string to display (if it doesn't become an icon)
|
||||||
|
let key = labeledKeyOrMouseMotion.key;
|
||||||
|
const label = labeledKeyOrMouseMotion.label;
|
||||||
|
|
||||||
|
// Replace Alt and Accel keys with their Mac-specific equivalents
|
||||||
|
if (operatingSystem() === "Mac") {
|
||||||
|
if (key === "Alt") key = "Option";
|
||||||
|
if (key === "Accel") key = "Command";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either display an icon...
|
||||||
|
const icon = keyboardHintIcon(key);
|
||||||
|
if (icon) return { icon };
|
||||||
|
|
||||||
|
// ...or display text
|
||||||
|
return { label };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Consolidate consecutive labels into a concatenated single label
|
||||||
|
const consolidatedList: typeof list = [];
|
||||||
|
list.forEach((item) => {
|
||||||
|
const lastItem = consolidatedList[consolidatedList.length - 1];
|
||||||
|
if (item.label && lastItem?.label) lastItem.label += " " + item.label;
|
||||||
|
else consolidatedList.push(item);
|
||||||
|
});
|
||||||
|
return consolidatedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyboardHintIcon(input: KeyRaw): IconName | undefined {
|
||||||
|
switch (input) {
|
||||||
|
case "ArrowDown":
|
||||||
|
return "KeyboardArrowDown";
|
||||||
|
case "ArrowLeft":
|
||||||
|
return "KeyboardArrowLeft";
|
||||||
|
case "ArrowRight":
|
||||||
|
return "KeyboardArrowRight";
|
||||||
|
case "ArrowUp":
|
||||||
|
return "KeyboardArrowUp";
|
||||||
|
case "Backspace":
|
||||||
|
return "KeyboardBackspace";
|
||||||
|
case "Enter":
|
||||||
|
return "KeyboardEnter";
|
||||||
|
case "Space":
|
||||||
|
return "KeyboardSpace";
|
||||||
|
case "Tab":
|
||||||
|
return "KeyboardTab";
|
||||||
|
case "Command":
|
||||||
|
return operatingSystem() === "Mac" ? "KeyboardCommand" : undefined;
|
||||||
|
case "Control":
|
||||||
|
return operatingSystem() === "Mac" ? "KeyboardControl" : undefined;
|
||||||
|
case "Option":
|
||||||
|
return operatingSystem() === "Mac" ? "KeyboardOption" : undefined;
|
||||||
|
case "Shift":
|
||||||
|
return operatingSystem() === "Mac" ? "KeyboardShift" : undefined;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseHintIcon(input?: MouseMotion): IconName {
|
||||||
|
return `MouseHint${input}` as IconName;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<LayoutRow class="shortcut-label">
|
||||||
|
{#each keyTextOrIconList(shortcut.shortcut) as { label, icon, mouseMotion }}
|
||||||
|
{#if label}
|
||||||
|
<div class="key-label">
|
||||||
|
<TextLabel>{label}</TextLabel>
|
||||||
|
</div>
|
||||||
|
{:else if icon}
|
||||||
|
<div class="key-icon">
|
||||||
|
<IconLabel {icon} />
|
||||||
|
</div>
|
||||||
|
{:else if mouseMotion}
|
||||||
|
<div class="mouse-icon">
|
||||||
|
<IconLabel icon={mouseHintIcon(mouseMotion)} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</LayoutRow>
|
||||||
|
|
||||||
|
<style lang="scss" global>
|
||||||
|
.shortcut-label {
|
||||||
|
.key-icon,
|
||||||
|
.key-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--color-3-darkgray);
|
||||||
|
color: var(--color-b-lightgray);
|
||||||
|
fill: var(--color-b-lightgray);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-b-lightgray);
|
||||||
|
|
||||||
|
.dim {
|
||||||
|
fill: var(--color-8-uppergray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-menu-content .row > & {
|
||||||
|
.key-label,
|
||||||
|
.key-icon,
|
||||||
|
.mouse-icon {
|
||||||
|
color: var(--color-8-uppergray);
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-icon svg {
|
||||||
|
fill: var(--color-8-uppergray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mouse-icon svg {
|
||||||
|
// 3 shades brighter than the 8-uppergray of key labels/icons
|
||||||
|
fill: var(--color-b-lightgray);
|
||||||
|
|
||||||
|
.dim {
|
||||||
|
// 3 shades darker than the 8-uppergray of key labels/icons
|
||||||
|
fill: var(--color-5-dullgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
|
|
||||||
let className = "";
|
let className = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let classes: Record<string, boolean> = {};
|
export let classes: Record<string, boolean> = {};
|
||||||
@@ -16,7 +18,7 @@
|
|||||||
export let multiline = false;
|
export let multiline = false;
|
||||||
export let tooltipLabel: string | undefined = undefined;
|
export let tooltipLabel: string | undefined = undefined;
|
||||||
export let tooltipDescription: string | undefined = undefined;
|
export let tooltipDescription: string | undefined = undefined;
|
||||||
export let tooltipShortcut: string | undefined = undefined;
|
export let tooltipShortcut: ActionShortcut | undefined = undefined;
|
||||||
export let forCheckbox: bigint | undefined = undefined;
|
export let forCheckbox: bigint | undefined = undefined;
|
||||||
|
|
||||||
$: extraClasses = Object.entries(classes)
|
$: extraClasses = Object.entries(classes)
|
||||||
@@ -41,7 +43,7 @@
|
|||||||
style={`${styleName} ${extraStyles}`.trim() || undefined}
|
style={`${styleName} ${extraStyles}`.trim() || undefined}
|
||||||
data-tooltip-label={tooltipLabel}
|
data-tooltip-label={tooltipLabel}
|
||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
for={forCheckbox !== undefined ? `checkbox-input-${forCheckbox}` : undefined}
|
for={forCheckbox !== undefined ? `checkbox-input-${forCheckbox}` : undefined}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -1,273 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { getContext } from "svelte";
|
|
||||||
|
|
||||||
import type { IconName } from "@graphite/icons";
|
|
||||||
|
|
||||||
import { type KeyRaw, type LayoutKeysGroup, type Key, type MouseMotion } from "@graphite/messages";
|
|
||||||
import type { FullscreenState } from "@graphite/state-providers/fullscreen";
|
|
||||||
import { operatingSystem } from "@graphite/utility-functions/platform";
|
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
|
||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
|
||||||
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
|
||||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
|
||||||
|
|
||||||
type LabelData = { label?: string; icon?: IconName; width: string };
|
|
||||||
|
|
||||||
// Keys that become icons if they are listed here with their units of width
|
|
||||||
const ICON_WIDTHS_MAC = {
|
|
||||||
Shift: 2,
|
|
||||||
Control: 2,
|
|
||||||
Option: 2,
|
|
||||||
Command: 2,
|
|
||||||
};
|
|
||||||
const ICON_WIDTHS = {
|
|
||||||
ArrowUp: 1,
|
|
||||||
ArrowRight: 1,
|
|
||||||
ArrowDown: 1,
|
|
||||||
ArrowLeft: 1,
|
|
||||||
Backspace: 2,
|
|
||||||
Enter: 2,
|
|
||||||
Tab: 2,
|
|
||||||
Space: 3,
|
|
||||||
...(operatingSystem() === "Mac" ? ICON_WIDTHS_MAC : {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const fullscreen = getContext<FullscreenState>("fullscreen");
|
|
||||||
|
|
||||||
export let keysWithLabelsGroups: LayoutKeysGroup[] = [];
|
|
||||||
export let mouseMotion: MouseMotion | undefined = undefined;
|
|
||||||
export let requiresLock = false;
|
|
||||||
export let textOnly = false;
|
|
||||||
|
|
||||||
$: keyboardLockInfoMessage = watchKeyboardLockInfoMessage($fullscreen.keyboardLockApiSupported);
|
|
||||||
|
|
||||||
$: displayKeyboardLockNotice = requiresLock && !$fullscreen.keyboardLocked;
|
|
||||||
|
|
||||||
function watchKeyboardLockInfoMessage(keyboardLockApiSupported: boolean): string {
|
|
||||||
const RESERVED = "This keyboard shortcut is reserved by the browser.";
|
|
||||||
const USE_FULLSCREEN = "It is made available in fullscreen mode.";
|
|
||||||
const USE_SECURE_CTX = "It is made available in fullscreen mode when this website is served from a secure context (https or localhost).";
|
|
||||||
const SWITCH_BROWSER = "Use a Chromium-based browser (like Chrome or Edge) in fullscreen mode to directly use the shortcut.";
|
|
||||||
|
|
||||||
if (keyboardLockApiSupported) return `${RESERVED} ${USE_FULLSCREEN}`;
|
|
||||||
if (!("chrome" in window)) return `${RESERVED} ${SWITCH_BROWSER}`;
|
|
||||||
if (!window.isSecureContext) return `${RESERVED} ${USE_SECURE_CTX}`;
|
|
||||||
return RESERVED;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyTextOrIconList(keyGroup: LayoutKeysGroup): LabelData[] {
|
|
||||||
return keyGroup.map((key) => keyTextOrIcon(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyTextOrIcon(keyWithLabel: Key): LabelData {
|
|
||||||
// `key` is the name of the `Key` enum in Rust, while `label` is the localized string to display (if it doesn't become an icon)
|
|
||||||
let key = keyWithLabel.key;
|
|
||||||
const label = keyWithLabel.label;
|
|
||||||
|
|
||||||
// Replace Alt and Accel keys with their Mac-specific equivalents
|
|
||||||
if (operatingSystem() === "Mac") {
|
|
||||||
if (key === "Alt") key = "Option";
|
|
||||||
if (key === "Accel") key = "Command";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Either display an icon...
|
|
||||||
// @ts-expect-error We want undefined if it isn't in the object
|
|
||||||
const iconWidth: number | undefined = ICON_WIDTHS[key];
|
|
||||||
const icon = iconWidth !== undefined && iconWidth > 0 && (keyboardHintIcon(key) || false);
|
|
||||||
if (icon) return { icon, width: `width-${iconWidth}` };
|
|
||||||
|
|
||||||
// ...or display text
|
|
||||||
return { label, width: `width-${label.length}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseHintIcon(input?: MouseMotion): IconName {
|
|
||||||
return `MouseHint${input}` as IconName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyboardHintIcon(input: KeyRaw): IconName | undefined {
|
|
||||||
switch (input) {
|
|
||||||
case "ArrowDown":
|
|
||||||
return "KeyboardArrowDown";
|
|
||||||
case "ArrowLeft":
|
|
||||||
return "KeyboardArrowLeft";
|
|
||||||
case "ArrowRight":
|
|
||||||
return "KeyboardArrowRight";
|
|
||||||
case "ArrowUp":
|
|
||||||
return "KeyboardArrowUp";
|
|
||||||
case "Backspace":
|
|
||||||
return "KeyboardBackspace";
|
|
||||||
case "Command":
|
|
||||||
return "KeyboardCommand";
|
|
||||||
case "Control":
|
|
||||||
return "KeyboardControl";
|
|
||||||
case "Enter":
|
|
||||||
return "KeyboardEnter";
|
|
||||||
case "Option":
|
|
||||||
return "KeyboardOption";
|
|
||||||
case "Shift":
|
|
||||||
return "KeyboardShift";
|
|
||||||
case "Space":
|
|
||||||
return "KeyboardSpace";
|
|
||||||
case "Tab":
|
|
||||||
return "KeyboardTab";
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if displayKeyboardLockNotice}
|
|
||||||
<IconLabel class="user-input-label keyboard-lock-notice" icon="Info" tooltipDescription={keyboardLockInfoMessage} />
|
|
||||||
{:else}
|
|
||||||
<LayoutRow class="user-input-label" classes={{ "text-only": textOnly }}>
|
|
||||||
{#each keysWithLabelsGroups as keysWithLabels, groupIndex}
|
|
||||||
{#if groupIndex > 0}
|
|
||||||
<Separator type="Related" />
|
|
||||||
{/if}
|
|
||||||
{#each keyTextOrIconList(keysWithLabels) as keyInfo}
|
|
||||||
<div class={`input-key ${keyInfo.width}`}>
|
|
||||||
{#if keyInfo.icon}
|
|
||||||
<IconLabel icon={keyInfo.icon} />
|
|
||||||
{:else if keyInfo.label !== undefined}
|
|
||||||
<TextLabel>{keyInfo.label}</TextLabel>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
{/each}
|
|
||||||
{#if mouseMotion}
|
|
||||||
<div class="input-mouse">
|
|
||||||
<IconLabel icon={mouseHintIcon(mouseMotion)} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if $$slots.default}
|
|
||||||
<div class="hint-text">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</LayoutRow>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style lang="scss" global>
|
|
||||||
.user-input-label {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
align-items: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&.text-only {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.input-key {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-label {
|
|
||||||
margin: calc(calc(18px - 12px) / 2) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
& + .input-key {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.text-only) {
|
|
||||||
.input-key,
|
|
||||||
.input-mouse {
|
|
||||||
& + .input-key,
|
|
||||||
& + .input-mouse {
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-key {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-family: "Source Code Pro", monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
text-align: center;
|
|
||||||
height: 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--color-3-darkgray);
|
|
||||||
color: var(--color-b-lightgray);
|
|
||||||
|
|
||||||
.icon-label {
|
|
||||||
fill: var(--color-b-lightgray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-label {
|
|
||||||
// Firefox renders the text 1px lower than Chrome (tested on Windows) with 16px line-height,
|
|
||||||
// so moving it up 1 pixel by using 15px makes them agree.
|
|
||||||
line-height: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.width-1 {
|
|
||||||
width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.width-2 {
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.width-3 {
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.width-4 {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.width-5 {
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-label {
|
|
||||||
margin: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-mouse {
|
|
||||||
.bright {
|
|
||||||
fill: var(--color-b-lightgray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dim {
|
|
||||||
fill: var(--color-8-uppergray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint-text:not(:empty) {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-menu-content .row > & {
|
|
||||||
.input-key {
|
|
||||||
border-color: var(--color-3-darkgray);
|
|
||||||
color: var(--color-8-uppergray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-key .icon-label svg,
|
|
||||||
&.keyboard-lock-notice.keyboard-lock-notice svg,
|
|
||||||
.input-mouse .bright {
|
|
||||||
fill: var(--color-8-uppergray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-mouse .dim {
|
|
||||||
fill: var(--color-3-darkgray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-menu-content .row:hover > & {
|
|
||||||
.input-key {
|
|
||||||
border-color: var(--color-8-uppergray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-mouse .dim {
|
|
||||||
fill: var(--color-8-uppergray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -2,45 +2,25 @@
|
|||||||
import { getContext, onMount } from "svelte";
|
import { getContext, onMount } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import { type HintData, type HintInfo, type LayoutKeysGroup, UpdateInputHints } from "@graphite/messages";
|
import { defaultWidgetLayout, patchWidgetLayout, UpdateStatusBarHintsLayout } from "@graphite/messages";
|
||||||
import { operatingSystem } from "@graphite/utility-functions/platform";
|
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
import Separator from "@graphite/components/widgets/labels/Separator.svelte";
|
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
|
||||||
import UserInputLabel from "@graphite/components/widgets/labels/UserInputLabel.svelte";
|
|
||||||
|
|
||||||
const editor = getContext<Editor>("editor");
|
const editor = getContext<Editor>("editor");
|
||||||
|
|
||||||
let hintData: HintData = [];
|
let statusBarHintsLayout = defaultWidgetLayout();
|
||||||
|
|
||||||
function inputKeysForPlatform(hint: HintInfo): LayoutKeysGroup[] {
|
|
||||||
return operatingSystem() === "Mac" && hint.keyGroupsMac ? hint.keyGroupsMac : hint.keyGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateInputHints, (data) => {
|
editor.subscriptions.subscribeJsMessage(UpdateStatusBarHintsLayout, (updateStatusBarHintsLayout) => {
|
||||||
hintData = data.hintData;
|
patchWidgetLayout(statusBarHintsLayout, updateStatusBarHintsLayout);
|
||||||
|
statusBarHintsLayout = statusBarHintsLayout;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutRow class="status-bar">
|
<LayoutRow class="status-bar">
|
||||||
<LayoutRow class="hint-groups">
|
<WidgetLayout class="hints" layout={statusBarHintsLayout} />
|
||||||
{#each hintData as hintGroup, index (hintGroup)}
|
|
||||||
{#if index !== 0}
|
|
||||||
<Separator type="Section" />
|
|
||||||
{/if}
|
|
||||||
{#each hintGroup as hint (hint)}
|
|
||||||
{#if hint.plus}
|
|
||||||
<LayoutRow class="plus">+</LayoutRow>
|
|
||||||
{/if}
|
|
||||||
{#if hint.slash}
|
|
||||||
<LayoutRow class="slash">/</LayoutRow>
|
|
||||||
{/if}
|
|
||||||
<UserInputLabel mouseMotion={hint.mouse} keysWithLabelsGroups={inputKeysForPlatform(hint)}>{hint.label}</UserInputLabel>
|
|
||||||
{/each}
|
|
||||||
{/each}
|
|
||||||
</LayoutRow>
|
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
@@ -49,30 +29,29 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
.hint-groups {
|
.hints {
|
||||||
flex: 0 0 auto;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0 -4px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
--row-height: 24px;
|
||||||
|
margin: 0 4px;
|
||||||
|
max-width: calc(100% - 2 * 4px);
|
||||||
|
|
||||||
.separator.section {
|
.text-label,
|
||||||
// Width of section separator (12px) minus the margin of the surrounding user input labels (8px)
|
.shortcut-label {
|
||||||
margin: 0 calc(12px - 8px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.plus,
|
|
||||||
.slash {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-weight: 700;
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
+ .text-label,
|
||||||
|
+ .shortcut-label {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-input-label {
|
.text-label:not(.bold) + .shortcut-label {
|
||||||
margin: 0 8px;
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
& + .user-input-label {
|
.text-label.bold {
|
||||||
margin-left: 0;
|
padding: 0 4px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
|
import { shortcutF11 } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
|
import type { ActionShortcut } from "@graphite/messages";
|
||||||
import type { FullscreenState } from "@graphite/state-providers/fullscreen";
|
import type { FullscreenState } from "@graphite/state-providers/fullscreen";
|
||||||
|
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
@@ -8,6 +10,8 @@
|
|||||||
|
|
||||||
const fullscreen = getContext<FullscreenState>("fullscreen");
|
const fullscreen = getContext<FullscreenState>("fullscreen");
|
||||||
|
|
||||||
|
const f11Keys: ActionShortcut = shortcutF11();
|
||||||
|
|
||||||
async function handleClick() {
|
async function handleClick() {
|
||||||
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
|
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
|
||||||
else fullscreen.enterFullscreen();
|
else fullscreen.enterFullscreen();
|
||||||
@@ -19,7 +23,7 @@
|
|||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
tooltipLabel={$fullscreen.windowFullscreen ? "Exit Fullscreen" : "Enter Fullscreen"}
|
tooltipLabel={$fullscreen.windowFullscreen ? "Exit Fullscreen" : "Enter Fullscreen"}
|
||||||
tooltipDescription={$fullscreen.keyboardLockApiSupported ? "While fullscreen, keyboard shortcuts normally reserved by the browser become available." : ""}
|
tooltipDescription={$fullscreen.keyboardLockApiSupported ? "While fullscreen, keyboard shortcuts normally reserved by the browser become available." : ""}
|
||||||
tooltipShortcut="F11"
|
tooltipShortcut={f11Keys}
|
||||||
>
|
>
|
||||||
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
|
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
import Document from "@graphite/components/panels/Document.svelte";
|
import Document from "@graphite/components/panels/Document.svelte";
|
||||||
import Layers from "@graphite/components/panels/Layers.svelte";
|
import Layers from "@graphite/components/panels/Layers.svelte";
|
||||||
import Properties from "@graphite/components/panels/Properties.svelte";
|
import Properties from "@graphite/components/panels/Properties.svelte";
|
||||||
|
import Welcome from "@graphite/components/panels/Welcome.svelte";
|
||||||
|
|
||||||
const PANEL_COMPONENTS = {
|
const PANEL_COMPONENTS = {
|
||||||
|
Welcome,
|
||||||
Document,
|
Document,
|
||||||
Layers,
|
Layers,
|
||||||
Properties,
|
Properties,
|
||||||
@@ -17,23 +19,16 @@
|
|||||||
import { getContext, tick } from "svelte";
|
import { getContext, tick } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import { type LayoutKeysGroup, type Key } from "@graphite/messages";
|
import { isEventSupported } from "@graphite/utility-functions/platform";
|
||||||
import type { AppWindowState } from "@graphite/state-providers/app-window";
|
|
||||||
import { operatingSystem, isEventSupported } from "@graphite/utility-functions/platform";
|
|
||||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
|
||||||
|
|
||||||
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||||
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
|
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
|
||||||
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
|
||||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
|
||||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||||
import UserInputLabel from "@graphite/components/widgets/labels/UserInputLabel.svelte";
|
|
||||||
|
|
||||||
const BUTTON_LEFT = 0;
|
const BUTTON_LEFT = 0;
|
||||||
const BUTTON_MIDDLE = 1;
|
const BUTTON_MIDDLE = 1;
|
||||||
|
|
||||||
const appWindow = getContext<AppWindowState>("appWindow");
|
|
||||||
const editor = getContext<Editor>("editor");
|
const editor = getContext<Editor>("editor");
|
||||||
|
|
||||||
export let tabMinWidths = false;
|
export let tabMinWidths = false;
|
||||||
@@ -54,52 +49,6 @@
|
|||||||
|
|
||||||
let tabElements: (LayoutRow | undefined)[] = [];
|
let tabElements: (LayoutRow | undefined)[] = [];
|
||||||
|
|
||||||
function platformModifiedAccelKey(browserReservedKey: boolean): LayoutKeysGroup {
|
|
||||||
// TODO: Remove this by properly feeding these keys from a layout provided by the backend
|
|
||||||
|
|
||||||
const ALT: Key = { key: "Alt", label: "Alt" };
|
|
||||||
const COMMAND: Key = { key: "Command", label: "Command" };
|
|
||||||
const CONTROL: Key = { key: "Control", label: "Ctrl" };
|
|
||||||
|
|
||||||
// Only consider the browser reserved key on web platforms
|
|
||||||
const reservedKey = $appWindow.platform === "Web" ? browserReservedKey : false;
|
|
||||||
|
|
||||||
// Return either Command (Mac) or Control (Windows/Linux), with Alt added if the browser reserves the shortcut
|
|
||||||
if (operatingSystem() === "Mac") return reservedKey ? [ALT, COMMAND] : [COMMAND];
|
|
||||||
return reservedKey ? [CONTROL, ALT] : [CONTROL];
|
|
||||||
}
|
|
||||||
|
|
||||||
function dropFile(e: DragEvent) {
|
|
||||||
if (!e.dataTransfer) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
Array.from(e.dataTransfer.items).forEach(async (item) => {
|
|
||||||
const file = item.getAsFile();
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
if (file.type.includes("svg")) {
|
|
||||||
const svgData = await file.text();
|
|
||||||
editor.handle.pasteSvg(file.name, svgData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.type.startsWith("image")) {
|
|
||||||
const imageData = await extractPixelData(file);
|
|
||||||
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const graphiteFileSuffix = "." + editor.handle.fileExtension();
|
|
||||||
if (file.name.endsWith(graphiteFileSuffix)) {
|
|
||||||
const content = await file.text();
|
|
||||||
const documentName = file.name.slice(0, -graphiteFileSuffix.length);
|
|
||||||
editor.handle.openDocumentFile(documentName, content);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEmptySpaceAction(e: MouseEvent) {
|
function onEmptySpaceAction(e: MouseEvent) {
|
||||||
if (e.target !== e.currentTarget) return;
|
if (e.target !== e.currentTarget) return;
|
||||||
if (e.button === BUTTON_MIDDLE || (e.button === BUTTON_LEFT && e.detail === 2)) emptySpaceAction?.();
|
if (e.button === BUTTON_MIDDLE || (e.button === BUTTON_LEFT && e.detail === 2)) emptySpaceAction?.();
|
||||||
@@ -162,66 +111,10 @@
|
|||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
{/each}
|
{/each}
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<!-- <PopoverButton style="VerticalEllipsis">
|
|
||||||
<TextLabel bold={true}>Panel Options</TextLabel>
|
|
||||||
<TextLabel multiline={true}>Coming soon</TextLabel>
|
|
||||||
</PopoverButton> -->
|
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutCol class="panel-body">
|
<LayoutCol class="panel-body">
|
||||||
{#if panelType}
|
{#if panelType}
|
||||||
<svelte:component this={PANEL_COMPONENTS[panelType]} />
|
<svelte:component this={PANEL_COMPONENTS[panelType]} />
|
||||||
{:else}
|
|
||||||
<LayoutCol class="empty-panel" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}>
|
|
||||||
<LayoutCol class="top-spacer"></LayoutCol>
|
|
||||||
<LayoutCol class="content-container">
|
|
||||||
<LayoutCol class="content">
|
|
||||||
<LayoutRow class="logotype">
|
|
||||||
<IconLabel icon="GraphiteLogotypeSolid" />
|
|
||||||
</LayoutRow>
|
|
||||||
<LayoutRow class="actions">
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<TextButton label="New Document" icon="File" flush={true} action={() => editor.handle.newDocumentDialog()} />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiedAccelKey(true), { key: "KeyN", label: "N" }]]} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<TextButton label="Open Document" icon="Folder" flush={true} action={() => editor.handle.openDocument()} />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiedAccelKey(false), { key: "KeyO", label: "O" }]]} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<TextButton label="Open Demo Artwork" icon="Image" flush={true} action={() => editor.handle.demoArtworkDialog()} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<TextButton label="Support the Development Fund" icon="Heart" flush={true} action={() => editor.handle.visitUrl("https://graphite.rs/donate/")} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</LayoutRow>
|
|
||||||
</LayoutCol>
|
|
||||||
</LayoutCol>
|
|
||||||
<LayoutCol class="bottom-message">
|
|
||||||
{#if new Date().getFullYear() === 2025}
|
|
||||||
<TextLabel italic={true} disabled={true}>
|
|
||||||
September 2025 release — <a href="https://youtube.com/watch?v=Vl5BA4g3QXM" target="_blank">What's new? (video)</a>
|
|
||||||
— Note: some older documents may render differently and require manual fixes.
|
|
||||||
<a href="https://ec6796b4.graphite-editor.pages.dev/" target="_blank">Need the old version?</a>
|
|
||||||
</TextLabel>
|
|
||||||
{/if}
|
|
||||||
</LayoutCol>
|
|
||||||
</LayoutCol>
|
|
||||||
{/if}
|
{/if}
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
@@ -323,7 +216,7 @@
|
|||||||
left: -1px;
|
left: -1px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: var(--color-4-dimgray);
|
background: var(--color-5-dullgray);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
@@ -335,15 +228,11 @@
|
|||||||
right: -1px;
|
right: -1px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: var(--color-4-dimgray);
|
background: var(--color-5-dullgray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .popover-button {
|
|
||||||
// margin: 2px 4px;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
@@ -354,66 +243,11 @@
|
|||||||
> div {
|
> div {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-panel {
|
|
||||||
background: var(--color-2-mildblack);
|
|
||||||
margin: 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.logotype {
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: auto;
|
|
||||||
height: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-spacing: 8px;
|
|
||||||
margin: -8px;
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-spacer {
|
|
||||||
flex: 0 1 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-message {
|
|
||||||
flex: 0 0 48px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: end;
|
|
||||||
|
|
||||||
.text-label {
|
|
||||||
white-space: wrap;
|
|
||||||
margin: 0 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed for the viewport hole punch on desktop
|
// Needed for the viewport hole punch on desktop
|
||||||
.viewport-hole-punch &.document-panel,
|
.viewport-hole-punch &.document-panel,
|
||||||
.viewport-hole-punch &.document-panel .panel-body:not(:has(.empty-panel)) {
|
.viewport-hole-punch &.document-panel .panel-body:not(:has(.welcome-panel)) {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
<LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["document"] }} data-subdivision-name="document">
|
<LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["document"] }} data-subdivision-name="document">
|
||||||
<Panel
|
<Panel
|
||||||
class="document-panel"
|
class="document-panel"
|
||||||
panelType={$portfolio.documents.length > 0 ? "Document" : undefined}
|
panelType={$portfolio.documents.length > 0 ? "Document" : "Welcome"}
|
||||||
tabCloseButtons={true}
|
tabCloseButtons={true}
|
||||||
tabMinWidths={true}
|
tabMinWidths={true}
|
||||||
tabLabels={documentTabLabels}
|
tabLabels={documentTabLabels}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// import { panicProxy } from "@graphite/utility-functions/panic-proxy";
|
// import { panicProxy } from "@graphite/utility-functions/panic-proxy";
|
||||||
import init, { setRandomSeed, wasmMemory, EditorHandle, receiveNativeMessage } from "@graphite/../wasm/pkg/graphite_wasm.js";
|
import init, { setRandomSeed, wasmMemory, EditorHandle, receiveNativeMessage } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
import { type JsMessageType } from "@graphite/messages";
|
import { type JsMessageType } from "@graphite/messages";
|
||||||
import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router";
|
import { createSubscriptionRouter, type SubscriptionRouter } from "@graphite/subscription-router";
|
||||||
|
|
||||||
|
|||||||
+66
-79
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { Transform, Type, plainToClass } from "class-transformer";
|
import { Transform, Type, plainToClass } from "class-transformer";
|
||||||
|
|
||||||
import { type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm.js";
|
import { type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
import { type PopoverButtonStyle, type IconName, type IconSize } from "@graphite/icons";
|
import { type PopoverButtonStyle, type IconName, type IconSize } from "@graphite/icons";
|
||||||
|
|
||||||
export class JsMessage {
|
export class JsMessage {
|
||||||
@@ -305,37 +305,14 @@ export class UpdateViewportPhysicalBounds extends JsMessage {
|
|||||||
readonly height!: number;
|
readonly height!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateInputHints extends JsMessage {
|
|
||||||
@Type(() => HintInfo)
|
|
||||||
readonly hintData!: HintData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type HintData = HintGroup[];
|
|
||||||
|
|
||||||
export type HintGroup = HintInfo[];
|
|
||||||
|
|
||||||
export class HintInfo {
|
|
||||||
readonly keyGroups!: LayoutKeysGroup[];
|
|
||||||
|
|
||||||
readonly keyGroupsMac!: LayoutKeysGroup[] | undefined;
|
|
||||||
|
|
||||||
readonly mouse!: MouseMotion | undefined;
|
|
||||||
|
|
||||||
readonly label!: string;
|
|
||||||
|
|
||||||
readonly plus!: boolean;
|
|
||||||
|
|
||||||
readonly slash!: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rust enum `Key`
|
// Rust enum `Key`
|
||||||
export type KeyRaw = string;
|
export type KeyRaw = string;
|
||||||
// Serde converts a Rust `Key` enum variant into this format with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key
|
// Serde converts a Rust `Key` enum variant into this format with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key
|
||||||
export type Key = { key: KeyRaw; label: string };
|
export type LabeledKey = { key: KeyRaw; label: string };
|
||||||
export type LayoutKeysGroup = Key[];
|
|
||||||
export type ActionKeys = { keys: LayoutKeysGroup };
|
|
||||||
|
|
||||||
export type MouseMotion = "None" | "Lmb" | "Rmb" | "Mmb" | "ScrollUp" | "ScrollDown" | "Drag" | "LmbDouble" | "LmbDrag" | "RmbDrag" | "RmbDouble" | "MmbDrag";
|
export type MouseMotion = "None" | "Lmb" | "Rmb" | "Mmb" | "ScrollUp" | "ScrollDown" | "Drag" | "LmbDouble" | "LmbDrag" | "RmbDrag" | "RmbDouble" | "MmbDrag";
|
||||||
|
export type LabeledKeyOrMouseMotion = LabeledKey | MouseMotion;
|
||||||
|
export type LabeledShortcut = LabeledKeyOrMouseMotion[];
|
||||||
|
export type ActionShortcut = { shortcut: LabeledShortcut };
|
||||||
|
|
||||||
// Channels can have any range (0-1, 0-255, 0-100, 0-360) in the context they are being used in, these are just containers for the numbers
|
// Channels can have any range (0-1, 0-255, 0-100, 0-360) in the context they are being used in, these are just containers for the numbers
|
||||||
export type HSVA = { h: number; s: number; v: number; a: number };
|
export type HSVA = { h: number; s: number; v: number; a: number };
|
||||||
@@ -910,8 +887,8 @@ export class CheckboxInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
forLabel!: bigint | undefined;
|
forLabel!: bigint | undefined;
|
||||||
}
|
}
|
||||||
@@ -954,8 +931,8 @@ export class ColorInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FillChoice = Color | Gradient;
|
export type FillChoice = Color | Gradient;
|
||||||
@@ -1000,9 +977,7 @@ export type MenuListEntry = {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
tooltipLabel?: string;
|
tooltipLabel?: string;
|
||||||
tooltipDescription?: string;
|
tooltipDescription?: string;
|
||||||
tooltipShortcut?: string;
|
tooltipShortcut?: ActionShortcut;
|
||||||
shortcutKeys?: ActionKeys;
|
|
||||||
shortcutRequiresLock?: boolean;
|
|
||||||
children?: MenuListEntry[][];
|
children?: MenuListEntry[][];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1028,8 +1003,8 @@ export class CurveInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DropdownInput extends WidgetProps {
|
export class DropdownInput extends WidgetProps {
|
||||||
@@ -1051,8 +1026,8 @@ export class DropdownInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
// Styling
|
// Styling
|
||||||
|
|
||||||
@@ -1076,8 +1051,8 @@ export class FontInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IconButton extends WidgetProps {
|
export class IconButton extends WidgetProps {
|
||||||
@@ -1097,8 +1072,8 @@ export class IconButton extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IconLabel extends WidgetProps {
|
export class IconLabel extends WidgetProps {
|
||||||
@@ -1112,8 +1087,8 @@ export class IconLabel extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageButton extends WidgetProps {
|
export class ImageButton extends WidgetProps {
|
||||||
@@ -1131,8 +1106,8 @@ export class ImageButton extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageLabel extends WidgetProps {
|
export class ImageLabel extends WidgetProps {
|
||||||
@@ -1150,8 +1125,13 @@ export class ImageLabel extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ShortcutLabel extends WidgetProps {
|
||||||
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
|
shortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NumberInputIncrementBehavior = "Add" | "Multiply" | "Callback" | "None";
|
export type NumberInputIncrementBehavior = "Add" | "Multiply" | "Callback" | "None";
|
||||||
@@ -1168,8 +1148,8 @@ export class NumberInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
|
|
||||||
@@ -1234,8 +1214,8 @@ export class PopoverButton extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
popoverLayout!: LayoutGroup[];
|
popoverLayout!: LayoutGroup[];
|
||||||
@@ -1251,7 +1231,7 @@ export type RadioEntryData = {
|
|||||||
icon?: IconName;
|
icon?: IconName;
|
||||||
tooltipLabel?: string;
|
tooltipLabel?: string;
|
||||||
tooltipDescription?: string;
|
tooltipDescription?: string;
|
||||||
tooltipShortcut?: string;
|
tooltipShortcut?: ActionShortcut;
|
||||||
};
|
};
|
||||||
export type RadioEntries = RadioEntryData[];
|
export type RadioEntries = RadioEntryData[];
|
||||||
|
|
||||||
@@ -1297,8 +1277,8 @@ export class TextAreaInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParameterExposeButton extends WidgetProps {
|
export class ParameterExposeButton extends WidgetProps {
|
||||||
@@ -1312,8 +1292,8 @@ export class ParameterExposeButton extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TextButton extends WidgetProps {
|
export class TextButton extends WidgetProps {
|
||||||
@@ -1339,8 +1319,8 @@ export class TextButton extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
menuListChildren!: MenuListEntry[][];
|
menuListChildren!: MenuListEntry[][];
|
||||||
}
|
}
|
||||||
@@ -1356,8 +1336,8 @@ export class BreadcrumbTrailButtons extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TextInput extends WidgetProps {
|
export class TextInput extends WidgetProps {
|
||||||
@@ -1379,8 +1359,8 @@ export class TextInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TextLabel extends WidgetProps {
|
export class TextLabel extends WidgetProps {
|
||||||
@@ -1412,8 +1392,8 @@ export class TextLabel extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
|
|
||||||
forCheckbox!: bigint | undefined;
|
forCheckbox!: bigint | undefined;
|
||||||
}
|
}
|
||||||
@@ -1431,8 +1411,8 @@ export class ReferencePointInput extends WidgetProps {
|
|||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: string }) => value || undefined)
|
||||||
tooltipDescription!: string | undefined;
|
tooltipDescription!: string | undefined;
|
||||||
|
|
||||||
@Transform(({ value }: { value: string }) => value || undefined)
|
@Transform(({ value }: { value: ActionShortcut }) => value || undefined)
|
||||||
tooltipShortcut!: string | undefined;
|
tooltipShortcut!: ActionShortcut | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WIDGET
|
// WIDGET
|
||||||
@@ -1447,6 +1427,7 @@ const widgetSubTypes = [
|
|||||||
{ value: IconButton, name: "IconButton" },
|
{ value: IconButton, name: "IconButton" },
|
||||||
{ value: ImageButton, name: "ImageButton" },
|
{ value: ImageButton, name: "ImageButton" },
|
||||||
{ value: ImageLabel, name: "ImageLabel" },
|
{ value: ImageLabel, name: "ImageLabel" },
|
||||||
|
{ value: ShortcutLabel, name: "ShortcutLabel" },
|
||||||
{ value: IconLabel, name: "IconLabel" },
|
{ value: IconLabel, name: "IconLabel" },
|
||||||
{ value: NodeCatalog, name: "NodeCatalog" },
|
{ value: NodeCatalog, name: "NodeCatalog" },
|
||||||
{ value: NumberInput, name: "NumberInput" },
|
{ value: NumberInput, name: "NumberInput" },
|
||||||
@@ -1586,7 +1567,7 @@ export function isWidgetSpanRow(layoutRow: LayoutGroup): layoutRow is WidgetSpan
|
|||||||
return Boolean((layoutRow as WidgetSpanRow)?.rowWidgets);
|
return Boolean((layoutRow as WidgetSpanRow)?.rowWidgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WidgetTable = { tableWidgets: Widget[][] };
|
export type WidgetTable = { tableWidgets: Widget[][]; unstyled: boolean };
|
||||||
export function isWidgetTable(layoutTable: LayoutGroup): layoutTable is WidgetTable {
|
export function isWidgetTable(layoutTable: LayoutGroup): layoutTable is WidgetTable {
|
||||||
return Boolean((layoutTable as WidgetTable)?.tableWidgets);
|
return Boolean((layoutTable as WidgetTable)?.tableWidgets);
|
||||||
}
|
}
|
||||||
@@ -1643,6 +1624,7 @@ function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
|||||||
if (layoutGroup.table) {
|
if (layoutGroup.table) {
|
||||||
const result: WidgetTable = {
|
const result: WidgetTable = {
|
||||||
tableWidgets: layoutGroup.table.tableWidgets.map(hoistWidgetHolders),
|
tableWidgets: layoutGroup.table.tableWidgets.map(hoistWidgetHolders),
|
||||||
|
unstyled: layoutGroup.table.unstyled,
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1671,10 +1653,14 @@ export class UpdateMenuBarLayout extends WidgetDiffUpdate {}
|
|||||||
|
|
||||||
export class UpdateNodeGraphControlBarLayout extends WidgetDiffUpdate {}
|
export class UpdateNodeGraphControlBarLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
|
export class UpdateWelcomeScreenButtonsLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
export class UpdatePropertiesPanelLayout extends WidgetDiffUpdate {}
|
export class UpdatePropertiesPanelLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
export class UpdateDataPanelLayout extends WidgetDiffUpdate {}
|
export class UpdateDataPanelLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
|
export class UpdateStatusBarHintsLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
export class UpdateToolOptionsLayout extends WidgetDiffUpdate {}
|
export class UpdateToolOptionsLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
|
export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
|
||||||
@@ -1696,21 +1682,21 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
SendUIMetadata,
|
SendUIMetadata,
|
||||||
TriggerAboutGraphiteLocalizedCommitDate,
|
TriggerAboutGraphiteLocalizedCommitDate,
|
||||||
TriggerDisplayThirdPartyLicensesDialog,
|
TriggerDisplayThirdPartyLicensesDialog,
|
||||||
TriggerSaveDocument,
|
|
||||||
TriggerSaveFile,
|
|
||||||
TriggerExportImage,
|
TriggerExportImage,
|
||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerFontLoad,
|
TriggerFontLoad,
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerPersistenceRemoveDocument,
|
|
||||||
TriggerPersistenceWriteDocument,
|
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
TriggerOpenLaunchDocuments,
|
|
||||||
TriggerOpenDocument,
|
TriggerOpenDocument,
|
||||||
|
TriggerOpenLaunchDocuments,
|
||||||
TriggerPaste,
|
TriggerPaste,
|
||||||
|
TriggerPersistenceRemoveDocument,
|
||||||
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerSaveActiveDocument,
|
TriggerSaveActiveDocument,
|
||||||
|
TriggerSaveDocument,
|
||||||
|
TriggerSaveFile,
|
||||||
TriggerSavePreferences,
|
TriggerSavePreferences,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
TriggerTextCopy,
|
TriggerTextCopy,
|
||||||
@@ -1719,6 +1705,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateBox,
|
UpdateBox,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
|
UpdateDataPanelLayout,
|
||||||
|
UpdateDataPanelState,
|
||||||
UpdateDialogButtons,
|
UpdateDialogButtons,
|
||||||
UpdateDialogColumn1,
|
UpdateDialogColumn1,
|
||||||
UpdateDialogColumn2,
|
UpdateDialogColumn2,
|
||||||
@@ -1735,18 +1723,18 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateGraphViewOverlay,
|
UpdateGraphViewOverlay,
|
||||||
UpdateImportReorderIndex,
|
UpdateImportReorderIndex,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateInputHints,
|
|
||||||
UpdateInSelectedNetwork,
|
UpdateInSelectedNetwork,
|
||||||
UpdateLayersPanelBottomBarLayout,
|
UpdateLayersPanelBottomBarLayout,
|
||||||
UpdateLayersPanelControlBarLeftLayout,
|
UpdateLayersPanelControlBarLeftLayout,
|
||||||
UpdateLayersPanelControlBarRightLayout,
|
UpdateLayersPanelControlBarRightLayout,
|
||||||
|
UpdateLayersPanelState,
|
||||||
UpdateLayerWidths,
|
UpdateLayerWidths,
|
||||||
UpdateMaximized,
|
UpdateMaximized,
|
||||||
UpdateMenuBarLayout,
|
UpdateMenuBarLayout,
|
||||||
UpdateMouseCursor,
|
UpdateMouseCursor,
|
||||||
UpdateNodeGraphControlBarLayout,
|
UpdateNodeGraphControlBarLayout,
|
||||||
UpdateNodeGraphNodes,
|
|
||||||
UpdateNodeGraphErrorDiagnostic,
|
UpdateNodeGraphErrorDiagnostic,
|
||||||
|
UpdateNodeGraphNodes,
|
||||||
UpdateNodeGraphSelection,
|
UpdateNodeGraphSelection,
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeGraphWires,
|
UpdateNodeGraphWires,
|
||||||
@@ -1754,15 +1742,14 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateOpenDocumentsList,
|
UpdateOpenDocumentsList,
|
||||||
UpdatePlatform,
|
UpdatePlatform,
|
||||||
UpdatePropertiesPanelLayout,
|
UpdatePropertiesPanelLayout,
|
||||||
UpdateDataPanelLayout,
|
|
||||||
UpdateDataPanelState,
|
|
||||||
UpdatePropertiesPanelState,
|
UpdatePropertiesPanelState,
|
||||||
UpdateLayersPanelState,
|
UpdateStatusBarHintsLayout,
|
||||||
UpdateToolOptionsLayout,
|
UpdateToolOptionsLayout,
|
||||||
UpdateToolShelfLayout,
|
UpdateToolShelfLayout,
|
||||||
UpdateViewportHolePunch,
|
UpdateViewportHolePunch,
|
||||||
UpdateViewportPhysicalBounds,
|
UpdateViewportPhysicalBounds,
|
||||||
UpdateVisibleNodes,
|
UpdateVisibleNodes,
|
||||||
|
UpdateWelcomeScreenButtonsLayout,
|
||||||
UpdateWirePathInProgress,
|
UpdateWirePathInProgress,
|
||||||
UpdateWorkingColorsLayout,
|
UpdateWorkingColorsLayout,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { plainToInstance } from "class-transformer";
|
import { plainToInstance } from "class-transformer";
|
||||||
|
|
||||||
import { type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm.js";
|
import { type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
import { type JsMessageType, messageMakers, type JsMessage } from "@graphite/messages";
|
import { type JsMessageType, messageMakers, type JsMessage } from "@graphite/messages";
|
||||||
|
|
||||||
type JsMessageCallback<T extends JsMessage> = (messageData: T) => void;
|
type JsMessageCallback<T extends JsMessage> = (messageData: T) => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isPlatformNative } from "@graphite/../wasm/pkg/graphite_wasm.js";
|
import { isPlatformNative } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
|
|
||||||
export function browserVersion(): string {
|
export function browserVersion(): string {
|
||||||
const agent = window.navigator.userAgent;
|
const agent = window.navigator.userAgent;
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
use crate::helpers::translate_key;
|
use crate::helpers::translate_key;
|
||||||
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
|
use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER};
|
||||||
use editor::consts::FILE_EXTENSION;
|
use editor::consts::FILE_EXTENSION;
|
||||||
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
|
use editor::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, ModifierKeys};
|
||||||
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta};
|
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta};
|
||||||
|
use editor::messages::input_mapper::utility_types::misc::ActionShortcut;
|
||||||
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport;
|
use editor::messages::portfolio::document::utility_types::network_interface::ImportOrExport;
|
||||||
use editor::messages::portfolio::utility_types::Platform;
|
use editor::messages::portfolio::utility_types::Platform;
|
||||||
@@ -67,6 +68,18 @@ pub fn is_platform_native() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = shortcutAltClick)]
|
||||||
|
pub fn shortcut_alt_click() -> JsValue {
|
||||||
|
let shortcut = Some(ActionShortcut::Shortcut(KeysGroup(vec![Key::Alt, Key::MouseLeft]).into()));
|
||||||
|
serde_wasm_bindgen::to_value(&shortcut).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = shortcutF11)]
|
||||||
|
pub fn shortcut_f11() -> JsValue {
|
||||||
|
let shortcut = Some(ActionShortcut::Shortcut(KeysGroup(vec![Key::F11]).into()));
|
||||||
|
serde_wasm_bindgen::to_value(&shortcut).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// This struct is, via wasm-bindgen, used by JS to interact with the editor backend. It does this by calling functions, which are `impl`ed
|
/// This struct is, via wasm-bindgen, used by JS to interact with the editor backend. It does this by calling functions, which are `impl`ed
|
||||||
@@ -392,15 +405,9 @@ impl EditorHandle {
|
|||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = openDocument)]
|
#[wasm_bindgen(js_name = requestWelcomeScreenButtonsLayout)]
|
||||||
pub fn open_document(&self) {
|
pub fn request_welcome_screen_buttons_layout(&self) {
|
||||||
let message = PortfolioMessage::OpenDocument;
|
let message = PortfolioMessage::RequestWelcomeScreenButtonsLayout;
|
||||||
self.dispatch(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = demoArtworkDialog)]
|
|
||||||
pub fn demo_artwork_dialog(&self) {
|
|
||||||
let message = DialogMessage::RequestDemoArtworkDialog;
|
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,13 +644,6 @@ impl EditorHandle {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the given URL
|
|
||||||
#[wasm_bindgen(js_name = visitUrl)]
|
|
||||||
pub fn visit_url(&self, url: String) {
|
|
||||||
let message = FrontendMessage::TriggerVisitLink { url };
|
|
||||||
self.dispatch(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Paste layers from a serialized JSON representation
|
/// Paste layers from a serialized JSON representation
|
||||||
#[wasm_bindgen(js_name = pasteSerializedData)]
|
#[wasm_bindgen(js_name = pasteSerializedData)]
|
||||||
pub fn paste_serialized_data(&self, data: String) {
|
pub fn paste_serialized_data(&self, data: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user