Bundle Graphite using Tauri (#873)

* Setup tauri component for graphite editor

Integrate graphite into tauri app

Split interpreted-executor out of graph-craft

* Add gpu execution node

* General Cleanup
This commit is contained in:
TrueDoctor
2022-12-11 12:46:55 -08:00
committed by Keavon Chambers
parent a2043bcb7c
commit f1b7abede7
109 changed files with 5661 additions and 544 deletions
@@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::layout_widget::SubLayout;
use crate::messages::layout::utility_types::misc::LayoutTarget;
use crate::messages::layout::utility_types::widgets::menu_widgets::MenuBarEntry;
use crate::messages::portfolio::document::node_graph::{FrontendNode, FrontendNodeLink, FrontendNodeType};
use crate::messages::portfolio::document::utility_types::layer_panel::{LayerPanelEntry, RawBuffer};
use crate::messages::portfolio::document::utility_types::layer_panel::{JsRawBuffer, LayerPanelEntry, RawBuffer};
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData;
@@ -156,6 +156,10 @@ pub enum FrontendMessage {
#[serde(rename = "dataBuffer")]
data_buffer: RawBuffer,
},
UpdateDocumentLayerTreeStructureJs {
#[serde(rename = "dataBuffer")]
data_buffer: JsRawBuffer,
},
UpdateDocumentModeLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
@@ -16,7 +16,7 @@ pub struct FrontendImageData {
pub path: Vec<LayerId>,
pub mime: String,
#[serde(skip)]
pub image_data: std::rc::Rc<Vec<u8>>,
pub image_data: std::sync::Arc<Vec<u8>>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
@@ -4,7 +4,6 @@ use crate::messages::prelude::*;
pub use graphene::DocumentResponse;
use bitflags::bitflags;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
@@ -51,7 +50,7 @@ bitflags! {
// (although we ignore the shift key, so the user doesn't have to press `Ctrl Shift +` on a US keyboard), even if the keyboard layout
// is for a different locale where the `+` key is somewhere entirely different, shifted or not. This would then also work for numpad `+`.
#[impl_message(Message, InputMapperMessage, KeyDown)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub enum Key {
// Writing system keys
Digit0,
@@ -210,18 +209,6 @@ pub enum Key {
NumKeys,
}
impl Serialize for Key {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let key = format!("{:?}", self);
let label = self.to_string();
let mut state = serializer.serialize_struct("KeyWithLabel", 2)?;
state.serialize_field("key", &key)?;
state.serialize_field("label", &label)?;
state.end()
}
}
impl fmt::Display for Key {
// TODO: Relevant key labels should be localized when we get around to implementing localization/internationalization
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
@@ -308,6 +295,35 @@ impl fmt::Display for Key {
}
}
impl From<Key> for LayoutKey {
fn from(key: Key) -> Self {
Self {
key: format!("{:?}", key),
label: key.to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
struct LayoutKey {
key: String,
label: String,
}
/*
impl Serialize for Key {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let key = format!("{:?}", self.0);
let label = self.0.to_string();
assert_eq!(serde_json::to_string(Key::KeyEscape), {"key": KeyEscape, "label": "Esc"});
let mut state = serializer.serialize_struct("KeyWithLabel", 2)?;
state.serialize_field("key", &key)?;
state.serialize_field("label", &label)?;
state.end()
}
}*/
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
/// Only `Key`s that exist on a physical keyboard should be used.
@@ -342,6 +358,22 @@ impl fmt::Display for KeysGroup {
}
}
impl From<KeysGroup> for String {
fn from(keys: KeysGroup) -> Self {
let layout_keys: LayoutKeysGroup = keys.into();
serde_json::to_string(&layout_keys).expect("Failed to serialize KeysGroup")
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayoutKeysGroup(Vec<LayoutKey>);
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, Serialize, Deserialize)]
pub enum MouseMotion {
None,
@@ -1,4 +1,4 @@
use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup};
use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup, LayoutKeysGroup};
use crate::messages::input_mapper::default_mapping::default_mapping;
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
use crate::messages::prelude::*;
@@ -81,24 +81,27 @@ pub struct MappingEntry {
pub enum ActionKeys {
Action(MessageDiscriminant),
#[serde(rename = "keys")]
Keys(KeysGroup),
Keys(LayoutKeysGroup),
}
impl ActionKeys {
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> String {
match self {
ActionKeys::Action(action) => {
if let Some(keys) = action_input_mapping(action).get_mut(0) {
let mut taken_keys = KeysGroup::default();
std::mem::swap(keys, &mut taken_keys);
*self = ActionKeys::Keys(taken_keys);
let description = taken_keys.to_string();
*self = ActionKeys::Keys(taken_keys.into());
description
} else {
*self = ActionKeys::Keys(KeysGroup::default());
*self = ActionKeys::Keys(KeysGroup::default().into());
String::new()
}
}
ActionKeys::Keys(keys) => {
warn!("Calling `.to_keys()` on a `ActionKeys::Keys` is a mistake/bug. Keys are: {:?}.", keys);
String::new()
}
}
}
@@ -10,7 +10,7 @@ use crate::messages::layout::utility_types::misc::LayoutTarget;
use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
use std::sync::Arc;
pub trait PropertyHolder {
fn properties(&self) -> Layout {
@@ -39,11 +39,9 @@ impl Layout {
if let Layout::WidgetLayout(mut widget_layout) = self {
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
tooltip_shortcut.to_keys(action_input_mapping);
if let ActionKeys::Keys(keys) = tooltip_shortcut {
let shortcut_text = keys.to_string();
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
if let ActionKeys::Keys(_keys) = tooltip_shortcut {
if !shortcut_text.is_empty() {
if !tooltip.is_empty() {
tooltip.push(' ');
@@ -279,12 +277,12 @@ impl WidgetHolder {
#[derive(Clone)]
pub struct WidgetCallback<T> {
pub callback: Rc<dyn Fn(&T) -> Message + 'static>,
pub callback: Arc<dyn Fn(&T) -> Message + 'static + Send + Sync>,
}
impl<T> WidgetCallback<T> {
pub fn new(callback: impl Fn(&T) -> Message + 'static) -> Self {
Self { callback: Rc::new(callback) }
pub fn new(callback: impl Fn(&T) -> Message + 'static + Send + Sync) -> Self {
Self { callback: Arc::new(callback) }
}
}
@@ -48,7 +48,7 @@ impl MenuBarEntry {
}
}
pub fn create_action(callback: impl Fn(&()) -> Message + 'static) -> WidgetHolder {
pub fn create_action(callback: impl Fn(&()) -> Message + 'static + Send + Sync) -> WidgetHolder {
WidgetHolder::new(Widget::InvisibleStandinInput(InvisibleStandinInput {
on_update: WidgetCallback::new(callback),
}))
@@ -548,7 +548,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
.into(),
);
let image_data = std::rc::Rc::new(image_data);
let image_data = std::sync::Arc::new(image_data);
responses.push_back(
FrontendMessage::UpdateImageData {
document_id,
@@ -162,7 +162,7 @@ impl MessageHandler<NavigationMessage, (&Document, &InputPreprocessorMessageHand
responses.push_back(
FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
@@ -244,7 +244,7 @@ impl MessageHandler<NavigationMessage, (&Document, &InputPreprocessorMessageHand
responses.push_back(
FrontendMessage::UpdateInputHints {
hint_data: HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap Increments"),
@@ -18,6 +18,8 @@ pub enum FrontendGraphDataType {
Raster,
#[serde(rename = "color")]
Color,
#[serde(rename = "text")]
Text,
#[serde(rename = "vector")]
Subpath,
#[serde(rename = "number")]
@@ -2,14 +2,11 @@ use super::{node_properties, FrontendGraphDataType, FrontendNodeType};
use crate::messages::layout::utility_types::layout_widget::{LayoutGroup, Widget, WidgetHolder};
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
use glam::DVec2;
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graph_craft::proto::{NodeIdentifier, Type};
use graphene_std::raster::Image;
use graphene_std::vector::subpath::Subpath;
use std::borrow::Cow;
use graphene_core::raster::Image;
pub struct DocumentInputType {
pub name: &'static str,
@@ -39,7 +36,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Identity",
category: "General",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
@@ -58,7 +55,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Input",
category: "Meta",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::Raster,
@@ -70,7 +67,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Output",
category: "Meta",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::Raster,
@@ -87,6 +84,21 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::no_properties,
},
DocumentNodeType {
name: "GpuImage",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType {
name: "Path",
data_type: FrontendGraphDataType::Text,
default: NodeInput::value(TaggedValue::String(String::new()), true),
},
],
outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::gpu_map_properties,
},
DocumentNodeType {
name: "Invert RGB",
category: "Image Adjustments",
@@ -98,7 +110,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Hue/Saturation",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false),
@@ -111,7 +123,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Brightness/Contrast",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Brightness", TaggedValue::F64(0.), false),
@@ -123,7 +135,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Gamma",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Gamma", TaggedValue::F64(1.), false),
@@ -134,7 +146,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Opacity",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Factor", TaggedValue::F64(1.), false),
@@ -145,7 +157,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Posterize",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Value", TaggedValue::F64(5.), false),
@@ -156,7 +168,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Exposure",
category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Value", TaggedValue::F64(0.), false),
@@ -167,7 +179,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Add",
category: "Math",
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]),
inputs: &[
DocumentInputType::new("Input", TaggedValue::F64(0.), true),
DocumentInputType::new("Addend", TaggedValue::F64(0.), true),
@@ -194,7 +206,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType {
name: "Path Generator",
category: "Vector",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType {
name: "Path Data",
data_type: FrontendGraphDataType::Subpath,
@@ -1,6 +1,6 @@
use crate::messages::layout::utility_types::layout_widget::{LayoutGroup, Widget, WidgetCallback, WidgetHolder};
use crate::messages::layout::utility_types::widgets::button_widgets::ParameterExposeButton;
use crate::messages::layout::utility_types::widgets::input_widgets::{NumberInput, NumberInputMode};
use crate::messages::layout::utility_types::widgets::input_widgets::{NumberInput, NumberInputMode, TextInput};
use crate::messages::prelude::NodeGraphMessage;
use glam::DVec2;
@@ -14,7 +14,7 @@ pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
vec![LayoutGroup::Row { widgets: vec![widget] }]
}
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static + Send + Sync>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
WidgetCallback::new(move |number_input: &T| {
NodeGraphMessage::SetInputValue {
node: node_id,
@@ -42,6 +42,32 @@ fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType
}))
}
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str) -> Vec<WidgetHolder> {
let input: &NodeInput = document_node.inputs.get(index).unwrap();
let mut widgets = vec![
expose_widget(node_id, index, FrontendGraphDataType::Number, input.is_exposed()),
WidgetHolder::unrelated_seperator(),
WidgetHolder::text_widget(name),
];
if let NodeInput::Value {
tagged_value: TaggedValue::String(x),
exposed: false,
} = &document_node.inputs[index]
{
widgets.extend_from_slice(&[
WidgetHolder::unrelated_seperator(),
WidgetHolder::new(Widget::TextInput(TextInput {
value: x.clone(),
on_update: update_value(|x: &TextInput| TaggedValue::String(x.value.clone()), node_id, index),
..TextInput::default()
})),
])
}
widgets
}
fn number_range_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, range_min: Option<f64>, range_max: Option<f64>, unit: String, is_integer: bool) -> Vec<WidgetHolder> {
let input: &NodeInput = document_node.inputs.get(index).unwrap();
@@ -98,6 +124,12 @@ pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId) ->
vec![LayoutGroup::Row { widgets: gamma }]
}
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
let map = text_widget(document_node, node_id, 1, "Map");
vec![LayoutGroup::Row { widgets: map }]
}
pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
let gamma = number_range_widget(document_node, node_id, 1, "Factor", Some(0.), Some(1.), "".into(), false);
@@ -21,7 +21,7 @@ use graphene::layers::text_layer::{FontCache, TextLayer};
use glam::{DAffine2, DVec2};
use std::f64::consts::PI;
use std::rc::Rc;
use std::sync::Arc;
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, font_cache: &FontCache) -> [f64; 6] {
let transformation = match transform_op {
@@ -1457,7 +1457,7 @@ fn node_gradient_type(gradient: &Gradient) -> LayoutGroup {
}
fn node_gradient_color(gradient: &Gradient, position: usize) -> LayoutGroup {
let gradient_clone = Rc::new(gradient.clone());
let gradient_clone = Arc::new(gradient.clone());
let gradient_2 = gradient_clone.clone();
let gradient_3 = gradient_clone.clone();
let send_fill_message = move |new_gradient: Gradient| PropertiesPanelMessage::ModifyFill { fill: Fill::Gradient(new_gradient) }.into();
@@ -7,7 +7,7 @@ use glam::{DAffine2, DVec2};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RawBuffer(Vec<u8>);
impl From<Vec<u64>> for RawBuffer {
@@ -22,8 +22,15 @@ impl From<Vec<u64>> for RawBuffer {
Self(v_from_raw)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct JsRawBuffer(Vec<u8>);
impl Serialize for RawBuffer {
impl From<RawBuffer> for JsRawBuffer {
fn from(buffer: RawBuffer) -> Self {
Self(buffer.0)
}
}
impl Serialize for JsRawBuffer {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buffer = serializer.serialize_struct("Buffer", 2)?;
buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?;
@@ -420,12 +420,12 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
size,
} => {
fn read_image(document: Option<&DocumentMessageHandler>, layer_path: &[LayerId], image_data: Vec<u8>, (width, height): (u32, u32)) -> Result<Vec<u8>, String> {
use graphene_std::raster::Image;
use graphene_core::raster::Image;
use image::{ImageBuffer, Rgba};
use std::io::Cursor;
let data = image_data.chunks_exact(4).map(|v| graphene_core::raster::color::Color::from_rgba8(v[0], v[1], v[2], v[3])).collect();
let image = graphene_std::raster::Image { width, height, data };
let image = graphene_core::raster::Image { width, height, data };
let document = document.ok_or_else(|| "Invalid document".to_string())?;
let layer = document.graphene_document.layer(layer_path).map_err(|e| format!("No layer: {e:?}"))?;
@@ -452,7 +452,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
for (_id, node) in proto_network.nodes {
info!("Inserting proto node {:?}", node);
graph_craft::node_registry::push_node(node, &stack);
interpreted_executor::node_registry::push_node(node, &stack);
}
use borrow_stack::BorrowStack;
@@ -484,7 +484,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
.into(),
);
let mime = "image/bmp".to_string();
let image_data = std::rc::Rc::new(image_data);
let image_data = std::sync::Arc::new(image_data);
responses.push_back(
FrontendMessage::UpdateImageData {
document_id,
@@ -453,7 +453,7 @@ impl Fsm for ArtboardToolFsmState {
plus: false,
}]),
HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Backspace])],
key_groups: vec![KeysGroup(vec![Key::Backspace]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Delete Artboard"),
@@ -461,7 +461,7 @@ impl Fsm for ArtboardToolFsmState {
}]),
]),
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain to Axis"),
@@ -469,14 +469,14 @@ impl Fsm for ArtboardToolFsmState {
}])]),
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -191,14 +191,14 @@ impl Fsm for EllipseToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Circular"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -207,14 +207,14 @@ impl Fsm for EllipseToolFsmState {
])]),
EllipseToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Circular"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -178,7 +178,7 @@ impl Fsm for EyedropperToolFsmState {
},
])]),
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Escape])],
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Cancel"),
@@ -518,7 +518,7 @@ impl Fsm for GradientToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
@@ -526,7 +526,7 @@ impl Fsm for GradientToolFsmState {
},
])]),
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
@@ -190,14 +190,14 @@ impl Fsm for ImaginateToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -206,14 +206,14 @@ impl Fsm for ImaginateToolFsmState {
])]),
ImaginateToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -247,21 +247,21 @@ impl Fsm for LineToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Lock Angle"),
@@ -270,21 +270,21 @@ impl Fsm for LineToolFsmState {
])]),
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Lock Angle"),
@@ -201,7 +201,7 @@ impl Fsm for NavigateToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Zoom Out"),
@@ -217,7 +217,7 @@ impl Fsm for NavigateToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap Increments"),
@@ -240,7 +240,7 @@ impl Fsm for NavigateToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
@@ -249,14 +249,14 @@ impl Fsm for NavigateToolFsmState {
]),
]),
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
plus: false,
}])]),
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap Increments"),
@@ -190,14 +190,14 @@ impl Fsm for NodeGraphToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -206,14 +206,14 @@ impl Fsm for NodeGraphToolFsmState {
])]),
NodeGraphToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -322,7 +322,7 @@ impl Fsm for PathToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Grow/Shrink Selection"),
@@ -339,10 +339,10 @@ impl Fsm for PathToolFsmState {
HintGroup(vec![
HintInfo {
key_groups: vec![
KeysGroup(vec![Key::ArrowUp]),
KeysGroup(vec![Key::ArrowRight]),
KeysGroup(vec![Key::ArrowDown]),
KeysGroup(vec![Key::ArrowLeft]),
KeysGroup(vec![Key::ArrowUp]).into(),
KeysGroup(vec![Key::ArrowRight]).into(),
KeysGroup(vec![Key::ArrowDown]).into(),
KeysGroup(vec![Key::ArrowLeft]).into(),
],
key_groups_mac: None,
mouse: None,
@@ -350,7 +350,7 @@ impl Fsm for PathToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Big Increment Nudge"),
@@ -359,21 +359,21 @@ impl Fsm for PathToolFsmState {
]),
HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyG])],
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Grab Selected (coming soon)"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyR])],
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Rotate Selected (coming soon)"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyS])],
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Scale Selected (coming soon)"),
@@ -383,14 +383,14 @@ impl Fsm for PathToolFsmState {
]),
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Split/Align Handles (Toggle)"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Share Lengths of Aligned Handles"),
@@ -636,21 +636,21 @@ impl Fsm for PenToolFsmState {
plus: false,
}]),
HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
plus: false,
}]),
HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Break Handle"),
plus: false,
}]),
HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Enter])],
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("End Path"),
@@ -192,14 +192,14 @@ impl Fsm for RectangleToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -208,14 +208,14 @@ impl Fsm for RectangleToolFsmState {
])]),
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain Square"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -793,21 +793,21 @@ impl Fsm for SelectToolFsmState {
}]),
HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyG])],
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Grab Selected"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyR])],
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Rotate Selected"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyS])],
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Scale Selected"),
@@ -823,14 +823,14 @@ impl Fsm for SelectToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command])]),
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command]).into()]),
mouse: None,
label: String::from("Innermost"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Grow/Shrink Selection"),
@@ -846,7 +846,7 @@ impl Fsm for SelectToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Grow/Shrink Selection"),
@@ -856,10 +856,10 @@ impl Fsm for SelectToolFsmState {
HintGroup(vec![
HintInfo {
key_groups: vec![
KeysGroup(vec![Key::ArrowUp]),
KeysGroup(vec![Key::ArrowRight]),
KeysGroup(vec![Key::ArrowDown]),
KeysGroup(vec![Key::ArrowLeft]),
KeysGroup(vec![Key::ArrowUp]).into(),
KeysGroup(vec![Key::ArrowRight]).into(),
KeysGroup(vec![Key::ArrowDown]).into(),
KeysGroup(vec![Key::ArrowLeft]).into(),
],
key_groups_mac: None,
mouse: None,
@@ -867,7 +867,7 @@ impl Fsm for SelectToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Big Increment Nudge"),
@@ -876,15 +876,15 @@ impl Fsm for SelectToolFsmState {
]),
HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: Some(MouseMotion::LmbDrag),
label: String::from("Move Duplicate"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control, Key::KeyD])],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::KeyD])]),
key_groups: vec![KeysGroup(vec![Key::Control, Key::KeyD]).into()],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::KeyD]).into()]),
mouse: None,
label: String::from("Duplicate"),
plus: false,
@@ -893,14 +893,14 @@ impl Fsm for SelectToolFsmState {
]),
SelectToolFsmState::Dragging => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain to Axis"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap to Points (coming soon)"),
@@ -910,7 +910,7 @@ impl Fsm for SelectToolFsmState {
SelectToolFsmState::DrawingBox => HintData(vec![]),
SelectToolFsmState::ResizingBounds => HintData(vec![]),
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control])],
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Snap 15°"),
@@ -235,14 +235,14 @@ impl Fsm for ShapeToolFsmState {
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain 1:1 Aspect"),
plus: true,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -251,14 +251,14 @@ impl Fsm for ShapeToolFsmState {
])]),
ShapeToolFsmState::Drawing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Shift])],
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Constrain 1:1 Aspect"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Alt])],
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("From Center"),
@@ -267,7 +267,7 @@ impl Fsm for SplineToolFsmState {
plus: false,
}]),
HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::Enter])],
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("End Spline"),
@@ -474,14 +474,14 @@ impl Fsm for TextToolFsmState {
])]),
TextToolFsmState::Editing => HintData(vec![HintGroup(vec![
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter])],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::Enter])]),
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter]).into()],
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::Enter]).into()]),
mouse: None,
label: String::from("Commit Edit"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::Escape])],
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
key_groups_mac: None,
mouse: None,
label: String::from("Discard Edit"),
+4 -4
View File
@@ -1,5 +1,5 @@
use super::tool_messages::*;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::input_keyboard::LayoutKeysGroup;
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
@@ -21,7 +21,7 @@ pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, u64, &'a Docum
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
type Tool = dyn ToolCommon;
type Tool = dyn ToolCommon + Send + Sync;
pub trait Fsm {
type ToolData;
@@ -442,10 +442,10 @@ pub struct HintInfo {
/// A `KeysGroup` specifies all the keys pressed simultaneously to perform an action (like "Ctrl C" to copy).
/// Usually at most one is given, but less commonly, multiple can be used to describe additional hotkeys not used simultaneously (like the four different arrow keys to nudge a layer).
#[serde(rename = "keyGroups")]
pub key_groups: Vec<KeysGroup>,
pub key_groups: Vec<LayoutKeysGroup>,
/// `None` means that the regular `key_groups` should be used for all platforms, `Some` is an override for a Mac-only input hint.
#[serde(rename = "keyGroupsMac")]
pub key_groups_mac: Option<Vec<KeysGroup>>,
pub key_groups_mac: Option<Vec<LayoutKeysGroup>>,
/// An optional `MouseMotion` that can indicate the mouse action, like which mouse button is used and whether a drag occurs.
/// No such icon is shown if `None` is given, and it can be combined with `key_groups` if desired.
pub mouse: Option<MouseMotion>,