effectcraft/crates/ui-egui/src/state.rs

697 lines
25 KiB
Rust

//! Frontend view state (not project data). Serde so the control channel can read and set it.
use std::collections::{BTreeMap, BTreeSet};
use serde::{Deserialize, Serialize};
use crate::dock::{DockNode, PanelKind};
use crate::icons::Icon;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Tool {
#[default]
Selection,
Hand,
Zoom,
Orbit,
PanCamera,
Dolly,
Rotate,
PanBehind,
Rectangle,
RoundedRect,
Ellipse,
Polygon,
Star,
Pen,
PenAdd,
PenDelete,
PenConvert,
MaskFeather,
Type,
TypeVertical,
Brush,
Clone,
Eraser,
RotoBrush,
RefineEdge,
Puppet,
PuppetStarch,
PuppetBend,
PuppetAdvanced,
PuppetOverlap,
}
impl Tool {
/// Toolbar slots: each slot shows its current tool; the slot cycles with its shortcut.
pub const SLOTS: &'static [&'static [Tool]] = &[
&[Tool::Selection],
&[Tool::Hand],
&[Tool::Zoom],
&[Tool::Orbit],
&[Tool::PanCamera],
&[Tool::Dolly],
&[Tool::Rotate],
&[Tool::PanBehind],
&[Tool::Rectangle, Tool::RoundedRect, Tool::Ellipse, Tool::Polygon, Tool::Star],
&[Tool::Pen, Tool::PenAdd, Tool::PenDelete, Tool::PenConvert, Tool::MaskFeather],
&[Tool::Type, Tool::TypeVertical],
&[Tool::Brush],
&[Tool::Clone],
&[Tool::Eraser],
&[Tool::RotoBrush, Tool::RefineEdge],
&[Tool::Puppet, Tool::PuppetStarch, Tool::PuppetBend, Tool::PuppetAdvanced, Tool::PuppetOverlap],
];
pub const ALL: [Tool; 30] = [
Tool::Selection,
Tool::Hand,
Tool::Zoom,
Tool::Orbit,
Tool::PanCamera,
Tool::Dolly,
Tool::Rotate,
Tool::PanBehind,
Tool::Rectangle,
Tool::RoundedRect,
Tool::Ellipse,
Tool::Polygon,
Tool::Star,
Tool::Pen,
Tool::PenAdd,
Tool::PenDelete,
Tool::PenConvert,
Tool::MaskFeather,
Tool::Type,
Tool::TypeVertical,
Tool::Brush,
Tool::Clone,
Tool::Eraser,
Tool::RotoBrush,
Tool::RefineEdge,
Tool::Puppet,
Tool::PuppetStarch,
Tool::PuppetBend,
Tool::PuppetAdvanced,
Tool::PuppetOverlap,
];
pub fn label(self) -> &'static str {
match self {
Tool::Selection => "Selection Tool",
Tool::Hand => "Hand Tool",
Tool::Zoom => "Zoom Tool",
Tool::Orbit => "Orbit Around Cursor Tool",
Tool::PanCamera => "Pan Under Cursor Tool",
Tool::Dolly => "Dolly Towards Cursor Tool",
Tool::Rotate => "Rotation Tool",
Tool::PanBehind => "Pan Behind (Anchor Point) Tool",
Tool::Rectangle => "Rectangle Tool",
Tool::RoundedRect => "Rounded Rectangle Tool",
Tool::Ellipse => "Ellipse Tool",
Tool::Polygon => "Polygon Tool",
Tool::Star => "Star Tool",
Tool::Pen => "Pen Tool",
Tool::PenAdd => "Add Vertex Tool",
Tool::PenDelete => "Delete Vertex Tool",
Tool::PenConvert => "Convert Vertex Tool",
Tool::MaskFeather => "Mask Feather Tool",
Tool::Type => "Horizontal Type Tool",
Tool::TypeVertical => "Vertical Type Tool",
Tool::Brush => "Brush Tool",
Tool::Clone => "Clone Stamp Tool",
Tool::Eraser => "Eraser Tool",
Tool::RotoBrush => "Roto Brush Tool",
Tool::RefineEdge => "Refine Edge Tool",
Tool::Puppet => "Puppet Position Pin Tool",
Tool::PuppetStarch => "Puppet Starch Pin Tool",
Tool::PuppetBend => "Puppet Bend Pin Tool",
Tool::PuppetAdvanced => "Puppet Advanced Pin Tool",
Tool::PuppetOverlap => "Puppet Overlap Pin Tool",
}
}
pub fn shortcut(self) -> Option<&'static str> {
match self {
Tool::Selection => Some("V"),
Tool::Hand => Some("H"),
Tool::Zoom => Some("Z"),
Tool::Orbit => Some("1"),
Tool::PanCamera => Some("2"),
Tool::Dolly => Some("3"),
Tool::Rotate => Some("W"),
Tool::PanBehind => Some("Y"),
Tool::Rectangle | Tool::RoundedRect | Tool::Ellipse | Tool::Polygon | Tool::Star => Some("Q"),
Tool::Pen | Tool::PenAdd | Tool::PenDelete | Tool::PenConvert | Tool::MaskFeather => Some("G"),
Tool::Type | Tool::TypeVertical => Some("Cmd+T"),
Tool::Brush | Tool::Clone | Tool::Eraser => Some("Cmd+B"),
Tool::RotoBrush | Tool::RefineEdge => Some("Alt+W"),
Tool::Puppet | Tool::PuppetStarch | Tool::PuppetBend | Tool::PuppetAdvanced | Tool::PuppetOverlap => Some("Cmd+P"),
}
}
pub fn icon(self) -> Icon {
match self {
Tool::Selection => Icon::Selection,
Tool::Hand => Icon::Hand,
Tool::Zoom => Icon::Zoom,
Tool::Orbit => Icon::Orbit,
Tool::PanCamera => Icon::PanCamera,
Tool::Dolly => Icon::Dolly,
Tool::Rotate => Icon::Rotate,
Tool::PanBehind => Icon::PanBehind,
Tool::Rectangle => Icon::Rectangle,
Tool::RoundedRect => Icon::RoundedRect,
Tool::Ellipse => Icon::Ellipse,
Tool::Polygon => Icon::Polygon,
Tool::Star => Icon::Star,
Tool::Pen => Icon::Pen,
Tool::PenAdd => Icon::PenAdd,
Tool::PenDelete => Icon::PenDelete,
Tool::PenConvert => Icon::PenConvert,
Tool::MaskFeather => Icon::MaskFeather,
Tool::Type => Icon::Type,
Tool::TypeVertical => Icon::TypeVertical,
Tool::Brush => Icon::Brush,
Tool::Clone => Icon::Clone,
Tool::Eraser => Icon::Eraser,
Tool::RotoBrush => Icon::RotoBrush,
Tool::RefineEdge => Icon::RefineEdge,
Tool::Puppet => Icon::Puppet,
Tool::PuppetStarch => Icon::PuppetStarch,
Tool::PuppetBend => Icon::PuppetBend,
Tool::PuppetAdvanced => Icon::PuppetAdvanced,
Tool::PuppetOverlap => Icon::PuppetOverlap,
}
}
pub fn from_name(s: &str) -> Option<Tool> {
let n = s.to_ascii_lowercase().replace([' ', '_', '-'], "");
Tool::ALL.into_iter().find(|t| format!("{t:?}").to_ascii_lowercase() == n || t.label().to_ascii_lowercase().replace(' ', "").starts_with(&n))
}
/// Puppet pin tools, with the pin kind they place (`puppet.addPin` kind).
pub fn puppet_kind(self) -> Option<&'static str> {
Some(match self {
Tool::Puppet => "position",
Tool::PuppetStarch => "starch",
Tool::PuppetBend => "bend",
Tool::PuppetAdvanced => "advanced",
Tool::PuppetOverlap => "overlap",
_ => return None,
})
}
/// Roto Brush / Refine Edge: the stroke kind they paint (`roto.stroke` kind), without and
/// with Alt/Option.
pub fn roto_kind(self, alt: bool) -> Option<&'static str> {
Some(match (self, alt) {
(Tool::RotoBrush, false) => "fg",
(Tool::RotoBrush, true) => "bg",
(Tool::RefineEdge, false) => "refine",
(Tool::RefineEdge, true) => "refineErase",
_ => return None,
})
}
/// Paint tools, with their stroke kind (`paint.stroke` kind).
pub fn paint_kind(self) -> Option<&'static str> {
Some(match self {
Tool::Brush => "brush",
Tool::Clone => "clone",
Tool::Eraser => "eraser",
_ => return None,
})
}
/// The Pen slot's tools (Pen, Add / Delete / Convert Vertex, Mask Feather).
pub fn is_pen(self) -> bool {
matches!(self, Tool::Pen | Tool::PenAdd | Tool::PenDelete | Tool::PenConvert | Tool::MaskFeather)
}
pub fn is_shape(self) -> bool {
matches!(self, Tool::Rectangle | Tool::RoundedRect | Tool::Ellipse | Tool::Polygon | Tool::Star)
}
}
/// Viewer resolution (Auto follows the magnification).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum Resolution {
#[default]
Auto,
Full,
Half,
Third,
Quarter,
/// View ▸ Resolution ▸ Custom: render every n-th pixel (2..=40).
Custom(u8),
}
impl Resolution {
pub const ALL: [Resolution; 5] = [Resolution::Auto, Resolution::Full, Resolution::Half, Resolution::Third, Resolution::Quarter];
pub fn label(self) -> &'static str {
match self {
Resolution::Auto => "Auto",
Resolution::Full => "Full",
Resolution::Half => "Half",
Resolution::Third => "Third",
Resolution::Quarter => "Quarter",
Resolution::Custom(_) => "Custom",
}
}
/// Render scale for a viewer magnification (and display pixel density).
pub fn scale(self, zoom: f32, ppp: f32) -> f64 {
match self {
Resolution::Auto => {
let z = (zoom * ppp) as f64;
if z >= 0.75 {
1.0
} else if z >= 0.45 {
0.5
} else if z >= 0.3 {
1.0 / 3.0
} else {
0.25
}
}
Resolution::Full => 1.0,
Resolution::Half => 0.5,
Resolution::Third => 1.0 / 3.0,
Resolution::Quarter => 0.25,
Resolution::Custom(n) => 1.0 / n.max(1) as f64,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct ViewerState {
/// Magnification (1 = 100%); None = fit.
pub zoom: Option<f32>,
/// Pan offset in points from the centred position.
pub pan: [f32; 2],
pub res: Resolution,
pub transparency_grid: bool,
pub show_masks: bool,
pub safe_margins: bool,
pub grid: bool,
pub rulers: bool,
pub channel: String,
pub show_layer_controls: bool,
pub fast_preview: bool,
/// View ▸ Show Guides / Snap to Guides / Lock Guides / Snap to Grid.
pub guides: bool,
pub snap_guides: bool,
pub lock_guides: bool,
pub snap_grid: bool,
/// Grid and guide options ▸ Proportional Grid.
pub proportional_grid: bool,
/// Rulers zero point (comp pixels), moved by dragging the ruler corner.
pub ruler_origin: [f64; 2],
/// Region of Interest button armed: the next drag in the viewer draws the region.
pub roi_draw: bool,
/// The pointer is dragging in the viewer this frame (Adaptive Resolution).
#[serde(skip)]
pub interacting: bool,
/// Extended Viewer: the comp-space region the viewer renders this frame (comp frame plus
/// the visible pasteboard), set while a 3D view shows past the frame.
#[serde(skip)]
pub extended: Option<[f64; 4]>,
/// View ▸ Panel Background Color (`None` = theme default).
pub pasteboard: Option<[u8; 3]>,
pub custom_pasteboard: [u8; 3],
}
impl Default for ViewerState {
fn default() -> Self {
ViewerState {
zoom: None,
pan: [0.0, 0.0],
res: Resolution::Auto,
transparency_grid: false,
show_masks: true,
safe_margins: false,
grid: false,
rulers: false,
channel: "RGB".into(),
show_layer_controls: true,
fast_preview: false,
guides: true,
snap_guides: false,
lock_guides: false,
snap_grid: false,
proportional_grid: false,
ruler_origin: [0.0, 0.0],
roi_draw: false,
interacting: false,
extended: None,
pasteboard: None,
custom_pasteboard: [0x80, 0x80, 0x80],
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TimelineState {
/// Visible span start (seconds) and pixels per second; None = fit comp.
pub start: f64,
pub pps: Option<f64>,
pub scroll_y: f32,
/// Left column area width.
pub columns_w: f32,
/// Horizontal scroll of the outline columns (pixels) when they are wider than the pane.
#[serde(default)]
pub outline_scroll: f32,
pub show_modes: bool,
pub graph_editor: bool,
pub search: String,
/// Twirled-open layers and groups (by layer id / group uid).
pub open_layers: BTreeSet<u64>,
pub open_groups: BTreeSet<u64>,
/// "Reveal" filter: only show these property match ids (P/S/R/T/A…) — empty = normal.
pub reveal: Vec<String>,
/// Properties / groups (uids) shown by the `props` reveal (Animation ▸ Reveal Properties…).
#[serde(default)]
pub reveal_props: BTreeSet<u64>,
/// Graph Editor: `auto` (Auto-Select Graph Type, the default: the speed graph when every
/// property shown is spatial, else the value graph), `value` or `speed`.
#[serde(default = "auto_graph")]
pub graph_mode: String,
/// Show only the selected properties (else every animated property of the selected layers).
#[serde(default = "yes")]
pub graph_show_selected: bool,
/// Auto-zoom the graph height to the visible curves.
#[serde(default = "yes")]
pub graph_auto_zoom: bool,
/// Manual graph value range (when auto-zoom is off).
#[serde(default)]
pub graph_range: Option<(f64, f64)>,
/// Graph Editor ▸ Snap: key drags snap to the current time, other keys and frames.
#[serde(default = "yes")]
pub graph_snap: bool,
/// Graph Editor ▸ Show Reference Graph (the other graph type, faint, behind).
#[serde(default)]
pub graph_reference: bool,
/// Graph Editor ▸ Show Transform Box When Multiple Keys Are Selected.
#[serde(default = "yes")]
pub graph_transform_box: bool,
/// Properties whose inline expression editor is collapsed.
#[serde(default)]
pub expr_closed: BTreeSet<u64>,
/// Visible optional columns (column header right-click ▸ Columns): `av`, `keys`, `label`,
/// `num`, `comment`, `switches`, `parent`, `in`, `out`, `duration`, `stretch`. The name
/// column is always shown; Modes follows `show_modes` (F4).
#[serde(default = "default_tl_columns")]
pub columns: BTreeSet<String>,
/// The name column shows Source Name instead of Layer Name (click its header).
#[serde(default)]
pub source_name: bool,
}
pub fn default_tl_columns() -> BTreeSet<String> {
["av", "label", "num", "switches", "parent"].map(String::from).into_iter().collect()
}
fn auto_graph() -> String {
"auto".into()
}
fn yes() -> bool {
true
}
impl Default for TimelineState {
fn default() -> Self {
TimelineState {
start: 0.0,
pps: None,
scroll_y: 0.0,
columns_w: 560.0,
outline_scroll: 0.0,
show_modes: false,
graph_editor: false,
search: String::new(),
open_layers: BTreeSet::new(),
open_groups: BTreeSet::new(),
reveal: vec![],
reveal_props: BTreeSet::new(),
graph_mode: auto_graph(),
graph_show_selected: true,
graph_auto_zoom: true,
graph_range: None,
graph_snap: true,
graph_reference: false,
graph_transform_box: true,
expr_closed: BTreeSet::new(),
columns: default_tl_columns(),
source_name: false,
}
}
}
fn default_project_sort() -> String {
"name".into()
}
pub fn default_project_columns() -> Vec<String> {
["type", "size", "duration", "fps"].map(String::from).to_vec()
}
/// What an Effect Controls crosshair / eyedropper click in the viewer sets.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct FxPick {
/// `point` (point parameter, layer space) or `color` (colour sampled from the frame).
pub kind: String,
pub layer: u64,
pub prop: u64,
/// Parameter name (for the viewer hint).
pub name: String,
}
/// Effects & Presets contents-menu options.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct EffectsView {
/// `all`, `32` (32 bpc effects only) or `gpu` (GPU-accelerated only).
pub depth: String,
/// Show the "* Animation Presets" folder.
pub presets: bool,
/// Flat alphabetical list instead of categories.
pub alphabetical: bool,
}
impl Default for EffectsView {
fn default() -> Self {
EffectsView { depth: "all".into(), presets: true, alphabetical: false }
}
}
/// Wiggler, Smoother and Motion Sketch panel settings.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct AnimToolsState {
/// Wiggler Apply To: Spatial Path (true) or Temporal Path.
pub wiggle_spatial: bool,
/// Noise Type: Smooth (true) or Jagged.
pub wiggle_smooth: bool,
/// `one` (dimension `wiggle_dim`), `same` or `independent`.
pub wiggle_dims: String,
pub wiggle_dim: usize,
pub wiggle_frequency: f64,
pub wiggle_magnitude: f64,
pub smooth_tolerance: f64,
/// Motion Sketch: capture speed (%), smoothing, show wireframe / background.
pub sketch_speed: f64,
pub sketch_smoothing: f64,
pub sketch_wireframe: bool,
pub sketch_background: bool,
/// Start Capture pressed: the next drag in the viewer records.
pub sketch_armed: bool,
}
impl Default for AnimToolsState {
fn default() -> Self {
AnimToolsState {
wiggle_spatial: true,
wiggle_smooth: true,
wiggle_dims: "independent".into(),
wiggle_dim: 0,
wiggle_frequency: 5.0,
wiggle_magnitude: 1.0,
smooth_tolerance: 1.0,
sketch_speed: 100.0,
sketch_smoothing: 1.0,
sketch_wireframe: false,
sketch_background: true,
sketch_armed: false,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UiState {
pub tool: Tool,
/// Window ▸ Script Console input, output and history.
#[serde(default)]
pub script_console: crate::panels::script_console::ScriptConsole,
/// Tool shown in each toolbar slot.
pub slot_tools: Vec<Tool>,
pub workspace: String,
pub dock: DockNode,
/// Saved workspace layouts (Save Changes to this Workspace / Save as New Workspace).
#[serde(default)]
pub saved_workspaces: std::collections::BTreeMap<String, DockNode>,
/// Undocked (floating) panel groups.
#[serde(default)]
pub floating: Vec<crate::dock::Floating>,
/// Floating panels of the saved workspaces.
#[serde(default)]
pub saved_floating: std::collections::BTreeMap<String, Vec<crate::dock::Floating>>,
/// The panel maximized to fill the dock area (`~`), if any.
#[serde(default)]
pub maximized: Option<PanelKind>,
/// Locked Composition / Timeline tabs (panel ids): opening another composition does not
/// bring a locked panel forward (the tab's lock icon, as After Effects' viewer lock).
#[serde(default)]
pub locked_tabs: BTreeSet<String>,
pub focused: PanelKind,
pub viewer: ViewerState,
pub timeline: TimelineState,
pub theme: crate::theme::ThemeKind,
pub show_menu_bar: bool,
pub status: String,
/// Effects & Presets search text.
pub effects_search: String,
pub effects_open: BTreeSet<String>,
pub project_search: String,
pub project_open_folders: BTreeSet<u64>,
/// Project panel sort column (`name`, `type`, `size`, `fps`) and direction, as in AE's
/// clickable column headers. Folders sort with everything else.
#[serde(default = "default_project_sort")]
pub project_sort: String,
#[serde(default)]
pub project_sort_desc: bool,
/// Visible Project panel columns after Name and Label, in order (`type`, `size`, `fps`,
/// `duration`, `path`, `comment`); toggled from the column header's context menu.
#[serde(default = "default_project_columns")]
pub project_columns: Vec<String>,
/// Horizontal scroll of the Project panel's optional columns (Name and Label stay put).
#[serde(default)]
pub project_hscroll: f32,
/// Vertical scroll of the Project panel's item list (points); only visible rows are drawn.
#[serde(default)]
pub project_scroll: f32,
/// Effect Controls twirl state (group uids that are collapsed).
pub fx_closed: BTreeSet<u64>,
/// Slider params whose slider row is twirled open (AE hides sliders by default).
#[serde(default)]
pub fx_slider_open: BTreeSet<u64>,
/// Effect Controls: a point crosshair or colour eyedropper waiting for a viewer click.
#[serde(default)]
pub fx_pick: Option<FxPick>,
/// Curves editor: shown channel (index into RGB/Red/Green/Blue/Alpha) per effect uid.
#[serde(default)]
pub fx_curve_channel: BTreeMap<u64, usize>,
/// Curves editors in pencil (freehand) mode, by effect uid.
#[serde(default)]
pub fx_curve_pencil: BTreeSet<u64>,
/// Levels (Individual Controls) editor: shown channel per effect uid.
#[serde(default)]
pub fx_levels_channel: BTreeMap<u64, usize>,
/// Effects & Presets: favourite effect ids (starred).
#[serde(default)]
pub effects_favorites: BTreeSet<String>,
/// Effects & Presets: recently applied effect ids, most recent first.
#[serde(default)]
pub effects_recent: Vec<String>,
/// Effects & Presets contents-menu view options.
#[serde(default)]
pub effects_view: EffectsView,
/// Shape tool options.
pub fill_color: [f32; 3],
pub stroke_color: [f32; 3],
pub stroke_width: f32,
pub snapping: bool,
/// Tool creates shape (true) or mask (false) when a layer is selected.
pub tool_creates_shape: bool,
pub start_screen: bool,
/// The Home screen shows its Learn tab (tutorials) instead of the recent projects.
#[serde(default)]
pub home_learn: bool,
/// The Home screen shows its Templates tab (New from Template gallery).
#[serde(default)]
pub home_templates: bool,
/// Align panel: Align Layers to Selection (off: to the composition).
#[serde(default)]
pub align_to_selection: bool,
/// Composition ▸ Preview ▸ Cache Frames When Idle.
pub cache_when_idle: bool,
/// Tracker panel ▸ Motion Source chosen without a tracker yet (layer id).
#[serde(default)]
pub tracker_source: Option<u64>,
/// Layer shown in the Layer panel (None = the first selected layer).
#[serde(default)]
pub layer_panel: Option<u64>,
/// Layer panel View: number of effects rendered (None = through the last Paint effect).
#[serde(default)]
pub layer_view: Option<usize>,
/// Wiggler / Smoother / Motion Sketch settings.
#[serde(default)]
pub anim_tools: AnimToolsState,
/// Composition Mini-Flowchart popup position (open when set).
#[serde(default)]
pub mini_flowchart: Option<[f32; 2]>,
/// Flowchart panel options (layers, effects, solids, direction, root comp).
#[serde(default)]
pub flowchart: crate::panels::flowchart::FlowOptions,
/// Lumetri Scopes panel options.
#[serde(default)]
pub scopes: crate::panels::scopes_panel::ScopesState,
}
impl Default for UiState {
fn default() -> Self {
UiState {
tool: Tool::Selection,
script_console: Default::default(),
slot_tools: Tool::SLOTS.iter().map(|s| s[0]).collect(),
workspace: "Default".into(),
dock: crate::dock::workspace("Default"),
saved_workspaces: Default::default(),
floating: vec![],
saved_floating: Default::default(),
maximized: None,
locked_tabs: BTreeSet::new(),
focused: PanelKind::Composition,
viewer: ViewerState::default(),
timeline: TimelineState::default(),
theme: Default::default(),
show_menu_bar: true,
status: String::new(),
effects_search: String::new(),
effects_open: BTreeSet::new(),
project_search: String::new(),
project_open_folders: BTreeSet::new(),
project_sort: default_project_sort(),
project_sort_desc: false,
project_hscroll: 0.0,
project_scroll: 0.0,
project_columns: default_project_columns(),
fx_closed: BTreeSet::new(),
fx_slider_open: BTreeSet::new(),
fx_pick: None,
fx_curve_channel: BTreeMap::new(),
fx_curve_pencil: BTreeSet::new(),
fx_levels_channel: BTreeMap::new(),
effects_favorites: BTreeSet::new(),
effects_recent: Vec::new(),
effects_view: EffectsView::default(),
fill_color: [0.24, 0.55, 0.96],
stroke_color: [1.0, 1.0, 1.0],
stroke_width: 0.0,
snapping: true,
tool_creates_shape: true,
start_screen: false,
home_learn: false,
home_templates: false,
align_to_selection: false,
cache_when_idle: false,
tracker_source: None,
layer_panel: None,
layer_view: None,
anim_tools: AnimToolsState::default(),
mini_flowchart: None,
flowchart: Default::default(),
scopes: Default::default(),
}
}
}