fyroxed_base/menu/
create.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::{
22    command::{Command, CommandGroup},
23    fyrox::{
24        core::{algebra::Vector3, pool::Handle},
25        engine::{Engine, SerializationContext},
26        fxhash::FxHashMap,
27        gui::{
28            menu::MenuItemMessage, message::MessageDirection, message::UiMessage,
29            widget::WidgetMessage, BuildContext, UiNode, UserInterface,
30        },
31        scene::node::Node,
32    },
33    menu::{create_menu_item, create_root_menu_item, ui::UiMenu},
34    message::MessageSender,
35    scene::{
36        commands::graph::{AddNodeCommand, MoveNodeCommand},
37        controller::SceneController,
38        GameScene, Selection,
39    },
40    ui_scene::UiScene,
41    Mode,
42};
43use fyrox::core::log::Log;
44use fyrox::graph::constructor::{VariantConstructor, VariantResult};
45use fyrox::gui::constructor::WidgetConstructorContainer;
46use fyrox::gui::menu::SortingPredicate;
47use fyrox::scene::graph::Graph;
48
49pub struct CreateEntityRootMenu {
50    pub menu: Handle<UiNode>,
51    pub sub_menus: CreateEntityMenu,
52}
53
54impl CreateEntityRootMenu {
55    pub fn new(
56        serialization_context: &SerializationContext,
57        widget_constructors_container: &WidgetConstructorContainer,
58        ctx: &mut BuildContext,
59    ) -> Self {
60        let sub_menus =
61            CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
62
63        let menu = create_root_menu_item("Create", sub_menus.root_items.clone(), ctx);
64
65        ctx.inner().send_message(MenuItemMessage::sort(
66            menu,
67            MessageDirection::ToWidget,
68            SortingPredicate::sort_by_text(),
69        ));
70
71        Self { menu, sub_menus }
72    }
73
74    pub fn handle_ui_message(
75        &mut self,
76        message: &UiMessage,
77        sender: &MessageSender,
78        controller: &mut dyn SceneController,
79        selection: &Selection,
80        engine: &mut Engine,
81    ) {
82        if let Some(node) = self
83            .sub_menus
84            .handle_ui_message(message, sender, controller, selection, engine)
85        {
86            if let Some(game_scene) = controller.downcast_ref::<GameScene>() {
87                let scene = &engine.scenes[game_scene.scene];
88
89                let position = game_scene
90                    .camera_controller
91                    .placement_position(&scene.graph, Default::default());
92
93                let node_handle = scene.graph.generate_free_handles(1)[0];
94                sender.do_command(CommandGroup::from(vec![
95                    Command::new(AddNodeCommand::new(node, Handle::NONE, true)),
96                    Command::new(MoveNodeCommand::new(
97                        node_handle,
98                        Vector3::default(),
99                        position,
100                    )),
101                ]));
102            }
103        }
104    }
105
106    pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) {
107        self.sub_menus.on_scene_changed(controller, ui);
108    }
109
110    pub fn on_mode_changed(&mut self, ui: &UserInterface, mode: &Mode) {
111        ui.send_message(WidgetMessage::enabled(
112            self.menu,
113            MessageDirection::ToWidget,
114            mode.is_edit(),
115        ));
116    }
117}
118
119pub struct CreateEntityMenu {
120    ui_menu: UiMenu,
121    pub root_items: Vec<Handle<UiNode>>,
122    constructor_views: FxHashMap<Handle<UiNode>, VariantConstructor<Node, Graph>>,
123}
124
125impl CreateEntityMenu {
126    pub fn new(
127        serialization_context: &SerializationContext,
128        widget_constructor_container: &WidgetConstructorContainer,
129        ctx: &mut BuildContext,
130    ) -> Self {
131        let ui_menu = UiMenu::new(widget_constructor_container, "UI", ctx);
132
133        let mut root_items = vec![ui_menu.menu];
134        let mut groups = FxHashMap::default();
135        let mut constructor_views = FxHashMap::default();
136        let constructors = serialization_context.node_constructors.map();
137        for constructor in constructors.values() {
138            for variant in constructor.variants.iter() {
139                let item = create_menu_item(&variant.name, vec![], ctx);
140                constructor_views.insert(item, variant.constructor.clone());
141                if constructor.group.is_empty() {
142                    root_items.push(item);
143                } else {
144                    let group = *groups.entry(constructor.group).or_insert_with(|| {
145                        let group = create_menu_item(constructor.group, vec![], ctx);
146                        root_items.push(group);
147                        group
148                    });
149                    ctx.send_message(MenuItemMessage::add_item(
150                        group,
151                        MessageDirection::ToWidget,
152                        item,
153                    ))
154                }
155            }
156        }
157
158        for root_item in root_items.iter() {
159            ctx.inner().send_message(MenuItemMessage::sort(
160                *root_item,
161                MessageDirection::ToWidget,
162                SortingPredicate::sort_by_text(),
163            ))
164        }
165
166        Self {
167            ui_menu,
168            constructor_views,
169            root_items,
170        }
171    }
172
173    pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) {
174        let is_ui_scene = controller.downcast_ref::<UiScene>().is_some();
175
176        ui.send_message(WidgetMessage::enabled(
177            self.ui_menu.menu,
178            MessageDirection::ToWidget,
179            is_ui_scene,
180        ));
181
182        for widget in self.root_items.iter() {
183            if *widget == self.ui_menu.menu {
184                continue;
185            }
186
187            ui.send_message(WidgetMessage::enabled(
188                *widget,
189                MessageDirection::ToWidget,
190                !is_ui_scene,
191            ));
192        }
193    }
194
195    pub fn handle_ui_message(
196        &mut self,
197        message: &UiMessage,
198        sender: &MessageSender,
199        controller: &mut dyn SceneController,
200        selection: &Selection,
201        engine: &mut Engine,
202    ) -> Option<Node> {
203        if let Some(ui_scene) = controller.downcast_mut::<UiScene>() {
204            self.ui_menu
205                .handle_ui_message(sender, message, ui_scene, selection);
206        } else if let Some(game_scene) = controller.downcast_mut::<GameScene>() {
207            let graph = &mut engine.scenes[game_scene.scene].graph;
208            if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
209                if let Some(constructor) = self.constructor_views.get(&message.destination()) {
210                    if let VariantResult::Owned(node) = constructor(graph) {
211                        return Some(node);
212                    } else {
213                        Log::err("Unsupported");
214                    }
215                }
216            }
217        }
218
219        None
220    }
221}