1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use crate::{RenderResult, SceneVM, SceneVMResult};
/// Minimal app abstraction to write one SceneVM app for native + wasm.
pub trait SceneVMApp {
/// Optional preferred initial window size on native (physical pixels). If `None`, a platform default is used.
fn initial_window_size(&self) -> Option<(u32, u32)> {
None
}
/// Optional window title on native. If `None`, `"SceneVM"` is used.
fn window_title(&self) -> Option<String> {
None
}
/// Optional target frame rate on native (FPS). If `None`, the runner will poll/redraw as fast as possible.
fn target_fps(&self) -> Option<f32> {
None
}
/// Called by the runner before init() to inform the app of its runtime mode
/// `is_native`: true = native wgpu runner (needs file buttons)
/// false = platform wrapper (Xcode, uses document system)
fn set_native_mode(&mut self, _is_native: bool) {
// Override this to store the mode and conditionally show UI elements
}
/// Get the current system theme preference (true = dark, false = light)
/// Called before init() to determine initial theme
#[cfg(feature = "ui")]
fn is_dark_mode(&self) -> bool {
true // Default to dark mode
}
/// Called once after the renderer is created and sized.
fn init(&mut self, _vm: &mut SceneVM, _size: (u32, u32)) {}
/// Per-frame update hook (e.g. animation).
fn update(&mut self, _vm: &mut SceneVM) {}
/// Render hook: call `ctx.present(vm)` to display.
fn render(&mut self, vm: &mut SceneVM, ctx: &mut dyn SceneVMRenderCtx);
/// Collect app events to be handled by the host (file operations, etc.)
/// The host calls this after render to get events like RequestSave, RequestOpen, etc.
#[cfg(feature = "ui")]
fn take_app_events(&mut self) -> Vec<crate::app_event::AppEvent> {
Vec::new()
}
/// Return `true` if the app wants an update/render this tick. Default is always true.
fn needs_update(&mut self, _vm: &SceneVM) -> bool {
true
}
/// Resize callback with new logical size.
fn resize(&mut self, _vm: &mut SceneVM, _size: (u32, u32)) {}
/// Set the scale factor (device pixel ratio) for HiDPI displays.
fn set_scale(&mut self, _scale: f32) {}
/// Mouse/touch down callback in logical pixels.
fn mouse_down(&mut self, _vm: &mut SceneVM, _x: f32, _y: f32) {}
/// Mouse/touch up callback in logical pixels.
fn mouse_up(&mut self, _vm: &mut SceneVM, _x: f32, _y: f32) {}
/// Mouse/touch move callback in logical pixels.
fn mouse_move(&mut self, _vm: &mut SceneVM, _x: f32, _y: f32) {}
/// Scroll/pan delta (e.g. trackpad or wheel) in logical units.
fn scroll(&mut self, _vm: &mut SceneVM, _dx: f32, _dy: f32) {}
/// Keyboard key pressed callback.
fn key_down(&mut self, _vm: &mut SceneVM, _key: &str) {}
/// Keyboard key released callback.
fn key_up(&mut self, _vm: &mut SceneVM, _key: &str) {}
/// Pinch gesture: scale factor and center in logical coordinates.
fn pinch(&mut self, _vm: &mut SceneVM, _scale: f32, _center: (f32, f32)) {}
// Project Management (optional, only available with 'project' feature)
// File paths are handled by the wrapper layer (Swift/native runners)
// These methods only deal with JSON serialization/deserialization
/// Serialize current app state to JSON string
/// Called during File->Save or auto-save operations
/// Returns JSON string representing the current state
#[cfg(feature = "ui")]
fn save_to_json(&mut self, _vm: &mut SceneVM) -> Option<String> {
None
}
/// Deserialize and apply JSON state to app
/// Called during File->Open or app launch with document
/// Return true if load was successful
#[cfg(feature = "ui")]
fn load_from_json(&mut self, _vm: &mut SceneVM, _json: &str) -> bool {
false
}
/// Create a new/empty project state
/// Called when user selects File->New
/// Should reset app to default state
#[cfg(feature = "ui")]
fn new_project(&mut self, _vm: &mut SceneVM) {
// Default: do nothing
}
/// Check if project has unsaved changes
/// Used to prompt user before closing or creating new project
#[cfg(feature = "ui")]
fn has_unsaved_changes(&self) -> bool {
false
}
/// Generate a thumbnail for the current project
/// Returns RGBA pixel data (width, height, pixels)
/// Thumbnail should be small (e.g., 256x256) for efficient storage
#[cfg(feature = "ui")]
fn generate_thumbnail(&mut self, _vm: &mut SceneVM) -> Option<(u32, u32, Vec<u8>)> {
None
}
/// Export data in the specified format
/// Called when user requests export (e.g., PNG, JPEG)
/// Returns exported data as bytes, or None if format not supported
#[cfg(feature = "ui")]
fn export_data(&mut self, _vm: &mut SceneVM, _format: &str) -> Option<Vec<u8>> {
None
}
/// Switch between light and dark themes
/// Called when the system appearance changes (e.g., iOS dark mode toggle)
/// or when user manually switches theme
/// `is_dark` is true for dark mode, false for light mode
#[cfg(feature = "ui")]
fn set_theme(&mut self, _vm: &mut SceneVM, _is_dark: bool, _size: (u32, u32)) {
// Default: do nothing
// Apps should override this to rebuild their UI with the new theme
}
// Undo/Redo Support (optional, only available with 'ui' feature)
/// Perform undo operation
/// Called when user triggers undo (Cmd+Z on macOS, Ctrl+Z on Windows/Linux)
/// Returns true if undo was performed, false if nothing to undo
#[cfg(feature = "ui")]
fn undo(&mut self, _vm: &mut SceneVM) -> bool {
false
}
/// Perform redo operation
/// Called when user triggers redo (Cmd+Shift+Z on macOS, Ctrl+Y on Windows/Linux)
/// Returns true if redo was performed, false if nothing to redo
#[cfg(feature = "ui")]
fn redo(&mut self, _vm: &mut SceneVM) -> bool {
false
}
/// Check if undo is available
/// Used by wrappers to enable/disable undo menu items
#[cfg(feature = "ui")]
fn can_undo(&self) -> bool {
false
}
/// Check if redo is available
/// Used by wrappers to enable/disable redo menu items
#[cfg(feature = "ui")]
fn can_redo(&self) -> bool {
false
}
/// Get description of next undo action (e.g., "Undo Change Slider")
/// Used for menu item display
#[cfg(feature = "ui")]
fn undo_description(&self) -> Option<String> {
None
}
/// Get description of next redo action (e.g., "Redo Change Slider")
/// Used for menu item display
#[cfg(feature = "ui")]
fn redo_description(&self) -> Option<String> {
None
}
}
/// Rendering context supplied to `SceneVMApp::render`.
pub trait SceneVMRenderCtx {
fn size(&self) -> (u32, u32);
fn present(&mut self, vm: &mut SceneVM) -> SceneVMResult<RenderResult>;
}