Skip to main content

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}