fyroxed_base/plugin.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{fyrox::gui::message::UiMessage, Editor, Message};
22use fyrox::core::define_as_any_trait;
23use fyrox::engine::ApplicationLoopController;
24
25define_as_any_trait!(EditorPluginAsAny => EditorPlugin);
26
27/// Editor plugin allows you to extend editor functionality with custom tools. It provides a standard way of interaction
28/// between your plugin and built-in editor's functionality.
29///
30/// ## Development Patterns
31///
32/// There are multiple development patterns that **should** (and strongly advised) be used. Following them will help you to
33/// write your tools _the right way_.
34///
35/// ### MVC
36///
37/// The editor uses classic [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (model-view-controller)
38/// pattern. This means that the editor always "renders" the actual state of your data model and its UI is used only to show
39/// the data - it does not store anything. Any user change forces the editor to sync the UI with the new data.
40///
41/// ### Commands
42///
43/// The editor usually operates on scenes (there could be multiple opened scenes, but only one active) and any modification of
44/// their content **must** be done via _commands_. [Command](https://en.wikipedia.org/wiki/Command_pattern) is a standard
45/// pattern that encapsulates an action. Command pattern is used for undo/redo functionality.
46pub trait EditorPlugin: EditorPluginAsAny {
47 /// This method is called right after the editor was fully initialized. It is guaranteed to be called only once.
48 fn on_start(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
49
50 /// This method is called when the editor is about to close. It is guaranteed to be called only once.
51 fn on_exit(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
52
53 /// This method is called either when there was some action via command, or a syncing request is performed. It should
54 /// be used to synchronize the state of your widgets with the actual data model.
55 fn on_sync_to_model(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
56
57 /// This method is called when the editor switches to another mode. For example, if a user clicks the "Play" button,
58 /// the mode will be changed from [`crate::Mode::Edit`] to [`crate::Mode::Build`], and if the build was successful,
59 /// it will then be changed to [`crate::Mode::Play`]. When the game was closed, the mode will be changed back to
60 /// [`crate::Mode::Edit`].
61 fn on_mode_changed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
62
63 /// This method is called when active scene was changed. It could happen if a user opens or loads
64 /// a new scene, closes existing scene so the active scene changes to previous in the list of
65 /// scenes (if any).
66 fn on_scene_changed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
67
68 /// This method is called when a UI message was extracted from the message queue. It should be used to react to user
69 /// changes, for example a user could click a button, then a [`fyrox::gui::button::ButtonMessage::Click`] will be
70 /// passed to this method. It then can be used to perform some other action.
71 fn on_ui_message(
72 &mut self,
73 #[allow(unused_variables)] message: &mut UiMessage,
74 #[allow(unused_variables)] editor: &mut Editor,
75 ) {
76 }
77
78 /// This method is called when the editor suspends its execution. It could happen in a few reasons, but the most
79 /// common ones are:
80 ///
81 /// 1) When the main editor's window is unfocused.
82 /// 2) When there's no messages coming from the OS to the main editor's window.
83 ///
84 /// All of these reason means, that a user does nothing with the editor and the editor just "sleeps" in this period of
85 /// time, saving precious CPU/GPU resources and keeping power consumption at lowest possible values. Which also means
86 /// that cooling fans won't spin like crazy.
87 fn on_suspended(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
88
89 /// This method is called when the editor continues its execution. See [`Self::on_suspended`] method for more info
90 /// about suspension.
91 fn on_resumed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
92
93 /// This method is called when the editor leaves preview mode. Usually this method is used to
94 /// rollback scene changes to the state in which scene objects were before entering the preview
95 /// mode. This method is typically called by the editor before execution of any command and before
96 /// saving (to prevent "leakage" of preview mode changes into the saved scene).
97 fn on_leave_preview_mode(&mut self, #[allow(unused_variables)] editor: &mut Editor) {}
98
99 /// This method is used to tell the editor, whether your plugin is in preview mode or not. Preview mode is a special
100 /// state of the editor, when it modifies a content of some scene every frame and discards these changes when the
101 /// preview mode is disabled.
102 fn is_in_preview_mode(&self, #[allow(unused_variables)] editor: &Editor) -> bool {
103 false
104 }
105
106 /// This method is called every frame at stable update rate of 60 FPS. It could be used to perform any contiguous
107 /// actions.
108 fn on_update(
109 &mut self,
110 #[allow(unused_variables)] editor: &mut Editor,
111 #[allow(unused_variables)] loop_controller: ApplicationLoopController,
112 ) {
113 }
114
115 /// This method is called at the end of all update routines of both the engine and the editor. It could be used to
116 /// perform some actions, that require all pre-defined steps to be done.
117 fn on_post_update(
118 &mut self,
119 #[allow(unused_variables)] editor: &mut Editor,
120 #[allow(unused_variables)] loop_controller: ApplicationLoopController,
121 ) {
122 }
123
124 /// This method is called when the editor receives a control message. It could be used to catch and react to specific
125 /// actions in the editor (such as: scene loading, command execution, undo, redo, etc.).
126 fn on_message(
127 &mut self,
128 #[allow(unused_variables)] message: &Message,
129 #[allow(unused_variables)] editor: &mut Editor,
130 ) {
131 }
132}
133
134#[macro_export]
135macro_rules! for_each_plugin {
136 ($container:expr => $func:ident($($param:expr),*)) => {{
137 let mut i = 0;
138 while i < $container.0.len() {
139 if let Some(mut plugin) = $container.0.get_mut(i).and_then(|p| p.take()) {
140 plugin.$func($($param),*);
141
142 if let Some(entry) = $container.0.get_mut(i) {
143 *entry = Some(plugin);
144 }
145 }
146
147 i += 1;
148 }
149 }};
150}
151
152#[derive(Default)]
153pub struct EditorPluginsContainer(pub Vec<Option<Box<dyn EditorPlugin>>>);
154
155impl EditorPluginsContainer {
156 pub fn new() -> Self {
157 Default::default()
158 }
159
160 pub fn with<T: EditorPlugin>(mut self, plugin: T) -> Self {
161 self.0.push(Some(Box::new(plugin)));
162 self
163 }
164
165 pub fn add<T: EditorPlugin>(&mut self, plugin: T) -> &mut Self {
166 self.0.push(Some(Box::new(plugin)));
167 self
168 }
169
170 pub fn try_get<T>(&self) -> Option<&T>
171 where
172 T: EditorPlugin,
173 {
174 self.0.iter().find_map(|container| {
175 container
176 .as_ref()
177 .and_then(|plugin| (**plugin).as_any().downcast_ref::<T>())
178 })
179 }
180
181 pub fn get<T>(&self) -> &T
182 where
183 T: EditorPlugin,
184 {
185 self.try_get()
186 .unwrap_or_else(|| panic!("There's no plugin with {} name", std::any::type_name::<T>()))
187 }
188
189 pub fn try_get_mut<T>(&mut self) -> Option<&mut T>
190 where
191 T: EditorPlugin,
192 {
193 self.0.iter_mut().find_map(|container| {
194 container
195 .as_mut()
196 .and_then(|plugin| (**plugin).as_any_mut().downcast_mut::<T>())
197 })
198 }
199
200 pub fn get_mut<T>(&mut self) -> &mut T
201 where
202 T: EditorPlugin,
203 {
204 self.try_get_mut()
205 .unwrap_or_else(|| panic!("There's no plugin with {} name", std::any::type_name::<T>()))
206 }
207}