mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 03:48:12 +08:00
Improve backwards compatability robustness of serde-based document format
This commit is contained in:
@@ -42,6 +42,7 @@ pub struct DocumentMessageData<'a> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct DocumentMessageHandler {
|
||||
// ======================
|
||||
// Child message handlers
|
||||
@@ -62,44 +63,32 @@ pub struct DocumentMessageHandler {
|
||||
//
|
||||
/// The node graph that generates this document's artwork.
|
||||
/// It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content.
|
||||
#[serde(default = "default_network")]
|
||||
pub network: NodeNetwork,
|
||||
/// List of the [`NodeId`]s that are currently selected by the user.
|
||||
#[serde(default = "default_selected_nodes")]
|
||||
pub selected_nodes: SelectedNodes,
|
||||
/// List of the [`LayerNodeIdentifier`]s that are currently collapsed by the user in the Layers panel.
|
||||
/// Collapsed means that the expansion arrow isn't set to show the children of these layers.
|
||||
#[serde(default = "default_collapsed")]
|
||||
pub collapsed: CollapsedLayers,
|
||||
/// The name of the document, which is displayed in the tab and title bar of the editor.
|
||||
#[serde(default = "default_name")]
|
||||
pub name: String,
|
||||
/// The full Git commit hash of the Graphite repository that was used to build the editor.
|
||||
/// We save this to provide a hint about which version of the editor was used to create the document.
|
||||
#[serde(default = "default_commit_hash")]
|
||||
commit_hash: String,
|
||||
/// The current pan, tilt, and zoom state of the viewport's view of the document canvas.
|
||||
#[serde(default = "default_pan_tilt_zoom")]
|
||||
pub navigation: PTZ,
|
||||
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
||||
#[serde(default = "default_document_mode")]
|
||||
document_mode: DocumentMode,
|
||||
/// The current view mode that the user has set for rendering the document within the viewport.
|
||||
/// This is usually "Normal" but can be set to "Outline" or "Pixels" to see the canvas differently.
|
||||
#[serde(default = "default_view_mode")]
|
||||
pub view_mode: ViewMode,
|
||||
/// Sets whether or not all the viewport overlays should be drawn on top of the artwork.
|
||||
/// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more.
|
||||
#[serde(default = "default_overlays_visible")]
|
||||
overlays_visible: bool,
|
||||
/// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area.
|
||||
#[serde(default = "default_rulers_visible")]
|
||||
pub rulers_visible: bool,
|
||||
/// Sets whether or not the node graph is drawn (as an overlay) on top of the viewport area, or otherwise if it's hidden.
|
||||
#[serde(default = "default_graph_view_overlay_open")]
|
||||
graph_view_overlay_open: bool,
|
||||
/// The current user choices for snapping behavior, including whether snapping is enabled at all.
|
||||
#[serde(default = "default_snapping_state")]
|
||||
pub snapping_state: SnappingState,
|
||||
|
||||
// =============================================
|
||||
@@ -131,6 +120,45 @@ pub struct DocumentMessageHandler {
|
||||
pub metadata: DocumentMetadata,
|
||||
}
|
||||
|
||||
impl Default for DocumentMessageHandler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// ======================
|
||||
// Child message handlers
|
||||
// ======================
|
||||
navigation_handler: NavigationMessageHandler::default(),
|
||||
node_graph_handler: NodeGraphMessageHandler::default(),
|
||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||
// ============================================
|
||||
// Fields that are saved in the document format
|
||||
// ============================================
|
||||
network: root_network(),
|
||||
selected_nodes: SelectedNodes::default(),
|
||||
collapsed: CollapsedLayers::default(),
|
||||
name: DEFAULT_DOCUMENT_NAME.to_string(),
|
||||
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
||||
navigation: PTZ::default(),
|
||||
document_mode: DocumentMode::DesignMode,
|
||||
view_mode: ViewMode::default(),
|
||||
overlays_visible: true,
|
||||
rulers_visible: true,
|
||||
graph_view_overlay_open: false,
|
||||
snapping_state: SnappingState::default(),
|
||||
// =============================================
|
||||
// Fields omitted from the saved document format
|
||||
// =============================================
|
||||
document_undo_history: VecDeque::new(),
|
||||
document_redo_history: VecDeque::new(),
|
||||
saved_hash: None,
|
||||
auto_saved_hash: None,
|
||||
undo_in_progress: false,
|
||||
layer_range_selection_reference: None,
|
||||
metadata: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
|
||||
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
|
||||
let DocumentMessageData {
|
||||
@@ -1913,94 +1941,6 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DocumentMessageHandler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// ======================
|
||||
// Child message handlers
|
||||
// ======================
|
||||
navigation_handler: NavigationMessageHandler::default(),
|
||||
node_graph_handler: NodeGraphMessageHandler::default(),
|
||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||
// ============================================
|
||||
// Fields that are saved in the document format
|
||||
// ============================================
|
||||
network: root_network(),
|
||||
selected_nodes: SelectedNodes::default(),
|
||||
collapsed: CollapsedLayers::default(),
|
||||
name: DEFAULT_DOCUMENT_NAME.to_string(),
|
||||
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
||||
navigation: PTZ::default(),
|
||||
document_mode: DocumentMode::DesignMode,
|
||||
view_mode: ViewMode::default(),
|
||||
overlays_visible: true,
|
||||
rulers_visible: true,
|
||||
// =============================================
|
||||
// Fields omitted from the saved document format
|
||||
// =============================================
|
||||
document_undo_history: VecDeque::new(),
|
||||
document_redo_history: VecDeque::new(),
|
||||
saved_hash: None,
|
||||
auto_saved_hash: None,
|
||||
undo_in_progress: false,
|
||||
graph_view_overlay_open: false,
|
||||
snapping_state: SnappingState::default(),
|
||||
layer_range_selection_reference: None,
|
||||
metadata: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default_network() -> NodeNetwork {
|
||||
DocumentMessageHandler::default().network
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_selected_nodes() -> SelectedNodes {
|
||||
DocumentMessageHandler::default().selected_nodes
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_collapsed() -> CollapsedLayers {
|
||||
DocumentMessageHandler::default().collapsed
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_name() -> String {
|
||||
DocumentMessageHandler::default().name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_commit_hash() -> String {
|
||||
DocumentMessageHandler::default().commit_hash
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_pan_tilt_zoom() -> PTZ {
|
||||
DocumentMessageHandler::default().navigation
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_document_mode() -> DocumentMode {
|
||||
DocumentMessageHandler::default().document_mode
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_view_mode() -> ViewMode {
|
||||
DocumentMessageHandler::default().view_mode
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_overlays_visible() -> bool {
|
||||
DocumentMessageHandler::default().overlays_visible
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_rulers_visible() -> bool {
|
||||
DocumentMessageHandler::default().rulers_visible
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_graph_view_overlay_open() -> bool {
|
||||
DocumentMessageHandler::default().graph_view_overlay_open
|
||||
}
|
||||
#[inline(always)]
|
||||
fn default_snapping_state() -> SnappingState {
|
||||
DocumentMessageHandler::default().snapping_state
|
||||
}
|
||||
|
||||
fn root_network() -> NodeNetwork {
|
||||
{
|
||||
let mut network = NodeNetwork::default();
|
||||
|
||||
@@ -356,19 +356,6 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
number_props
|
||||
.value(Some(x as f64))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F32((x.value.unwrap()) as f32), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl FrontendGraphDataType {
|
||||
pub const fn with_tagged_value(value: &TaggedValue) -> Self {
|
||||
match value {
|
||||
TaggedValue::String(_) => Self::Text,
|
||||
TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
|
||||
TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
|
||||
TaggedValue::Bool(_) => Self::Boolean,
|
||||
TaggedValue::DVec2(_) | TaggedValue::IVec2(_) => Self::Vector,
|
||||
TaggedValue::Image(_) => Self::Raster,
|
||||
|
||||
@@ -56,8 +56,9 @@ impl DocumentMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
/// SnappingState determines the current individual snapping states
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct SnappingState {
|
||||
pub snapping_enabled: bool,
|
||||
pub grid_snapping: bool,
|
||||
@@ -67,39 +68,21 @@ pub struct SnappingState {
|
||||
pub tolerance: f64,
|
||||
pub artboards: bool,
|
||||
}
|
||||
|
||||
impl Default for SnappingState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
snapping_enabled: true,
|
||||
grid_snapping: false,
|
||||
bounds: BoundsSnapping {
|
||||
edges: true,
|
||||
corners: true,
|
||||
edge_midpoints: false,
|
||||
centers: true,
|
||||
},
|
||||
nodes: PointSnapping {
|
||||
paths: true,
|
||||
path_intersections: true,
|
||||
anchors: true,
|
||||
line_midpoints: true,
|
||||
normals: true,
|
||||
tangents: true,
|
||||
},
|
||||
grid: GridSnapping {
|
||||
origin: DVec2::ZERO,
|
||||
grid_type: GridType::RECTANGLE,
|
||||
grid_color: COLOR_OVERLAY_GRAY
|
||||
.strip_prefix("#")
|
||||
.and_then(|value| Color::from_rgb_str(value))
|
||||
.expect("Should create Color from prefixed hex string"),
|
||||
dot_display: false,
|
||||
},
|
||||
bounds: Default::default(),
|
||||
nodes: Default::default(),
|
||||
grid: Default::default(),
|
||||
tolerance: 8.,
|
||||
artboards: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SnappingState {
|
||||
pub const fn target_enabled(&self, target: SnapTarget) -> bool {
|
||||
if !self.snapping_enabled {
|
||||
@@ -127,13 +110,27 @@ impl SnappingState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct BoundsSnapping {
|
||||
pub edges: bool,
|
||||
pub corners: bool,
|
||||
pub edge_midpoints: bool,
|
||||
pub centers: bool,
|
||||
}
|
||||
|
||||
impl Default for BoundsSnapping {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
edges: true,
|
||||
corners: true,
|
||||
edge_midpoints: false,
|
||||
centers: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OptionBoundsSnapping {
|
||||
pub edges: Option<bool>,
|
||||
@@ -141,7 +138,9 @@ pub struct OptionBoundsSnapping {
|
||||
pub edge_midpoints: Option<bool>,
|
||||
pub centers: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct PointSnapping {
|
||||
pub paths: bool,
|
||||
pub path_intersections: bool,
|
||||
@@ -150,6 +149,20 @@ pub struct PointSnapping {
|
||||
pub normals: bool,
|
||||
pub tangents: bool,
|
||||
}
|
||||
|
||||
impl Default for PointSnapping {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
paths: true,
|
||||
path_intersections: true,
|
||||
anchors: true,
|
||||
line_midpoints: true,
|
||||
normals: true,
|
||||
tangents: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OptionPointSnapping {
|
||||
pub paths: Option<bool>,
|
||||
@@ -159,11 +172,19 @@ pub struct OptionPointSnapping {
|
||||
pub normals: Option<bool>,
|
||||
pub tangents: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
pub enum GridType {
|
||||
Rectangle { spacing: DVec2 },
|
||||
Isometric { y_axis_spacing: f64, angle_a: f64, angle_b: f64 },
|
||||
}
|
||||
|
||||
impl Default for GridType {
|
||||
fn default() -> Self {
|
||||
Self::RECTANGLE
|
||||
}
|
||||
}
|
||||
|
||||
impl GridType {
|
||||
pub const RECTANGLE: Self = GridType::Rectangle { spacing: DVec2::ONE };
|
||||
pub const ISOMETRIC: Self = GridType::Isometric {
|
||||
@@ -196,13 +217,30 @@ impl GridType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct GridSnapping {
|
||||
pub origin: DVec2,
|
||||
pub grid_type: GridType,
|
||||
pub grid_color: Color,
|
||||
pub dot_display: bool,
|
||||
}
|
||||
|
||||
impl Default for GridSnapping {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: DVec2::ZERO,
|
||||
grid_type: Default::default(),
|
||||
grid_color: COLOR_OVERLAY_GRAY
|
||||
.strip_prefix("#")
|
||||
.and_then(|value| Color::from_rgb_str(value))
|
||||
.expect("Should create Color from prefixed hex string"),
|
||||
dot_display: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GridSnapping {
|
||||
// Double grid size until it takes up at least 10px.
|
||||
pub fn compute_rectangle_spacing(mut size: DVec2, navigation: &PTZ) -> Option<DVec2> {
|
||||
@@ -240,11 +278,13 @@ pub enum BoundingBoxSnapSource {
|
||||
Corner,
|
||||
EdgeMidpoint,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BoardSnapSource {
|
||||
Center,
|
||||
Corner,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GeometrySnapSource {
|
||||
AnchorWithColinearHandles,
|
||||
@@ -253,6 +293,7 @@ pub enum GeometrySnapSource {
|
||||
LineMidpoint,
|
||||
Intersection,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SnapSource {
|
||||
#[default]
|
||||
@@ -261,6 +302,7 @@ pub enum SnapSource {
|
||||
Board(BoardSnapSource),
|
||||
Geometry(GeometrySnapSource),
|
||||
}
|
||||
|
||||
impl SnapSource {
|
||||
pub fn is_some(&self) -> bool {
|
||||
self != &Self::None
|
||||
@@ -269,6 +311,7 @@ impl SnapSource {
|
||||
matches!(self, Self::BoundingBox(_) | Self::Board(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum BoundingBoxSnapTarget {
|
||||
Center,
|
||||
@@ -319,12 +362,14 @@ pub enum BoardSnapTarget {
|
||||
Corner,
|
||||
Center,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GridSnapTarget {
|
||||
Line,
|
||||
LineNormal,
|
||||
Intersection,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SnapTarget {
|
||||
#[default]
|
||||
@@ -334,6 +379,7 @@ pub enum SnapTarget {
|
||||
Board(BoardSnapTarget),
|
||||
Grid(GridSnapTarget),
|
||||
}
|
||||
|
||||
impl SnapTarget {
|
||||
pub fn is_some(&self) -> bool {
|
||||
self != &Self::None
|
||||
@@ -342,6 +388,7 @@ impl SnapTarget {
|
||||
matches!(self, Self::BoundingBox(_) | Self::Board(_))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement icons for SnappingOptions eventually
|
||||
pub enum SnappingOptions {
|
||||
BoundingBoxes,
|
||||
@@ -358,9 +405,11 @@ impl fmt::Display for SnappingOptions {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct PTZ {
|
||||
pub pan: DVec2,
|
||||
pub tilt: f64,
|
||||
// TODO: Make this private and add getter/setter methods which ensure zoom is always positive and greater than the smallest zoom level in `VIEWPORT_ZOOM_LEVELS`.
|
||||
pub zoom: f64,
|
||||
}
|
||||
|
||||
|
||||
@@ -637,7 +637,6 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
TaggedValue::Bool(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
TaggedValue::String(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
TaggedValue::F32(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
TaggedValue::F64(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
TaggedValue::VectorData(render_object) => Self::debug_render(render_object, transform, responses),
|
||||
|
||||
Reference in New Issue
Block a user