mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-11 22:50:08 +00:00
entity creator window
- replaces chained context menu with a dedicated window - old functionality preserved, new will be added in following commits
This commit is contained in:
+41
-24
@@ -57,7 +57,7 @@ pub mod world;
|
||||
|
||||
pub use fyrox;
|
||||
|
||||
use crate::world::WorldViewerItemContextMenu;
|
||||
use crate::world::create::EntityCreator;
|
||||
use crate::{
|
||||
asset::{item::AssetItem, AssetBrowser},
|
||||
audio::{preview::AudioPreviewPanel, AudioPanel},
|
||||
@@ -658,6 +658,7 @@ pub struct Editor {
|
||||
pub engine: Engine,
|
||||
pub plugins: EditorPluginsContainer,
|
||||
pub focused: bool,
|
||||
pub entity_creator: EntityCreator,
|
||||
pub update_loop_state: UpdateLoopState,
|
||||
pub is_suspended: bool,
|
||||
pub scene_node_context_menu: Rc<RefCell<SceneNodeContextMenu>>,
|
||||
@@ -836,15 +837,8 @@ impl Editor {
|
||||
)
|
||||
.build(ctx);
|
||||
let navmesh_panel = NavmeshPanel::new(scene_viewer.frame(), ctx, message_sender.clone());
|
||||
let scene_node_context_menu = Rc::new(RefCell::new(SceneNodeContextMenu::new(
|
||||
&engine.serialization_context,
|
||||
&engine.widget_constructors,
|
||||
ctx,
|
||||
)));
|
||||
let widget_context_menu = Rc::new(RefCell::new(WidgetContextMenu::new(
|
||||
&engine.widget_constructors,
|
||||
ctx,
|
||||
)));
|
||||
let scene_node_context_menu = Rc::new(RefCell::new(SceneNodeContextMenu::new(ctx)));
|
||||
let widget_context_menu = Rc::new(RefCell::new(WidgetContextMenu::new(ctx)));
|
||||
let world_outliner = WorldViewer::new(ctx, message_sender.clone(), &settings);
|
||||
let command_stack_viewer = CommandStackViewer::new(ctx, message_sender.clone());
|
||||
let log = LogPanel::new(
|
||||
@@ -1017,6 +1011,7 @@ impl Editor {
|
||||
)
|
||||
.with_buttons(MessageBoxButtons::Ok)
|
||||
.build(ctx);
|
||||
let entity_creator = EntityCreator::new(ctx);
|
||||
|
||||
let save_scene_dialog = SaveSceneConfirmationDialog::new(ctx);
|
||||
|
||||
@@ -1086,6 +1081,7 @@ impl Editor {
|
||||
// was created. So we must assume that the editor is focused by default, otherwise editor's thread
|
||||
// will sleep forever and the window won't come up.
|
||||
focused: true,
|
||||
entity_creator,
|
||||
update_loop_state: UpdateLoopState::default(),
|
||||
is_suspended: false,
|
||||
scene_node_context_menu,
|
||||
@@ -1482,6 +1478,13 @@ impl Editor {
|
||||
);
|
||||
|
||||
if let Some(game_scene) = current_scene_entry.controller.downcast_mut::<GameScene>() {
|
||||
self.entity_creator.handle_ui_message_with_game_scene(
|
||||
game_scene,
|
||||
engine,
|
||||
message,
|
||||
¤t_scene_entry.selection,
|
||||
&self.message_sender,
|
||||
);
|
||||
self.particle_system_control_panel.handle_ui_message(
|
||||
message,
|
||||
¤t_scene_entry.selection,
|
||||
@@ -1560,6 +1563,14 @@ impl Editor {
|
||||
self.light_panel
|
||||
.handle_ui_message(message, game_scene, engine);
|
||||
} else if let Some(ui_scene) = current_scene_entry.controller.downcast_mut::<UiScene>() {
|
||||
self.entity_creator.handle_ui_message_with_ui_scene(
|
||||
ui_scene,
|
||||
engine,
|
||||
message,
|
||||
¤t_scene_entry.selection,
|
||||
&self.message_sender,
|
||||
);
|
||||
|
||||
self.bbcode_panel.handle_ui_message(
|
||||
message,
|
||||
¤t_scene_entry.selection,
|
||||
@@ -2232,7 +2243,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
fn on_scene_changed(&mut self, old_selection: Selection) {
|
||||
let ui = &self.engine.user_interfaces.first();
|
||||
self.refresh_entity_creator();
|
||||
|
||||
let ui = self.engine.user_interfaces.first_mut();
|
||||
let entry = self.scenes.current_scene_entry_ref();
|
||||
if let Some(game_scene) = entry.controller.downcast_ref::<GameScene>() {
|
||||
self.world_viewer.item_context_menu = Some(self.scene_node_context_menu.clone());
|
||||
@@ -2247,7 +2260,6 @@ impl Editor {
|
||||
|
||||
self.message_sender
|
||||
.send(Message::SelectionChanged { old_selection });
|
||||
self.menu.on_scene_changed(&*entry.controller, ui);
|
||||
|
||||
self.world_viewer.clear(ui);
|
||||
|
||||
@@ -2685,6 +2697,10 @@ impl Editor {
|
||||
)
|
||||
}
|
||||
}
|
||||
Message::OpenEntityCreator(mode) => {
|
||||
self.entity_creator
|
||||
.open(mode, self.engine.user_interfaces.first());
|
||||
}
|
||||
Message::SetAssetBrowserCurrentDir(path) => {
|
||||
self.asset_browser
|
||||
.request_current_path(path, self.engine.user_interfaces.first());
|
||||
@@ -2841,21 +2857,22 @@ impl Editor {
|
||||
);
|
||||
}
|
||||
|
||||
fn on_game_plugin_added(&mut self) {
|
||||
let mut widget_context_menu = self.widget_context_menu.borrow_mut();
|
||||
let mut scene_node_context_menu = self.scene_node_context_menu.borrow_mut();
|
||||
for menu in [
|
||||
&mut *widget_context_menu as &mut dyn WorldViewerItemContextMenu,
|
||||
&mut *scene_node_context_menu as &mut dyn WorldViewerItemContextMenu,
|
||||
] {
|
||||
menu.on_plugin_added(
|
||||
&self.engine.serialization_context,
|
||||
&self.engine.widget_constructors,
|
||||
self.engine.user_interfaces.first_mut(),
|
||||
);
|
||||
fn refresh_entity_creator(&mut self) {
|
||||
let ui = self.engine.user_interfaces.first_mut();
|
||||
let entry = self.scenes.current_scene_entry_ref();
|
||||
if entry.controller.downcast_ref::<GameScene>().is_some() {
|
||||
self.entity_creator
|
||||
.on_constructors_changed(ui, &self.engine.serialization_context.node_constructors);
|
||||
} else if entry.controller.downcast_ref::<UiScene>().is_some() {
|
||||
self.entity_creator
|
||||
.on_constructors_changed(ui, &self.engine.widget_constructors);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_game_plugin_added(&mut self) {
|
||||
self.refresh_entity_creator();
|
||||
}
|
||||
|
||||
pub fn add_game_plugin<P>(&mut self, plugin: P)
|
||||
where
|
||||
P: Plugin + 'static,
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::{
|
||||
command::{Command, CommandGroup},
|
||||
fyrox::{
|
||||
core::{algebra::Vector3, pool::Handle},
|
||||
engine::{Engine, SerializationContext},
|
||||
fxhash::FxHashMap,
|
||||
gui::{
|
||||
menu::MenuItemMessage, message::UiMessage, widget::WidgetMessage, BuildContext,
|
||||
UserInterface,
|
||||
},
|
||||
scene::node::Node,
|
||||
},
|
||||
menu::{create_menu_item, create_root_menu_item, ui::UiMenu},
|
||||
message::MessageSender,
|
||||
scene::{
|
||||
commands::graph::{AddNodeCommand, MoveNodeCommand},
|
||||
controller::SceneController,
|
||||
GameScene, Selection,
|
||||
},
|
||||
ui_scene::UiScene,
|
||||
Mode,
|
||||
};
|
||||
use fyrox::core::log::Log;
|
||||
use fyrox::core::uuid::{uuid, Uuid};
|
||||
use fyrox::graph::constructor::{VariantConstructor, VariantResult};
|
||||
use fyrox::gui::constructor::WidgetConstructorContainer;
|
||||
use fyrox::gui::menu::{MenuItem, SortingPredicate};
|
||||
use fyrox::scene::graph::Graph;
|
||||
|
||||
pub struct CreateEntityRootMenu {
|
||||
pub menu: Handle<MenuItem>,
|
||||
pub sub_menus: CreateEntityMenu,
|
||||
}
|
||||
|
||||
impl CreateEntityRootMenu {
|
||||
pub const CREATE: Uuid = uuid!("edc4d319-2e8e-4173-8b35-73ff4982f380");
|
||||
|
||||
pub fn new(
|
||||
serialization_context: &SerializationContext,
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Self {
|
||||
let sub_menus =
|
||||
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
|
||||
|
||||
let menu = create_root_menu_item("Create", Self::CREATE, sub_menus.root_items.clone(), ctx);
|
||||
|
||||
ctx.inner().send(
|
||||
menu,
|
||||
MenuItemMessage::Sort(SortingPredicate::sort_by_text()),
|
||||
);
|
||||
|
||||
Self { menu, sub_menus }
|
||||
}
|
||||
|
||||
pub fn handle_ui_message(
|
||||
&mut self,
|
||||
message: &UiMessage,
|
||||
sender: &MessageSender,
|
||||
controller: &mut dyn SceneController,
|
||||
selection: &Selection,
|
||||
engine: &mut Engine,
|
||||
) {
|
||||
if let Some(node) = self
|
||||
.sub_menus
|
||||
.handle_ui_message(message, sender, controller, selection, engine)
|
||||
{
|
||||
if let Some(game_scene) = controller.downcast_ref::<GameScene>() {
|
||||
let scene = &engine.scenes[game_scene.scene];
|
||||
|
||||
let position = game_scene
|
||||
.camera_controller
|
||||
.placement_position(&scene.graph, Default::default());
|
||||
|
||||
let node_handle = scene.graph.generate_free_handles(1)[0];
|
||||
sender.do_command(CommandGroup::from(vec![
|
||||
Command::new(AddNodeCommand::new(node, Handle::NONE, true)),
|
||||
Command::new(MoveNodeCommand::new(
|
||||
node_handle,
|
||||
Vector3::default(),
|
||||
position,
|
||||
)),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) {
|
||||
self.sub_menus.on_scene_changed(controller, ui);
|
||||
}
|
||||
|
||||
pub fn on_mode_changed(&mut self, ui: &UserInterface, mode: &Mode) {
|
||||
ui.send(self.menu, WidgetMessage::Enabled(mode.is_edit()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreateEntityMenu {
|
||||
ui_menu: UiMenu,
|
||||
pub root_items: Vec<Handle<MenuItem>>,
|
||||
constructor_views: FxHashMap<Handle<MenuItem>, VariantConstructor<Node, Graph>>,
|
||||
}
|
||||
|
||||
impl CreateEntityMenu {
|
||||
pub fn new(
|
||||
serialization_context: &SerializationContext,
|
||||
widget_constructor_container: &WidgetConstructorContainer,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Self {
|
||||
let ui_menu = UiMenu::new(widget_constructor_container, "UI", ctx);
|
||||
|
||||
let mut root_items = vec![ui_menu.menu];
|
||||
let mut groups = FxHashMap::default();
|
||||
let mut constructor_views = FxHashMap::default();
|
||||
let constructors = serialization_context.node_constructors.map();
|
||||
for constructor in constructors.values() {
|
||||
for variant in constructor.variants.iter() {
|
||||
let item = create_menu_item(&variant.name, Uuid::new_v4(), vec![], ctx);
|
||||
constructor_views.insert(item, variant.constructor.clone());
|
||||
if constructor.group.is_empty() {
|
||||
root_items.push(item);
|
||||
} else {
|
||||
let group = *groups.entry(constructor.group).or_insert_with(|| {
|
||||
let group =
|
||||
create_menu_item(constructor.group, Uuid::new_v4(), vec![], ctx);
|
||||
root_items.push(group);
|
||||
group
|
||||
});
|
||||
ctx.inner().send(group, MenuItemMessage::AddItem(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for root_item in root_items.iter() {
|
||||
ctx.inner().send(
|
||||
*root_item,
|
||||
MenuItemMessage::Sort(SortingPredicate::sort_by_text()),
|
||||
)
|
||||
}
|
||||
|
||||
Self {
|
||||
ui_menu,
|
||||
constructor_views,
|
||||
root_items,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) {
|
||||
let is_ui_scene = controller.downcast_ref::<UiScene>().is_some();
|
||||
|
||||
ui.send(self.ui_menu.menu, WidgetMessage::Enabled(is_ui_scene));
|
||||
|
||||
for widget in self.root_items.iter() {
|
||||
if *widget == self.ui_menu.menu {
|
||||
continue;
|
||||
}
|
||||
ui.send(*widget, WidgetMessage::Enabled(!is_ui_scene));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_ui_message(
|
||||
&mut self,
|
||||
message: &UiMessage,
|
||||
sender: &MessageSender,
|
||||
controller: &mut dyn SceneController,
|
||||
selection: &Selection,
|
||||
engine: &mut Engine,
|
||||
) -> Option<Node> {
|
||||
if let Some(ui_scene) = controller.downcast_mut::<UiScene>() {
|
||||
self.ui_menu
|
||||
.handle_ui_message(sender, message, ui_scene, selection);
|
||||
} else if let Some(game_scene) = controller.downcast_mut::<GameScene>() {
|
||||
let graph = &mut engine.scenes[game_scene.scene].graph;
|
||||
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
|
||||
if let Some(constructor) = self
|
||||
.constructor_views
|
||||
.get(&message.destination().to_variant())
|
||||
{
|
||||
if let VariantResult::Owned(node) = constructor(graph) {
|
||||
return Some(node);
|
||||
} else {
|
||||
Log::err("Unsupported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
+9
-37
@@ -22,45 +22,38 @@ use crate::{
|
||||
asset::preview::cache::IconRequest,
|
||||
export::ExportWindow,
|
||||
fyrox::{
|
||||
asset::manager::ResourceManager,
|
||||
core::uuid::Uuid,
|
||||
core::{algebra::Vector2, pool::Handle},
|
||||
gui::{
|
||||
menu::{MenuBuilder, MenuItemBuilder, MenuItemContent},
|
||||
file_browser::FileType,
|
||||
image::{Image, ImageBuilder},
|
||||
menu::{MenuBuilder, MenuItem, MenuItemBuilder, MenuItemContent},
|
||||
message::UiMessage,
|
||||
texture::TextureResource,
|
||||
widget::{WidgetBuilder, WidgetMessage},
|
||||
window::Window,
|
||||
BuildContext, Thickness, UserInterface,
|
||||
},
|
||||
},
|
||||
menu::{
|
||||
create::CreateEntityRootMenu, edit::EditMenu, file::FileMenu, help::HelpMenu,
|
||||
utils::UtilsMenu, view::ViewMenu,
|
||||
},
|
||||
menu::{edit::EditMenu, file::FileMenu, help::HelpMenu, utils::UtilsMenu, view::ViewMenu},
|
||||
message::MessageSender,
|
||||
scene::{container::EditorSceneEntry, controller::SceneController},
|
||||
scene::container::EditorSceneEntry,
|
||||
settings::Settings,
|
||||
stats::StatisticsWindow,
|
||||
Engine, Mode, SceneSettingsWindow,
|
||||
};
|
||||
use fyrox::asset::manager::ResourceManager;
|
||||
use fyrox::core::uuid::Uuid;
|
||||
use fyrox::gui::file_browser::FileType;
|
||||
use fyrox::gui::image::{Image, ImageBuilder};
|
||||
use fyrox::gui::menu::MenuItem;
|
||||
use fyrox::gui::texture::TextureResource;
|
||||
use fyrox::gui::window::Window;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub mod create;
|
||||
pub mod edit;
|
||||
pub mod file;
|
||||
pub mod help;
|
||||
pub mod ui;
|
||||
pub mod utils;
|
||||
pub mod view;
|
||||
|
||||
pub struct Menu {
|
||||
pub menu: Handle<fyrox::gui::menu::Menu>,
|
||||
pub create_entity_menu: CreateEntityRootMenu,
|
||||
pub edit_menu: EditMenu,
|
||||
pub file_menu: FileMenu,
|
||||
pub view_menu: ViewMenu,
|
||||
@@ -160,11 +153,6 @@ impl Menu {
|
||||
pub fn new(engine: &mut Engine, message_sender: MessageSender, settings: &Settings) -> Self {
|
||||
let file_menu = FileMenu::new(engine, settings);
|
||||
let ctx = &mut engine.user_interfaces.first_mut().build_ctx();
|
||||
let create_entity_menu = CreateEntityRootMenu::new(
|
||||
&engine.serialization_context,
|
||||
&engine.widget_constructors,
|
||||
ctx,
|
||||
);
|
||||
let edit_menu = EditMenu::new(ctx);
|
||||
let view_menu = ViewMenu::new(ctx);
|
||||
let utils_menu = UtilsMenu::new(ctx);
|
||||
@@ -174,7 +162,6 @@ impl Menu {
|
||||
.with_items(vec![
|
||||
file_menu.menu,
|
||||
edit_menu.menu,
|
||||
create_entity_menu.menu,
|
||||
view_menu.menu,
|
||||
utils_menu.menu,
|
||||
help_menu.menu,
|
||||
@@ -183,7 +170,6 @@ impl Menu {
|
||||
|
||||
Self {
|
||||
menu,
|
||||
create_entity_menu,
|
||||
edit_menu,
|
||||
message_sender,
|
||||
file_menu,
|
||||
@@ -216,7 +202,6 @@ impl Menu {
|
||||
self.file_menu.close_scene,
|
||||
self.file_menu.save,
|
||||
self.file_menu.save_as,
|
||||
self.create_entity_menu.menu,
|
||||
self.edit_menu.menu,
|
||||
self.file_menu.open_scene_settings,
|
||||
]
|
||||
@@ -236,14 +221,6 @@ impl Menu {
|
||||
ctx.engine,
|
||||
);
|
||||
|
||||
self.create_entity_menu.handle_ui_message(
|
||||
message,
|
||||
&self.message_sender,
|
||||
&mut *entry.controller,
|
||||
&entry.selection,
|
||||
ctx.engine,
|
||||
);
|
||||
|
||||
self.utils_menu.handle_ui_message(
|
||||
message,
|
||||
&mut ctx.panels,
|
||||
@@ -266,12 +243,7 @@ impl Menu {
|
||||
self.help_menu.handle_ui_message(message);
|
||||
}
|
||||
|
||||
pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) {
|
||||
self.create_entity_menu.on_scene_changed(controller, ui);
|
||||
}
|
||||
|
||||
pub fn on_mode_changed(&mut self, ui: &UserInterface, mode: &Mode) {
|
||||
self.create_entity_menu.on_mode_changed(ui, mode);
|
||||
self.edit_menu.on_mode_changed(ui, mode);
|
||||
self.file_menu.on_mode_changed(ui, mode);
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::{
|
||||
fyrox::{
|
||||
core::{log::Log, pool::Handle},
|
||||
fxhash::FxHashMap,
|
||||
graph::constructor::{VariantConstructor, VariantResult},
|
||||
gui::{
|
||||
constructor::WidgetConstructorContainer, menu::MenuItemMessage, message::UiMessage,
|
||||
BuildContext, UiNode, UserInterface,
|
||||
},
|
||||
},
|
||||
menu::create_menu_item,
|
||||
message::MessageSender,
|
||||
scene::Selection,
|
||||
ui_scene::{commands::graph::AddWidgetCommand, UiScene},
|
||||
};
|
||||
use fyrox::core::uuid::{uuid, Uuid};
|
||||
use fyrox::gui::menu::{MenuItem, SortingPredicate};
|
||||
|
||||
pub struct UiMenu {
|
||||
pub menu: Handle<MenuItem>,
|
||||
constructor_views: FxHashMap<Handle<MenuItem>, VariantConstructor<UiNode, UserInterface>>,
|
||||
}
|
||||
|
||||
impl UiMenu {
|
||||
pub const MENU: Uuid = uuid!("3a3f7035-529e-4d79-a413-a4d887d5e32e");
|
||||
|
||||
pub fn new(
|
||||
constructors: &WidgetConstructorContainer,
|
||||
name: &str,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Self {
|
||||
let mut root_items = vec![];
|
||||
let mut groups = FxHashMap::default();
|
||||
let mut constructor_views = FxHashMap::default();
|
||||
let constructors = constructors.map();
|
||||
for constructor in constructors.values() {
|
||||
for variant in constructor.variants.iter() {
|
||||
let item = create_menu_item(&variant.name, Uuid::new_v4(), vec![], ctx);
|
||||
constructor_views.insert(item, variant.constructor.clone());
|
||||
if constructor.group.is_empty() {
|
||||
root_items.push(item);
|
||||
} else {
|
||||
let group = *groups.entry(constructor.group).or_insert_with(|| {
|
||||
let group =
|
||||
create_menu_item(constructor.group, Uuid::new_v4(), vec![], ctx);
|
||||
root_items.push(group);
|
||||
group
|
||||
});
|
||||
ctx.inner().send(group, MenuItemMessage::AddItem(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let menu = create_menu_item(name, Self::MENU, root_items.clone(), ctx);
|
||||
|
||||
for root_item in root_items.iter().chain(&[menu]) {
|
||||
ctx.inner().send(
|
||||
*root_item,
|
||||
MenuItemMessage::Sort(SortingPredicate::sort_by_text()),
|
||||
)
|
||||
}
|
||||
|
||||
Self {
|
||||
menu,
|
||||
constructor_views,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_ui_message(
|
||||
&mut self,
|
||||
sender: &MessageSender,
|
||||
message: &UiMessage,
|
||||
scene: &mut UiScene,
|
||||
selection: &Selection,
|
||||
) {
|
||||
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
|
||||
if let Some(constructor) = self
|
||||
.constructor_views
|
||||
.get_mut(&message.destination().to_variant())
|
||||
{
|
||||
if let VariantResult::Handle(ui_node_handle) = constructor(&mut scene.ui) {
|
||||
let sub_graph = scene.ui.take_reserve_sub_graph(ui_node_handle);
|
||||
let parent = if let Some(selection) = selection.as_ui() {
|
||||
selection.widgets.first().cloned().unwrap_or_default()
|
||||
} else {
|
||||
Handle::NONE
|
||||
};
|
||||
sender.do_command(AddWidgetCommand::new(sub_graph, parent, true));
|
||||
} else {
|
||||
Log::err("Unsupported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::world::create::EntityCreatorMode;
|
||||
use crate::{
|
||||
command::{Command, CommandGroup, CommandTrait},
|
||||
fyrox::{
|
||||
@@ -122,6 +123,7 @@ pub enum Message {
|
||||
ViewSurfaceData(SurfaceResource),
|
||||
SyncInteractionModes,
|
||||
SetAssetBrowserCurrentDir(PathBuf),
|
||||
OpenEntityCreator(EntityCreatorMode),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Reflect)]
|
||||
|
||||
+20
-38
@@ -18,6 +18,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::world::create::EntityCreatorMode;
|
||||
use crate::{
|
||||
fyrox::{
|
||||
asset::manager::ResourceManager,
|
||||
@@ -25,19 +26,17 @@ use crate::{
|
||||
pool::Handle,
|
||||
uuid::{uuid, Uuid},
|
||||
},
|
||||
engine::SerializationContext,
|
||||
graph::{NodeWrapper, SceneGraph},
|
||||
gui::{
|
||||
constructor::WidgetConstructorContainer,
|
||||
menu::{ContextMenuBuilder, MenuItem, MenuItemMessage},
|
||||
message::UiMessage,
|
||||
popup::{Placement, PopupBuilder, PopupMessage},
|
||||
stack_panel::{StackPanel, StackPanelBuilder},
|
||||
stack_panel::StackPanelBuilder,
|
||||
widget::{WidgetBuilder, WidgetMessage},
|
||||
BuildContext, RcUiNodeHandle, UiNode, UserInterface,
|
||||
BuildContext, RcUiNodeHandle, UiNode,
|
||||
},
|
||||
},
|
||||
menu::{create_menu_item, create_menu_item_shortcut, ui::UiMenu},
|
||||
menu::{create_menu_item, create_menu_item_shortcut},
|
||||
message::MessageSender,
|
||||
scene::{controller::SceneController, Selection},
|
||||
ui_scene::{
|
||||
@@ -54,36 +53,17 @@ pub struct WidgetContextMenu {
|
||||
menu: RcUiNodeHandle,
|
||||
delete_selection: Handle<MenuItem>,
|
||||
copy_selection: Handle<MenuItem>,
|
||||
widgets_menu: UiMenu,
|
||||
create_child: Handle<MenuItem>,
|
||||
placement_target: Handle<UiNode>,
|
||||
paste: Handle<MenuItem>,
|
||||
make_root: Handle<MenuItem>,
|
||||
open_asset: Handle<MenuItem>,
|
||||
stack_panel: Handle<StackPanel>,
|
||||
}
|
||||
|
||||
impl WorldViewerItemContextMenu for WidgetContextMenu {
|
||||
fn menu(&self) -> RcUiNodeHandle {
|
||||
self.menu.clone()
|
||||
}
|
||||
|
||||
fn on_plugin_added(
|
||||
&mut self,
|
||||
_serialization_context: &SerializationContext,
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ui: &mut UserInterface,
|
||||
) {
|
||||
ui.send(self.widgets_menu.menu, WidgetMessage::Remove);
|
||||
self.widgets_menu = UiMenu::new(
|
||||
widget_constructors_container,
|
||||
"Create Child Widget",
|
||||
&mut ui.build_ctx(),
|
||||
);
|
||||
ui.send(
|
||||
self.widgets_menu.menu,
|
||||
WidgetMessage::LinkWith(self.stack_panel.to_base()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn resource_path_of_first_selected_node(
|
||||
@@ -110,21 +90,17 @@ impl WidgetContextMenu {
|
||||
pub const DELETE_SELECTION: Uuid = uuid!("30eef2a7-9f12-4e64-9142-b604f25e9e06");
|
||||
pub const COPY_SELECTION: Uuid = uuid!("0a2d10bc-de1e-4196-aba6-0a51c54eb238");
|
||||
pub const PASTE_AS_CHILD: Uuid = uuid!("d3b86c8c-1efd-4917-9543-b0f5f32f8cbd");
|
||||
pub const CREATE_CHILD_WIDGET: Uuid = uuid!("26b2ed1f-c4b8-4ec4-8112-9f29fc600101");
|
||||
pub const MAKE_ROOT: Uuid = uuid!("968318f6-21c7-430f-a13d-36aefb61cde2");
|
||||
pub const OPEN_ASSET: Uuid = uuid!("f3f7d0fa-e905-4371-8973-dfc1eb758e5a");
|
||||
|
||||
pub fn new(
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Self {
|
||||
pub fn new(ctx: &mut BuildContext) -> Self {
|
||||
let delete_selection;
|
||||
let copy_selection;
|
||||
let paste;
|
||||
let make_root;
|
||||
let open_asset;
|
||||
|
||||
let widgets_menu = UiMenu::new(widget_constructors_container, "Create Child Widget", ctx);
|
||||
|
||||
let create_child;
|
||||
let stack_panel;
|
||||
let menu = ContextMenuBuilder::new(
|
||||
PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
|
||||
@@ -172,7 +148,15 @@ impl WidgetContextMenu {
|
||||
create_menu_item("Open Asset", Self::OPEN_ASSET, vec![], ctx);
|
||||
open_asset
|
||||
})
|
||||
.with_child(widgets_menu.menu),
|
||||
.with_child({
|
||||
create_child = create_menu_item(
|
||||
"Create Child Widget",
|
||||
Self::CREATE_CHILD_WIDGET,
|
||||
vec![],
|
||||
ctx,
|
||||
);
|
||||
create_child
|
||||
}),
|
||||
)
|
||||
.build(ctx);
|
||||
stack_panel
|
||||
@@ -183,7 +167,7 @@ impl WidgetContextMenu {
|
||||
let menu = RcUiNodeHandle::new(menu, ctx.sender());
|
||||
|
||||
Self {
|
||||
widgets_menu,
|
||||
create_child,
|
||||
menu,
|
||||
delete_selection,
|
||||
copy_selection,
|
||||
@@ -191,7 +175,6 @@ impl WidgetContextMenu {
|
||||
paste,
|
||||
make_root,
|
||||
open_asset,
|
||||
stack_panel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +187,6 @@ impl WidgetContextMenu {
|
||||
sender: &MessageSender,
|
||||
) {
|
||||
if let Some(ui_scene) = controller.downcast_mut::<UiScene>() {
|
||||
self.widgets_menu
|
||||
.handle_ui_message(sender, message, ui_scene, editor_selection);
|
||||
|
||||
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
|
||||
if message.destination() == self.delete_selection {
|
||||
if let Some(ui_selection) = editor_selection.as_ui() {
|
||||
@@ -247,6 +227,8 @@ impl WidgetContextMenu {
|
||||
sender.send(Message::LoadScene(path));
|
||||
}
|
||||
}
|
||||
} else if message.destination() == self.create_child {
|
||||
sender.send(Message::OpenEntityCreator(EntityCreatorMode::CreateChild));
|
||||
}
|
||||
} else if let Some(PopupMessage::Placement(Placement::Cursor(target))) = message.data()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::{
|
||||
command::{Command, CommandGroup},
|
||||
fyrox::{
|
||||
core::{algebra::Vector3, pool::Handle},
|
||||
engine::Engine,
|
||||
fxhash::FxHashMap,
|
||||
graph::constructor::{ConstructorVariantId, GraphNodeConstructorContainer, VariantResult},
|
||||
gui::{
|
||||
button::{Button, ButtonBuilder, ButtonMessage},
|
||||
grid::{Column, GridBuilder, Row},
|
||||
list_view::{ListView, ListViewBuilder},
|
||||
message::UiMessage,
|
||||
scroll_viewer::ScrollViewerBuilder,
|
||||
stack_panel::StackPanelBuilder,
|
||||
text::TextBuilder,
|
||||
tree::{Tree, TreeBuilder, TreeMessage, TreeRoot, TreeRootBuilder, TreeRootMessage},
|
||||
widget::WidgetBuilder,
|
||||
window::{Window, WindowAlignment, WindowBuilder, WindowMessage, WindowTitle},
|
||||
BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface,
|
||||
},
|
||||
scene::{graph::Graph, node::Node},
|
||||
},
|
||||
message::MessageSender,
|
||||
scene::commands::graph::{
|
||||
AddNodeCommand, LinkNodesCommand, MoveNodeCommand, ReplaceNodeCommand, SetGraphRootCommand,
|
||||
},
|
||||
scene::{GameScene, Selection},
|
||||
ui_scene::{commands::graph::AddWidgetCommand, UiScene},
|
||||
};
|
||||
use fyrox::gui::widget::WidgetMessage;
|
||||
|
||||
#[derive(Default, Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum EntityCreatorMode {
|
||||
#[default]
|
||||
CreateChild,
|
||||
CreateParent,
|
||||
CreateReplacement,
|
||||
}
|
||||
|
||||
pub struct EntityCreator {
|
||||
window: Handle<Window>,
|
||||
recent_list: Handle<ListView>,
|
||||
groups_tree: Handle<TreeRoot>,
|
||||
items_map: FxHashMap<Handle<Tree>, ConstructorVariantId>,
|
||||
create: Handle<Button>,
|
||||
cancel: Handle<Button>,
|
||||
selection: Option<ConstructorVariantId>,
|
||||
mode: EntityCreatorMode,
|
||||
}
|
||||
|
||||
fn make_tree_item(ui: &mut UserInterface, text: &str) -> Handle<Tree> {
|
||||
let ctx = &mut ui.build_ctx();
|
||||
TreeBuilder::new(WidgetBuilder::new())
|
||||
.with_content(
|
||||
TextBuilder::new(WidgetBuilder::new())
|
||||
.with_text(text)
|
||||
.build(ctx),
|
||||
)
|
||||
.build(ctx)
|
||||
}
|
||||
|
||||
impl EntityCreator {
|
||||
pub fn new(ctx: &mut BuildContext) -> Self {
|
||||
let recent_list = ListViewBuilder::new(WidgetBuilder::new().on_column(0)).build(ctx);
|
||||
|
||||
let groups_tree = TreeRootBuilder::new(WidgetBuilder::new()).build(ctx);
|
||||
|
||||
let groups_scroll_viewer = ScrollViewerBuilder::new(WidgetBuilder::new().on_column(1))
|
||||
.with_content(groups_tree)
|
||||
.build(ctx);
|
||||
|
||||
let create = ButtonBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
.with_enabled(false)
|
||||
.with_width(100.0)
|
||||
.with_margin(Thickness::uniform(2.0)),
|
||||
)
|
||||
.with_text("Create")
|
||||
.build(ctx);
|
||||
let cancel = ButtonBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
.with_width(100.0)
|
||||
.with_margin(Thickness::uniform(2.0)),
|
||||
)
|
||||
.with_text("Cancel")
|
||||
.build(ctx);
|
||||
|
||||
let buttons = StackPanelBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
.with_height(28.0)
|
||||
.with_horizontal_alignment(HorizontalAlignment::Right)
|
||||
.on_row(1)
|
||||
.with_child(create)
|
||||
.with_child(cancel),
|
||||
)
|
||||
.with_orientation(Orientation::Horizontal)
|
||||
.build(ctx);
|
||||
|
||||
let grid = GridBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
.with_margin(Thickness::uniform(2.0))
|
||||
.with_child(recent_list)
|
||||
.with_child(groups_scroll_viewer),
|
||||
)
|
||||
.add_column(Column::strict(200.0))
|
||||
.add_column(Column::stretch())
|
||||
.add_row(Row::stretch())
|
||||
.build(ctx);
|
||||
|
||||
let content = GridBuilder::new(WidgetBuilder::new().with_child(grid).with_child(buttons))
|
||||
.add_column(Column::stretch())
|
||||
.add_row(Row::stretch())
|
||||
.add_row(Row::auto())
|
||||
.build(ctx);
|
||||
|
||||
let window = WindowBuilder::new(WidgetBuilder::new().with_width(500.0).with_height(600.0))
|
||||
.with_content(content)
|
||||
.open(false)
|
||||
.with_title(WindowTitle::text("Entity Creator"))
|
||||
.build(ctx);
|
||||
|
||||
Self {
|
||||
window,
|
||||
recent_list,
|
||||
groups_tree,
|
||||
items_map: FxHashMap::default(),
|
||||
create,
|
||||
cancel,
|
||||
selection: None,
|
||||
mode: EntityCreatorMode::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&mut self, mode: EntityCreatorMode, ui: &UserInterface) {
|
||||
self.mode = mode;
|
||||
ui.send(
|
||||
self.window,
|
||||
WindowMessage::Open {
|
||||
alignment: WindowAlignment::Center,
|
||||
modal: true,
|
||||
focus_content: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn on_constructors_changed<N, C>(
|
||||
&mut self,
|
||||
ui: &mut UserInterface,
|
||||
constructors: &GraphNodeConstructorContainer<N, C>,
|
||||
) {
|
||||
self.selection = None;
|
||||
ui.send(self.create, WidgetMessage::Enabled(false));
|
||||
ui.send(self.groups_tree, TreeRootMessage::Items(Vec::new()));
|
||||
self.items_map.clear();
|
||||
let constructors = constructors.map();
|
||||
let mut groups = FxHashMap::default();
|
||||
for (type_uuid, constructor) in constructors.iter() {
|
||||
for (variant_index, variant) in constructor.variants.iter().enumerate() {
|
||||
let item = make_tree_item(ui, &variant.name);
|
||||
self.items_map
|
||||
.insert(item, ConstructorVariantId::new(type_uuid, variant_index));
|
||||
if constructor.group.is_empty() {
|
||||
ui.send(self.groups_tree, TreeRootMessage::AddItem(item));
|
||||
} else {
|
||||
let group = *groups.entry(constructor.group).or_insert_with(|| {
|
||||
let group = make_tree_item(ui, constructor.group);
|
||||
ui.send(self.groups_tree, TreeRootMessage::AddItem(group));
|
||||
group
|
||||
});
|
||||
ui.send(group, TreeMessage::AddItem(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ui_message_internal<N, Ctx>(
|
||||
&mut self,
|
||||
constructors: &GraphNodeConstructorContainer<N, Ctx>,
|
||||
message: &UiMessage,
|
||||
ui: &UserInterface,
|
||||
ctx: &mut Ctx,
|
||||
) -> Option<VariantResult<N>> {
|
||||
if let Some(TreeRootMessage::Select(selection)) = message.data_from(self.groups_tree) {
|
||||
if let Some(first) = selection.first() {
|
||||
let constructor_id = self.items_map.get(first);
|
||||
if let Some(constructor_id) = constructor_id {
|
||||
self.selection = Some(constructor_id.clone());
|
||||
}
|
||||
let can_create = constructor_id.is_some();
|
||||
ui.send(self.create, WidgetMessage::Enabled(can_create));
|
||||
}
|
||||
} else if let Some(ButtonMessage::Click) = message.data_from(self.create) {
|
||||
if let Some(constructor_id) = self.selection.as_ref() {
|
||||
ui.send(self.window, WindowMessage::Close);
|
||||
return constructors.try_create_variant(constructor_id, ctx);
|
||||
}
|
||||
} else if let Some(ButtonMessage::Click) = message.data_from(self.cancel) {
|
||||
ui.send(self.window, WindowMessage::Close);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn handle_ui_message_with_game_scene(
|
||||
&mut self,
|
||||
game_scene: &GameScene,
|
||||
engine: &mut Engine,
|
||||
message: &UiMessage,
|
||||
editor_selection: &Selection,
|
||||
sender: &MessageSender,
|
||||
) {
|
||||
let ui = engine.user_interfaces.first();
|
||||
let graph = &mut engine.scenes[game_scene.scene].graph;
|
||||
if let Some(VariantResult::Owned(node)) = self.handle_ui_message_internal::<Node, Graph>(
|
||||
&engine.serialization_context.node_constructors,
|
||||
message,
|
||||
ui,
|
||||
graph,
|
||||
) {
|
||||
match self.mode {
|
||||
EntityCreatorMode::CreateChild => {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(parent) = graph_selection.nodes().first() {
|
||||
sender.do_command(AddNodeCommand::new(node, *parent, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityCreatorMode::CreateParent => {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(first) = graph_selection.nodes().first() {
|
||||
let scene = &engine.scenes[game_scene.scene];
|
||||
|
||||
let position = game_scene
|
||||
.camera_controller
|
||||
.placement_position(&scene.graph, *first);
|
||||
|
||||
let first_ref = &scene.graph[*first];
|
||||
let parent = if first_ref.parent().is_some() {
|
||||
first_ref.parent()
|
||||
} else {
|
||||
game_scene.scene_content_root
|
||||
};
|
||||
|
||||
let new_parent_handle = scene.graph.generate_free_handles(1)[0];
|
||||
let mut commands = CommandGroup::from(vec![
|
||||
Command::new(AddNodeCommand::new(node, parent, true)),
|
||||
Command::new(LinkNodesCommand::new(*first, new_parent_handle)),
|
||||
]);
|
||||
|
||||
if parent == game_scene.scene_content_root {
|
||||
commands.push(MoveNodeCommand::new(
|
||||
new_parent_handle,
|
||||
Vector3::default(),
|
||||
position,
|
||||
));
|
||||
}
|
||||
|
||||
if *first == game_scene.scene_content_root {
|
||||
commands.push(SetGraphRootCommand {
|
||||
root: new_parent_handle,
|
||||
link_scheme: Default::default(),
|
||||
})
|
||||
}
|
||||
sender.do_command(commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityCreatorMode::CreateReplacement => {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(first) = graph_selection.nodes().first() {
|
||||
sender.do_command(ReplaceNodeCommand {
|
||||
handle: *first,
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_ui_message_with_ui_scene(
|
||||
&mut self,
|
||||
ui_scene: &mut UiScene,
|
||||
engine: &mut Engine,
|
||||
message: &UiMessage,
|
||||
editor_selection: &Selection,
|
||||
sender: &MessageSender,
|
||||
) {
|
||||
let ui = engine.user_interfaces.first();
|
||||
if let Some(VariantResult::Handle(ui_node_handle)) = self
|
||||
.handle_ui_message_internal::<UiNode, UserInterface>(
|
||||
&engine.widget_constructors,
|
||||
message,
|
||||
ui,
|
||||
&mut ui_scene.ui,
|
||||
)
|
||||
{
|
||||
let sub_graph = ui_scene.ui.take_reserve_sub_graph(ui_node_handle);
|
||||
let parent = if let Some(selection) = editor_selection.as_ui() {
|
||||
selection.widgets.first().cloned().unwrap_or_default()
|
||||
} else {
|
||||
Handle::NONE
|
||||
};
|
||||
sender.do_command(AddWidgetCommand::new(sub_graph, parent, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-129
@@ -18,20 +18,19 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::world::create::EntityCreatorMode;
|
||||
use crate::{
|
||||
command::{Command, CommandGroup},
|
||||
fyrox::{
|
||||
asset::untyped::UntypedResource,
|
||||
core::{
|
||||
algebra::{Vector2, Vector3},
|
||||
algebra::Vector2,
|
||||
pool::Handle,
|
||||
reflect::Reflect,
|
||||
uuid::{uuid, Uuid},
|
||||
},
|
||||
engine::SerializationContext,
|
||||
graph::SceneGraph,
|
||||
gui::{
|
||||
constructor::WidgetConstructorContainer,
|
||||
file_browser::{FileSelector, FileSelectorMessage, FileType},
|
||||
menu::{
|
||||
self, ContextMenuBuilder, MenuItem, MenuItemBuilder, MenuItemContent,
|
||||
@@ -42,18 +41,15 @@ use crate::{
|
||||
stack_panel::StackPanelBuilder,
|
||||
widget::{WidgetBuilder, WidgetMessage},
|
||||
window::{WindowAlignment, WindowMessage},
|
||||
BuildContext, RcUiNodeHandle, UiNode, UserInterface,
|
||||
BuildContext, RcUiNodeHandle, UiNode,
|
||||
},
|
||||
},
|
||||
make_save_file_selector,
|
||||
menu::{create::CreateEntityMenu, create_menu_item, create_menu_item_shortcut},
|
||||
menu::{create_menu_item, create_menu_item_shortcut},
|
||||
message::MessageSender,
|
||||
scene::{
|
||||
commands::{
|
||||
graph::{
|
||||
AddNodeCommand, LinkNodesCommand, MoveNodeCommand, ReplaceNodeCommand,
|
||||
SetGraphRootCommand, SetNodeTransformCommand,
|
||||
},
|
||||
graph::{SetGraphRootCommand, SetNodeTransformCommand},
|
||||
make_delete_selection_command, RevertSceneNodePropertyCommand,
|
||||
},
|
||||
controller::SceneController,
|
||||
@@ -71,11 +67,8 @@ pub struct SceneNodeContextMenu {
|
||||
delete_selection: Handle<MenuItem>,
|
||||
copy_selection: Handle<MenuItem>,
|
||||
create_child_menu: Handle<MenuItem>,
|
||||
create_child_entity_menu: CreateEntityMenu,
|
||||
create_parent_menu: Handle<MenuItem>,
|
||||
create_parent_entity_menu: CreateEntityMenu,
|
||||
replace_with_menu: Handle<MenuItem>,
|
||||
replace_with_entity_menu: CreateEntityMenu,
|
||||
placement_target: Handle<UiNode>,
|
||||
save_as_prefab: Handle<MenuItem>,
|
||||
save_as_prefab_dialog: Handle<FileSelector>,
|
||||
@@ -89,26 +82,6 @@ impl WorldViewerItemContextMenu for SceneNodeContextMenu {
|
||||
fn menu(&self) -> RcUiNodeHandle {
|
||||
self.menu.clone()
|
||||
}
|
||||
|
||||
fn on_plugin_added(
|
||||
&mut self,
|
||||
serialization_context: &SerializationContext,
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ui: &mut UserInterface,
|
||||
) {
|
||||
for (handle, menu) in [
|
||||
(self.create_child_menu, &mut self.create_child_entity_menu),
|
||||
(self.create_parent_menu, &mut self.create_parent_entity_menu),
|
||||
(self.replace_with_menu, &mut self.replace_with_entity_menu),
|
||||
] {
|
||||
*menu = CreateEntityMenu::new(
|
||||
serialization_context,
|
||||
widget_constructors_container,
|
||||
&mut ui.build_ctx(),
|
||||
);
|
||||
ui.send(handle, MenuItemMessage::Items(menu.root_items.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resource_path_of_first_selected_node(
|
||||
@@ -145,11 +118,7 @@ impl SceneNodeContextMenu {
|
||||
pub const RESET_INHERITABLE: Uuid = uuid!("95c6437f-dc23-4ec1-9490-d35f0864f027");
|
||||
pub const SAVE_AS_PREFAB_FILE_SELECTOR: Uuid = uuid!("5d438037-a4be-4d70-a830-138185e1a049");
|
||||
|
||||
pub fn new(
|
||||
serialization_context: &SerializationContext,
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ctx: &mut BuildContext,
|
||||
) -> Self {
|
||||
pub fn new(ctx: &mut BuildContext) -> Self {
|
||||
let delete_selection;
|
||||
let copy_selection;
|
||||
let save_as_prefab;
|
||||
@@ -161,13 +130,6 @@ impl SceneNodeContextMenu {
|
||||
let create_child;
|
||||
let replace_with;
|
||||
|
||||
let create_child_entity_menu =
|
||||
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
|
||||
let create_parent_entity_menu =
|
||||
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
|
||||
let replace_with_entity_menu =
|
||||
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
|
||||
|
||||
let menu = ContextMenuBuilder::new(
|
||||
PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
|
||||
.with_content(
|
||||
@@ -177,8 +139,7 @@ impl SceneNodeContextMenu {
|
||||
create_child = MenuItemBuilder::new(
|
||||
WidgetBuilder::new().with_min_size(Vector2::new(120.0, 22.0)),
|
||||
)
|
||||
.with_content(MenuItemContent::text("Create Child Node"))
|
||||
.with_items(create_child_entity_menu.root_items.clone())
|
||||
.with_content(MenuItemContent::text("Create Child Node..."))
|
||||
.build(ctx);
|
||||
create_child
|
||||
})
|
||||
@@ -221,8 +182,7 @@ impl SceneNodeContextMenu {
|
||||
.with_id(Self::CREATE_PARENT)
|
||||
.with_min_size(Vector2::new(120.0, 22.0)),
|
||||
)
|
||||
.with_content(MenuItemContent::text("Create Parent Node"))
|
||||
.with_items(create_parent_entity_menu.root_items.clone())
|
||||
.with_content(MenuItemContent::text("Create Parent Node..."))
|
||||
.build(ctx);
|
||||
create_parent
|
||||
})
|
||||
@@ -232,8 +192,7 @@ impl SceneNodeContextMenu {
|
||||
.with_id(Self::REPLACE_WITH)
|
||||
.with_min_size(Vector2::new(120.0, 22.0)),
|
||||
)
|
||||
.with_content(MenuItemContent::text("Replace With Node"))
|
||||
.with_items(replace_with_entity_menu.root_items.clone())
|
||||
.with_content(MenuItemContent::text("Replace With Node..."))
|
||||
.build(ctx);
|
||||
replace_with
|
||||
})
|
||||
@@ -290,19 +249,16 @@ impl SceneNodeContextMenu {
|
||||
}
|
||||
|
||||
Self {
|
||||
create_child_entity_menu,
|
||||
menu,
|
||||
delete_selection,
|
||||
copy_selection,
|
||||
placement_target: Default::default(),
|
||||
save_as_prefab,
|
||||
save_as_prefab_dialog: Default::default(),
|
||||
replace_with_entity_menu,
|
||||
paste,
|
||||
make_root,
|
||||
open_asset,
|
||||
reset_inheritable_properties,
|
||||
create_parent_entity_menu,
|
||||
create_child_menu: create_child,
|
||||
create_parent_menu: create_parent,
|
||||
replace_with_menu: replace_with,
|
||||
@@ -318,82 +274,6 @@ impl SceneNodeContextMenu {
|
||||
sender: &MessageSender,
|
||||
settings: &Settings,
|
||||
) {
|
||||
if let Some(node) = self.create_child_entity_menu.handle_ui_message(
|
||||
message,
|
||||
sender,
|
||||
controller,
|
||||
editor_selection,
|
||||
engine,
|
||||
) {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(parent) = graph_selection.nodes().first() {
|
||||
sender.do_command(AddNodeCommand::new(node, *parent, true));
|
||||
}
|
||||
}
|
||||
} else if let Some(node) = self.create_parent_entity_menu.handle_ui_message(
|
||||
message,
|
||||
sender,
|
||||
controller,
|
||||
editor_selection,
|
||||
engine,
|
||||
) {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(first) = graph_selection.nodes().first() {
|
||||
if let Some(game_scene) = controller.downcast_ref::<GameScene>() {
|
||||
let scene = &engine.scenes[game_scene.scene];
|
||||
|
||||
let position = game_scene
|
||||
.camera_controller
|
||||
.placement_position(&scene.graph, *first);
|
||||
|
||||
let first_ref = &scene.graph[*first];
|
||||
let parent = if first_ref.parent().is_some() {
|
||||
first_ref.parent()
|
||||
} else {
|
||||
game_scene.scene_content_root
|
||||
};
|
||||
|
||||
let new_parent_handle = scene.graph.generate_free_handles(1)[0];
|
||||
let mut commands = CommandGroup::from(vec![
|
||||
Command::new(AddNodeCommand::new(node, parent, true)),
|
||||
Command::new(LinkNodesCommand::new(*first, new_parent_handle)),
|
||||
]);
|
||||
|
||||
if parent == game_scene.scene_content_root {
|
||||
commands.push(MoveNodeCommand::new(
|
||||
new_parent_handle,
|
||||
Vector3::default(),
|
||||
position,
|
||||
));
|
||||
}
|
||||
|
||||
if *first == game_scene.scene_content_root {
|
||||
commands.push(SetGraphRootCommand {
|
||||
root: new_parent_handle,
|
||||
link_scheme: Default::default(),
|
||||
})
|
||||
}
|
||||
sender.do_command(commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(replacement) = self.replace_with_entity_menu.handle_ui_message(
|
||||
message,
|
||||
sender,
|
||||
controller,
|
||||
editor_selection,
|
||||
engine,
|
||||
) {
|
||||
if let Some(graph_selection) = editor_selection.as_graph() {
|
||||
if let Some(first) = graph_selection.nodes().first() {
|
||||
sender.do_command(ReplaceNodeCommand {
|
||||
handle: *first,
|
||||
node: replacement,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(game_scene) = controller.downcast_mut::<GameScene>() {
|
||||
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
|
||||
if message.destination() == self.delete_selection {
|
||||
@@ -503,6 +383,14 @@ impl SceneNodeContextMenu {
|
||||
}
|
||||
sender.do_command(CommandGroup::from(commands));
|
||||
}
|
||||
} else if message.destination() == self.create_child_menu {
|
||||
sender.send(Message::OpenEntityCreator(EntityCreatorMode::CreateChild));
|
||||
} else if message.destination() == self.create_parent_menu {
|
||||
sender.send(Message::OpenEntityCreator(EntityCreatorMode::CreateParent));
|
||||
} else if message.destination() == self.replace_with_menu {
|
||||
sender.send(Message::OpenEntityCreator(
|
||||
EntityCreatorMode::CreateReplacement,
|
||||
));
|
||||
}
|
||||
} else if let Some(PopupMessage::Placement(Placement::Cursor(target))) = message.data()
|
||||
{
|
||||
|
||||
@@ -57,9 +57,7 @@ use crate::{
|
||||
};
|
||||
use fyrox::core::color::Color;
|
||||
use fyrox::core::pool::HandlesVecExtension;
|
||||
use fyrox::engine::SerializationContext;
|
||||
use fyrox::gui::button::Button;
|
||||
use fyrox::gui::constructor::WidgetConstructorContainer;
|
||||
use fyrox::gui::scroll_viewer::ScrollViewer;
|
||||
use fyrox::gui::searchbar::SearchBar;
|
||||
use fyrox::gui::text_box::EmptyTextPlaceholder;
|
||||
@@ -78,6 +76,7 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
pub mod create;
|
||||
pub mod graph;
|
||||
pub mod item;
|
||||
pub mod menu;
|
||||
@@ -139,12 +138,6 @@ pub trait WorldViewerDataProvider {
|
||||
|
||||
pub trait WorldViewerItemContextMenu {
|
||||
fn menu(&self) -> RcUiNodeHandle;
|
||||
fn on_plugin_added(
|
||||
&mut self,
|
||||
serialization_context: &SerializationContext,
|
||||
widget_constructors_container: &WidgetConstructorContainer,
|
||||
ui: &mut UserInterface,
|
||||
);
|
||||
}
|
||||
|
||||
pub struct WorldViewer {
|
||||
|
||||
Reference in New Issue
Block a user