diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml deleted file mode 100644 index abfcdf3ce..000000000 --- a/.github/workflows/audit.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: audit - -# This is a separate file so it can have a separate badge in readme -# and therefore spread awareness of cargo audit a tiny bit. - -on: - push: - branches: [master] - pull_request: - branches: [master] - schedule: - # Runs at 15:00 UTC on Fri - - cron: "0 15 * * 5" - workflow_dispatch: - # This allows running it on any branch manually: - # https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow - -env: - CARGO_TERM_COLOR: always - -jobs: - audit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # Actions-rs/install is unmaintained but works for now. - # Alternatives for when it breaks: - # - dtolnay/install - not a full replacement - only supports crates he uses (not audit currently) - # - baptiste0928/cargo-install - looks like it just compiles the crate and then caches it for subsequent runs - # - just `cargo install` and caching it - - uses: actions-rs/install@v0.1 - with: - crate: cargo-audit - version: latest - - run: cargo audit --version - # RUSTSEC-2020-0097: xcb - Soundness issue with base::Error - # RUSTSEC-2022-0048: xml-rs is Unmaintained - # For more info: https://github.com/FyroxEngine/Fyrox/issues/208 - - run: cargo audit --deny warnings --ignore RUSTSEC-2020-0097 --ignore RUSTSEC-2022-0048 --ignore RUSTSEC-2021-0140 diff --git a/README.md b/README.md index 8f60d12c2..a3739735b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ [![License (MIT)](https://img.shields.io/crates/l/fyrox)](https://github.com/FyroxEngine/Fyrox/blob/master/LICENSE.md) [![CI Status](https://github.com/FyroxEngine/Fyrox/actions/workflows/ci.yml/badge.svg)](https://github.com/FyroxEngine/Fyrox/actions/workflows/ci.yml) -[![audit](https://github.com/FyroxEngine/Fyrox/actions/workflows/audit.yml/badge.svg)](https://github.com/FyroxEngine/Fyrox/actions/workflows/audit.yml) [![Dependency status](https://deps.rs/repo/github/FyroxEngine/Fyrox/status.svg)](https://deps.rs/repo/github/FyroxEngine/Fyrox) [![Crates.io](https://img.shields.io/crates/v/fyrox)](https://crates.io/crates/fyrox) [![docs.rs](https://img.shields.io/badge/docs-website-blue)](https://docs.rs/Fyrox/) diff --git a/editor/src/asset/inspector/mod.rs b/editor/src/asset/inspector/mod.rs index 27e420664..21c2edf9d 100644 --- a/editor/src/asset/inspector/mod.rs +++ b/editor/src/asset/inspector/mod.rs @@ -37,9 +37,8 @@ use crate::fyrox::{ BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, }, }; -use crate::{ - inspector::editors::make_property_editors_container, message::MessageSender, MSG_SYNC_FLAG, -}; +use crate::plugins::inspector::editors::make_property_editors_container; +use crate::{message::MessageSender, MSG_SYNC_FLAG}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/editor/src/asset/item.rs b/editor/src/asset/item.rs index e1f627f91..c02f2b780 100644 --- a/editor/src/asset/item.rs +++ b/editor/src/asset/item.rs @@ -39,7 +39,7 @@ use crate::{ text::TextBuilder, widget::{Widget, WidgetBuilder, WidgetMessage}, BuildContext, Control, HorizontalAlignment, RcUiNodeHandle, Thickness, UiNode, - UserInterface, BRUSH_DARKER, BRUSH_DARKEST, + UserInterface, }, material::Material, scene::tilemap::tileset::TileSet, @@ -47,6 +47,8 @@ use crate::{ message::MessageSender, Message, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ ops::{Deref, DerefMut}, path::{Path, PathBuf}, @@ -254,14 +256,14 @@ fn make_tooltip(ctx: &mut BuildContext, text: &str) -> RcUiNodeHandle { let handle = BorderBuilder::new( WidgetBuilder::new() .with_visibility(false) - .with_foreground(BRUSH_DARKEST) + .with_foreground(ctx.style.get_or_default(Style::BRUSH_DARKEST)) .with_background(Brush::Solid(Color::opaque(230, 230, 230))) .with_max_size(Vector2::new(300.0, f32::INFINITY)) .with_child( TextBuilder::new( WidgetBuilder::new() .with_margin(Thickness::uniform(2.0)) - .with_foreground(BRUSH_DARKER), + .with_foreground(ctx.style.get_or_default(Style::BRUSH_DARKER)), ) .with_wrap(WrapMode::Letter) .with_text(text) @@ -337,7 +339,7 @@ impl AssetItemBuilder { .add_row(Row::auto()) .build(ctx), ) - .build(), + .build(ctx), path, preview, selected: false, @@ -347,3 +349,18 @@ impl AssetItemBuilder { ctx.add_node(UiNode::new(item)) } } + +#[cfg(test)] +mod test { + use crate::asset::item::AssetItemBuilder; + use fyrox::asset::manager::ResourceManager; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + let rm = ResourceManager::new(Default::default()); + test_widget_deletion(|ctx| { + AssetItemBuilder::new(WidgetBuilder::new()).build(rm, Default::default(), ctx) + }); + } +} diff --git a/editor/src/asset/mod.rs b/editor/src/asset/mod.rs index ce0802b46..5c5933c0b 100644 --- a/editor/src/asset/mod.rs +++ b/editor/src/asset/mod.rs @@ -61,7 +61,7 @@ use crate::{ window::{WindowBuilder, WindowMessage, WindowTitle}, wrap_panel::WrapPanelBuilder, BuildContext, HorizontalAlignment, Orientation, RcUiNodeHandle, Thickness, UiNode, - UserInterface, VerticalAlignment, BRUSH_DARK, + UserInterface, VerticalAlignment, }, resource::{model::Model, texture::Texture}, scene::sound::SoundBuffer, @@ -74,6 +74,8 @@ use crate::{ utils::window_content, Message, Mode, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use std::fs::File; use std::io::Write; @@ -620,7 +622,7 @@ impl AssetBrowser { ctx, 18.0, 18.0, - load_image(include_bytes!("../../resources/reimport.png")), + load_image!("../../resources/reimport.png"), "Refresh", Some(1), ); @@ -659,7 +661,7 @@ impl AssetBrowser { .with_child( BorderBuilder::new( WidgetBuilder::new() - .with_background(BRUSH_DARK) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARK)) .with_child({ folder_browser = FileBrowserBuilder::new( WidgetBuilder::new() @@ -819,7 +821,7 @@ impl AssetBrowser { WidgetBuilder::new().with_context_menu(self.context_menu.menu.clone()), ) .with_icon(if is_dir { - load_image(include_bytes!("../../resources/folder.png")) + load_image!("../../resources/folder.png") } else { None }) @@ -924,9 +926,7 @@ impl AssetBrowser { let asset_item = AssetItemBuilder::new( WidgetBuilder::new().with_context_menu(self.context_menu.menu.clone()), ) - .with_icon(load_image(include_bytes!( - "../../resources/folder_return.png" - ))) + .with_icon(load_image!("../../resources/folder_return.png")) .with_path(parent_path) .build( resource_manager.clone(), diff --git a/editor/src/asset/preview/mod.rs b/editor/src/asset/preview/mod.rs index 50ca469eb..bd425e9fb 100644 --- a/editor/src/asset/preview/mod.rs +++ b/editor/src/asset/preview/mod.rs @@ -285,7 +285,7 @@ impl AssetPreviewGenerator for SoundPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/sound.png")) + load_image!("../../../resources/sound.png") } } @@ -410,7 +410,7 @@ impl AssetPreviewGenerator for ModelPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/model.png")) + load_image!("../../../resources/model.png") } } @@ -451,7 +451,7 @@ impl AssetPreviewGenerator for SurfaceDataPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/model.png")) + load_image!("../../../resources/model.png") } } @@ -495,7 +495,7 @@ impl AssetPreviewGenerator for ShaderPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/shader.png")) + load_image!("../../../resources/shader.png") } } @@ -539,7 +539,7 @@ impl AssetPreviewGenerator for MaterialPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/material.png")) + load_image!("../../../resources/material.png") } } @@ -570,7 +570,7 @@ impl AssetPreviewGenerator for HrirPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/hrir.png")) + load_image!("../../../resources/hrir.png") } } @@ -600,7 +600,7 @@ impl AssetPreviewGenerator for CurvePreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/curve.png")) + load_image!("../../../resources/curve.png") } } @@ -678,7 +678,7 @@ impl AssetPreviewGenerator for FontPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/font.png")) + load_image!("../../../resources/font.png") } } @@ -713,6 +713,6 @@ impl AssetPreviewGenerator for UserInterfacePreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/ui.png")) + load_image!("../../../resources/ui.png") } } diff --git a/editor/src/audio/bus.rs b/editor/src/audio/bus.rs index b9e6ad77b..1e2e27a74 100644 --- a/editor/src/audio/bus.rs +++ b/editor/src/audio/bus.rs @@ -35,11 +35,13 @@ use crate::fyrox::{ utils::make_simple_tooltip, widget::{Widget, WidgetBuilder}, BuildContext, Control, HorizontalAlignment, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_LIGHTER, + VerticalAlignment, }, scene::sound::{AudioBus, AudioBusGraph}, }; use crate::gui::make_dropdown_list_option; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] @@ -136,12 +138,12 @@ fn make_items(buses: &[(Handle, String)], ctx: &mut BuildContext) -> V fn make_effect_names(names: &[String], ctx: &mut BuildContext) -> Vec> { if names.is_empty() { - vec![ - TextBuilder::new(WidgetBuilder::new().with_foreground(BRUSH_LIGHTER)) - .with_text("No Effects") - .with_horizontal_text_alignment(HorizontalAlignment::Center) - .build(ctx), - ] + vec![TextBuilder::new( + WidgetBuilder::new().with_foreground(ctx.style.get_or_default(Style::BRUSH_LIGHTER)), + ) + .with_text("No Effects") + .with_horizontal_text_alignment(HorizontalAlignment::Center) + .build(ctx)] } else { names .iter() @@ -281,7 +283,7 @@ impl AudioBusViewBuilder { )) .build(ctx), ) - .build(), + .build(ctx), bus: self.bus, parent_bus_selector, possible_parent_buses: self @@ -295,3 +297,14 @@ impl AudioBusViewBuilder { ctx.add_node(UiNode::new(view)) } } + +#[cfg(test)] +mod test { + use crate::audio::bus::AudioBusViewBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| AudioBusViewBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/audio/mod.rs b/editor/src/audio/mod.rs index de8d4d3a7..ba3741a6e 100644 --- a/editor/src/audio/mod.rs +++ b/editor/src/audio/mod.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::inspector::editors::resource::{ResourceFieldBuilder, ResourceFieldMessage}; use crate::{ audio::bus::{AudioBusView, AudioBusViewBuilder, AudioBusViewMessage}, command::CommandGroup, @@ -41,7 +42,6 @@ use crate::{ scene::sound::{AudioBus, AudioBusGraph, DistanceModel, HrirSphereResourceData, Renderer}, }, gui::make_dropdown_list_option, - inspector::editors::resource::{ResourceFieldBuilder, ResourceFieldMessage}, message::MessageSender, scene::{ commands::{ diff --git a/editor/src/build.rs b/editor/src/build.rs index e7e24e549..593959c50 100644 --- a/editor/src/build.rs +++ b/editor/src/build.rs @@ -31,10 +31,11 @@ use crate::fyrox::{ widget::WidgetBuilder, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - BRUSH_DARKEST, }, }; use crate::{message::MessageSender, Message}; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ io::{BufRead, BufReader}, process::ChildStderr, @@ -78,7 +79,7 @@ impl BuildWindow { WidgetBuilder::new() .on_row(1) .with_margin(Thickness::uniform(2.0)) - .with_background(BRUSH_DARKEST) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARKEST)) .with_child({ scroll_viewer = ScrollViewerBuilder::new(WidgetBuilder::new()) diff --git a/editor/src/camera/panel.rs b/editor/src/camera/panel.rs index 7273e5ba0..6c36f231c 100644 --- a/editor/src/camera/panel.rs +++ b/editor/src/camera/panel.rs @@ -18,23 +18,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::graph::SceneGraph; -use crate::fyrox::gui::{HorizontalAlignment, Thickness}; -use crate::fyrox::{ - core::pool::Handle, - engine::Engine, - gui::{ - check_box::{CheckBoxBuilder, CheckBoxMessage}, - message::{MessageDirection, UiMessage}, - stack_panel::StackPanelBuilder, - text::TextBuilder, - widget::WidgetBuilder, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, Orientation, UiNode, VerticalAlignment, - }, - scene::{camera::Camera, node::Node}, -}; use crate::{ + fyrox::{ + core::pool::Handle, + engine::Engine, + graph::SceneGraph, + gui::{ + check_box::{CheckBoxBuilder, CheckBoxMessage}, + message::{MessageDirection, UiMessage}, + stack_panel::StackPanelBuilder, + text::TextBuilder, + widget::WidgetBuilder, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, Orientation, UiNode, VerticalAlignment, + }, + gui::{HorizontalAlignment, Thickness}, + scene::{camera::Camera, node::Node}, + }, scene::{GameScene, Selection}, send_sync_message, Message, }; @@ -49,31 +49,36 @@ pub struct CameraPreviewControlPanel { impl CameraPreviewControlPanel { pub fn new(scene_viewer_frame: Handle, ctx: &mut BuildContext) -> Self { let preview; - let window = WindowBuilder::new(WidgetBuilder::new().with_name("CameraPanel")) - .with_title(WindowTitle::text("Camera Preview")) - .with_content( - StackPanelBuilder::new( - WidgetBuilder::new() - .with_margin(Thickness::uniform(1.0)) - .with_child({ - preview = CheckBoxBuilder::new(WidgetBuilder::new()) - .with_content( - TextBuilder::new( - WidgetBuilder::new().with_margin(Thickness::uniform(1.0)), - ) - .with_text("Preview") - .with_vertical_text_alignment(VerticalAlignment::Center) - .build(ctx), + let window = WindowBuilder::new( + WidgetBuilder::new() + .with_width(200.0) + .with_height(50.0) + .with_name("CameraPanel"), + ) + .with_title(WindowTitle::text("Camera Preview")) + .with_content( + StackPanelBuilder::new( + WidgetBuilder::new() + .with_margin(Thickness::uniform(1.0)) + .with_child({ + preview = CheckBoxBuilder::new(WidgetBuilder::new()) + .with_content( + TextBuilder::new( + WidgetBuilder::new().with_margin(Thickness::uniform(1.0)), ) - .build(ctx); - preview - }), - ) - .with_orientation(Orientation::Vertical) - .build(ctx), + .with_text("Preview") + .with_vertical_text_alignment(VerticalAlignment::Center) + .build(ctx), + ) + .build(ctx); + preview + }), ) - .open(false) - .build(ctx); + .with_orientation(Orientation::Vertical) + .build(ctx), + ) + .open(false) + .build(ctx); Self { window, diff --git a/editor/src/command/panel.rs b/editor/src/command/panel.rs index 5eccf701d..272183be8 100644 --- a/editor/src/command/panel.rs +++ b/editor/src/command/panel.rs @@ -67,7 +67,7 @@ impl CommandStackViewer { ctx, 20.0, 20.0, - load_image(include_bytes!("../../resources/undo.png")), + load_image!("../../resources/undo.png"), "Undo The Command", Some(0), ); @@ -78,7 +78,7 @@ impl CommandStackViewer { ctx, 20.0, 20.0, - load_image(include_bytes!("../../resources/redo.png")), + load_image!("../../resources/redo.png"), "Redo The Command", Some(1), ); @@ -89,7 +89,7 @@ impl CommandStackViewer { ctx, 20.0, 20.0, - load_image(include_bytes!("../../resources/clear.png")), + load_image!("../../resources/clear.png"), "Clear Command Stack\nChanges history will be erased.", Some(2), ); diff --git a/editor/src/export.rs b/editor/src/export.rs index 08c56dd10..2333f7539 100644 --- a/editor/src/export.rs +++ b/editor/src/export.rs @@ -48,7 +48,7 @@ use crate::{ window::{WindowBuilder, WindowMessage, WindowTitle}, wrap_panel::WrapPanelBuilder, BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_DARKER, BRUSH_LIGHT, + VerticalAlignment, }, }, gui::make_dropdown_list_option, @@ -57,6 +57,8 @@ use crate::{ }; use cargo_metadata::{camino::Utf8Path, Metadata}; use fyrox::graph::SceneGraph; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ ffi::OsStr, fmt::{Display, Formatter}, @@ -826,7 +828,7 @@ impl ExportWindow { WidgetBuilder::new() .on_row(3) .with_margin(Thickness::uniform(2.0)) - .with_background(BRUSH_LIGHT) + .with_background(ctx.style.get_or_default(Style::BRUSH_LIGHT)) .with_child( ScrollViewerBuilder::new( WidgetBuilder::new().with_margin(Thickness::uniform(2.0)), @@ -862,7 +864,7 @@ impl ExportWindow { BorderBuilder::new( WidgetBuilder::new() .on_row(1) - .with_background(BRUSH_DARKER) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARKER)) .with_margin(Thickness::uniform(2.0)) .with_child({ log_scroll_viewer = ScrollViewerBuilder::new( diff --git a/editor/src/highlight.rs b/editor/src/highlight.rs index 6b7301303..a1794c31c 100644 --- a/editor/src/highlight.rs +++ b/editor/src/highlight.rs @@ -195,17 +195,20 @@ impl SceneRenderPass for HighlightRenderPass { { let render_pass_name = ImmutableString::new("Forward"); - let mut render_bundle_storage = RenderDataBundleStorage::default(); + let observer_info = ObserverInfo { + observer_position: ctx.camera.global_position(), + z_near: ctx.camera.projection().z_near(), + z_far: ctx.camera.projection().z_far(), + view_matrix: ctx.camera.view_matrix(), + projection_matrix: ctx.camera.projection_matrix(), + }; + + let mut render_bundle_storage = + RenderDataBundleStorage::new_empty(observer_info.clone()); let frustum = ctx.camera.frustum(); let mut render_context = RenderContext { - observer_info: &ObserverInfo { - observer_position: ctx.camera.global_position(), - z_near: ctx.camera.projection().z_near(), - z_far: ctx.camera.projection().z_far(), - view_matrix: ctx.camera.view_matrix(), - projection_matrix: ctx.camera.projection_matrix(), - }, + observer_info: &observer_info, frustum: Some(&frustum), storage: &mut render_bundle_storage, graph: &ctx.scene.graph, diff --git a/editor/src/interaction/mod.rs b/editor/src/interaction/mod.rs index cd0a90142..73a998905 100644 --- a/editor/src/interaction/mod.rs +++ b/editor/src/interaction/mod.rs @@ -37,17 +37,18 @@ use crate::{ message::{KeyCode, UiMessage}, utils::make_simple_tooltip, widget::WidgetBuilder, - BuildContext, Thickness, UiNode, BRUSH_BRIGHT_BLUE, BRUSH_DARKER, BRUSH_LIGHT, - BRUSH_LIGHTER, BRUSH_LIGHTEST, + BuildContext, Thickness, UiNode, }, scene::{camera::Projection, graph::Graph, node::Node}, }, - load_image, + load_image_internal, message::MessageSender, scene::{controller::SceneController, Selection}, settings::Settings, Engine, Message, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::any::Any; pub mod gizmo; @@ -211,15 +212,17 @@ pub fn make_interaction_mode_button( ) .with_back( DecoratorBuilder::new( - BorderBuilder::new(WidgetBuilder::new().with_foreground(BRUSH_DARKER)) - .with_pad_by_corner_radius(false) - .with_corner_radius(4.0) - .with_stroke_thickness(Thickness::uniform(1.0)), + BorderBuilder::new( + WidgetBuilder::new().with_foreground(ctx.style.get_or_default(Style::BRUSH_DARKER)), + ) + .with_pad_by_corner_radius(false) + .with_corner_radius(4.0) + .with_stroke_thickness(Thickness::uniform(1.0)), ) - .with_normal_brush(BRUSH_LIGHT) - .with_hover_brush(BRUSH_LIGHTER) - .with_pressed_brush(BRUSH_LIGHTEST) - .with_selected_brush(BRUSH_BRIGHT_BLUE) + .with_normal_brush(ctx.style.get_or_default(Style::BRUSH_LIGHT)) + .with_hover_brush(ctx.style.get_or_default(Style::BRUSH_LIGHTER)) + .with_pressed_brush(ctx.style.get_or_default(Style::BRUSH_LIGHTEST)) + .with_selected_brush(ctx.style.get_or_default(Style::BRUSH_BRIGHT_BLUE)) .with_selected(selected) .build(ctx), ) @@ -231,7 +234,7 @@ pub fn make_interaction_mode_button( .with_width(23.0) .with_height(23.0), ) - .with_opt_texture(load_image(image)) + .with_opt_texture(load_image_internal(image)) .build(ctx), ) .build(ctx) diff --git a/editor/src/lib.rs b/editor/src/lib.rs index b1a47ae8f..3db62a423 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -30,29 +30,23 @@ #[macro_use] extern crate lazy_static; -pub mod absm; -pub mod animation; pub mod asset; pub mod audio; pub mod build; pub mod camera; pub mod command; pub mod configurator; -pub mod curve_editor; pub mod export; pub mod gui; pub mod highlight; -pub mod inspector; pub mod interaction; pub mod light; pub mod log; -pub mod material; pub mod menu; pub mod mesh; pub mod message; pub mod overlay; pub mod particle; -pub mod physics; pub mod plugin; pub mod plugins; pub mod preview; @@ -66,16 +60,14 @@ pub mod world; pub use fyrox; +use crate::plugins::path_fixer::PathFixerPlugin; use crate::{ - absm::AbsmEditor, - animation::AnimationEditor, asset::AssetBrowser, audio::{preview::AudioPreviewPanel, AudioPanel}, build::BuildWindow, camera::panel::CameraPreviewControlPanel, command::{panel::CommandStackViewer, Command, CommandTrait}, configurator::Configurator, - curve_editor::CurveEditorWindow, export::ExportWindow, fyrox::{ asset::{io::FsResourceIo, manager::ResourceManager, untyped::UntypedResource}, @@ -84,6 +76,7 @@ use crate::{ color::Color, futures::executor::block_on, log::{Log, MessageKind}, + parking_lot::Mutex, pool::Handle, task::TaskPool, uuid::Uuid, @@ -132,7 +125,6 @@ use crate::{ window::{Icon, WindowAttributes}, }, highlight::HighlightRenderPass, - inspector::InspectorPlugin, interaction::{ move_mode::MoveInteractionMode, navmesh::{EditNavmeshMode, NavmeshPanel}, @@ -143,15 +135,17 @@ use crate::{ }, light::LightPanel, log::LogPanel, - material::MaterialEditor, menu::{Menu, MenuContext, Panels}, mesh::{MeshControlPanel, SurfaceDataViewer}, message::MessageSender, overlay::OverlayRenderPass, particle::ParticleSystemPreviewControlPanel, - physics::ColliderControlPanel, - plugin::EditorPlugin, - plugins::collider::ColliderShapePlugin, + plugin::{EditorPlugin, EditorPluginsContainer}, + plugins::{ + absm::AbsmEditorPlugin, animation::AnimationEditorPlugin, collider::ColliderPlugin, + curve_editor::CurveEditorPlugin, material::MaterialPlugin, ragdoll::RagdollPlugin, + settings::SettingsPlugin, stats::UiStatisticsPlugin, tilemap::TileMapEditorPlugin, + }, scene::{ commands::{ make_delete_selection_command, ChangeSelectionCommand, GameSceneContext, PasteCommand, @@ -169,9 +163,12 @@ use crate::{ commands::graph::PasteWidgetCommand, menu::WidgetContextMenu, utils::UiSceneWorldViewerDataProvider, UiScene, }, - utils::{doc::DocWindow, path_fixer::PathFixer, ragdoll::RagdollWizard}, + utils::doc::DocWindow, world::{graph::menu::SceneNodeContextMenu, graph::EditorSceneWrapper, WorldViewer}, }; +use fyrox::gui::constructor::new_widget_constructor_container; +pub use message::Message; +use plugins::inspector::InspectorPlugin; use std::{ cell::RefCell, collections::VecDeque, @@ -187,10 +184,6 @@ use std::{ time::{Duration, Instant}, }; -use crate::plugin::EditorPluginsContainer; -use crate::plugins::tilemap::TileMapEditorPlugin; -pub use message::Message; - pub const FIXED_TIMESTEP: f32 = 1.0 / 60.0; pub const MSG_SYNC_FLAG: u64 = 1; @@ -199,19 +192,52 @@ pub fn send_sync_message(ui: &UserInterface, mut msg: UiMessage) { ui.send_message(msg); } -pub fn load_texture(data: &[u8]) -> Option { - TextureResource::load_from_memory( - Default::default(), - data, - TextureImportOptions::default() - .with_compression(CompressionOptions::NoCompression) - .with_minification_filter(TextureMinificationFilter::Linear), - ) - .ok() +lazy_static! { + static ref EDITOR_TEXTURE_CACHE: Mutex> = Default::default(); } -pub fn load_image(data: &[u8]) -> Option { - Some(load_texture(data)?.into()) +pub fn load_texture_internal(data: &[u8]) -> Option { + let mut cache = EDITOR_TEXTURE_CACHE.lock(); + + // Editor use data that is embedded in the binary, so each such piece of data will have fixed + // location in memory. This fact allows us to cache the resources and skip redundant loading if + // they're already loaded. + let id = data.as_ptr() as usize; + + if let Some(existing) = cache.get(&id) { + Some(existing.clone()) + } else { + let texture = TextureResource::load_from_memory( + Default::default(), + data, + TextureImportOptions::default() + .with_compression(CompressionOptions::NoCompression) + .with_minification_filter(TextureMinificationFilter::Linear), + ) + .ok()?; + + cache.insert(id, texture.clone()); + + Some(texture) + } +} + +pub fn load_image_internal(data: &[u8]) -> Option { + Some(load_texture_internal(data)?.into()) +} + +#[macro_export] +macro_rules! load_texture { + ($file:expr $(,)?) => { + $crate::load_texture_internal(include_bytes!($file)) + }; +} + +#[macro_export] +macro_rules! load_image { + ($file:expr $(,)?) => { + $crate::load_image_internal(include_bytes!($file)) + }; } lazy_static! { @@ -261,7 +287,8 @@ pub fn make_save_file_selector( FileSelectorBuilder::new( WindowBuilder::new(WidgetBuilder::new().with_width(300.0).with_height(400.0)) .with_title(WindowTitle::text("Save Scene As")) - .open(false), + .open(false) + .with_remove_on_close(true), ) .with_mode(FileBrowserMode::Save { default_file_name }) .with_path("./") @@ -506,15 +533,10 @@ pub struct Editor { pub validation_message_box: Handle, pub navmesh_panel: NavmeshPanel, pub settings: Settings, - pub path_fixer: PathFixer, - pub material_editor: MaterialEditor, - pub curve_editor: CurveEditorWindow, pub audio_panel: AudioPanel, - pub absm_editor: AbsmEditor, pub mode: Mode, pub build_window: BuildWindow, pub scene_settings: SceneSettingsWindow, - pub animation_editor: AnimationEditor, pub particle_system_control_panel: ParticleSystemPreviewControlPanel, pub camera_control_panel: CameraPreviewControlPanel, pub mesh_control_panel: MeshControlPanel, @@ -527,15 +549,14 @@ pub struct Editor { pub focused: bool, pub update_loop_state: UpdateLoopState, pub is_suspended: bool, - pub ragdoll_wizard: RagdollWizard, pub scene_node_context_menu: Rc>, pub widget_context_menu: Rc>, - pub collider_control_panel: ColliderControlPanel, pub overlay_pass: Option>>, pub highlighter: Option>>, pub export_window: Option, pub statistics_window: Option, pub surface_data_viewer: Option, + pub processed_ui_messages: usize, } impl Editor { @@ -587,7 +608,7 @@ impl Editor { resource_manager: ResourceManager::new(task_pool.clone()), serialization_context, task_pool, - widget_constructors: Arc::new(Default::default()), + widget_constructors: Arc::new(new_widget_constructor_container()), }) .unwrap(); @@ -612,27 +633,28 @@ impl Editor { let menu = Menu::new(&mut engine, message_sender.clone(), &settings); let light_panel = LightPanel::new(&mut engine, message_sender.clone()); let audio_panel = AudioPanel::new(&mut engine, message_sender.clone()); - let material_editor = MaterialEditor::new(&mut engine, message_sender.clone()); - let ctx = &mut engine.user_interfaces.first_mut().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(ctx))); - let widget_context_menu = Rc::new(RefCell::new(WidgetContextMenu::new(ctx))); + 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 world_outliner = WorldViewer::new(ctx, message_sender.clone(), &settings); let command_stack_viewer = CommandStackViewer::new(ctx, message_sender.clone()); let log = LogPanel::new(ctx, log_message_receiver); let inspector_plugin = InspectorPlugin::new(ctx, message_sender.clone()); - let animation_editor = AnimationEditor::new(ctx); - let absm_editor = AbsmEditor::new(ctx, message_sender.clone()); let particle_system_control_panel = ParticleSystemPreviewControlPanel::new(scene_viewer.frame(), ctx); let camera_control_panel = CameraPreviewControlPanel::new(scene_viewer.frame(), ctx); let mesh_control_panel = MeshControlPanel::new(scene_viewer.frame(), ctx); let audio_preview_panel = AudioPreviewPanel::new(scene_viewer.frame(), ctx); - let collider_control_panel = ColliderControlPanel::new(scene_viewer.frame(), ctx); let doc_window = DocWindow::new(ctx); let node_removal_dialog = NodeRemovalDialog::new(ctx); - let ragdoll_wizard = RagdollWizard::new(ctx, message_sender.clone()); let scene_settings = SceneSettingsWindow::new(ctx, message_sender.clone()); let docking_manager; @@ -754,19 +776,14 @@ impl Editor { .build(ctx) })) .with_floating_windows(vec![ - animation_editor.window, - absm_editor.window, particle_system_control_panel.window, camera_control_panel.window, mesh_control_panel.window, audio_preview_panel.window, - collider_control_panel.window, navmesh_panel.window, doc_window.window, light_panel.window, - menu.file_menu.settings.window, scene_settings.window, - material_editor.window, ]) .build(ctx); docking_manager @@ -797,14 +814,8 @@ impl Editor { .with_buttons(MessageBoxButtons::Ok) .build(ctx); - let path_fixer = PathFixer::new(ctx); - - let curve_editor = CurveEditorWindow::new(ctx); - let save_scene_dialog = SaveSceneConfirmationDialog::new(ctx); - let build_window = BuildWindow::new(ctx); - if let Some(layout) = settings.windows.layout.as_ref() { engine .user_interfaces @@ -818,7 +829,6 @@ impl Editor { let editor = Self { docking_manager, - animation_editor, engine, navmesh_panel, scene_viewer, @@ -837,9 +847,6 @@ impl Editor { command_stack_viewer, validation_message_box, settings, - path_fixer, - material_editor, - curve_editor, audio_panel, save_scene_dialog, mode: Mode::Edit, @@ -847,7 +854,6 @@ impl Editor { clock: Instant::now(), lag: 0.0, }, - absm_editor, build_window, scene_settings, particle_system_control_panel, @@ -857,8 +863,16 @@ impl Editor { node_removal_dialog, doc_window, plugins: EditorPluginsContainer::new() - .with(ColliderShapePlugin::default()) + .with(ColliderPlugin::default()) .with(TileMapEditorPlugin::default()) + .with(MaterialPlugin::default()) + .with(RagdollPlugin::default()) + .with(SettingsPlugin::default()) + .with(AnimationEditorPlugin::default()) + .with(AbsmEditorPlugin::default()) + .with(UiStatisticsPlugin::default()) + .with(CurveEditorPlugin::default()) + .with(PathFixerPlugin::default()) .with(inspector_plugin), // Apparently, some window managers (like Wayland), does not send `Focused` event after the window // was created. So we must assume that the editor is focused by default, otherwise editor's thread @@ -866,15 +880,14 @@ impl Editor { focused: true, update_loop_state: UpdateLoopState::default(), is_suspended: false, - ragdoll_wizard, scene_node_context_menu, widget_context_menu, - collider_control_panel, overlay_pass: None, highlighter: None, export_window: None, statistics_window: None, surface_data_viewer: None, + processed_ui_messages: 0, }; if let Some(data) = startup_data { @@ -1189,13 +1202,8 @@ impl Editor { navmesh_panel: self.navmesh_panel.window, audio_panel: self.audio_panel.window, configurator_window: self.configurator.window, - path_fixer: self.path_fixer.window, - curve_editor: &self.curve_editor, - absm_editor: &self.absm_editor, command_stack_panel: self.command_stack_viewer.window, scene_settings: &self.scene_settings, - animation_editor: &self.animation_editor, - ragdoll_wizard: &self.ragdoll_wizard, export_window: &mut self.export_window, statistics_window: &mut self.statistics_window, }, @@ -1216,13 +1224,6 @@ impl Editor { self.asset_browser .handle_ui_message(message, engine, self.message_sender.clone()); self.command_stack_viewer.handle_ui_message(message); - self.curve_editor.handle_ui_message(message, engine); - self.path_fixer.handle_ui_message( - message, - engine.user_interfaces.first_mut(), - engine.serialization_context.clone(), - engine.resource_manager.clone(), - ); self.scene_viewer.handle_ui_message( message, engine, @@ -1249,32 +1250,6 @@ impl Editor { if let Some(current_scene_entry) = current_scene_entry { if let Some(game_scene) = current_scene_entry.controller.downcast_mut::() { - let graph = &mut engine.scenes[game_scene.scene].graph; - self.animation_editor.handle_ui_message( - message, - ¤t_scene_entry.selection, - graph, - game_scene.scene_content_root, - engine.user_interfaces.first_mut(), - &engine.resource_manager, - &self.message_sender, - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.handle_ui_message( - message, - &self.message_sender, - ¤t_scene_entry.selection, - graph, - engine.user_interfaces.first_mut(), - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - self.ragdoll_wizard.handle_ui_message( - message, - engine.user_interfaces.first_mut(), - graph, - game_scene, - &self.message_sender, - ); self.particle_system_control_panel.handle_ui_message( message, ¤t_scene_entry.selection, @@ -1294,13 +1269,6 @@ impl Editor { engine, &self.message_sender, ); - self.collider_control_panel.handle_ui_message( - message, - engine, - game_scene, - ¤t_scene_entry.selection, - &self.message_sender, - ); self.audio_preview_panel.handle_ui_message( message, ¤t_scene_entry.selection, @@ -1368,25 +1336,6 @@ impl Editor { .handle_ui_message(message, game_scene, engine); } else if let Some(ui_scene) = current_scene_entry.controller.downcast_mut::() { - let ui_root = ui_scene.ui.root(); - self.animation_editor.handle_ui_message( - message, - ¤t_scene_entry.selection, - &mut ui_scene.ui, - ui_root, - engine.user_interfaces.first_mut(), - &engine.resource_manager, - &self.message_sender, - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.handle_ui_message( - message, - &self.message_sender, - ¤t_scene_entry.selection, - &mut ui_scene.ui, - engine.user_interfaces.first_mut(), - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); self.world_viewer.handle_ui_message( message, &mut UiSceneWorldViewerDataProvider { @@ -1408,9 +1357,6 @@ impl Editor { &self.message_sender, ); } - - self.material_editor - .handle_ui_message(message, engine, &self.message_sender); } if let Some(MessageBoxMessage::Close(result)) = message.data() { @@ -1613,16 +1559,6 @@ impl Editor { ); if let Some(game_scene) = current_scene_entry.controller.downcast_mut::() { - self.animation_editor.sync_to_model( - ¤t_scene_entry.selection, - engine.user_interfaces.first_mut(), - &engine.scenes[game_scene.scene].graph, - ); - self.absm_editor.sync_to_model( - ¤t_scene_entry.selection, - &engine.scenes[game_scene.scene].graph, - engine.user_interfaces.first_mut(), - ); self.scene_settings.sync_to_model(game_scene, engine); let sender = &self.message_sender; self.world_viewer.sync_to_model( @@ -1638,8 +1574,7 @@ impl Editor { engine.user_interfaces.first_mut(), &self.settings, ); - self.material_editor - .sync_to_model(engine.user_interfaces.first_mut()); + self.audio_panel .sync_to_model(¤t_scene_entry.selection, game_scene, engine); self.navmesh_panel.sync_to_model( @@ -1649,16 +1584,6 @@ impl Editor { ); } else if let Some(ui_scene) = current_scene_entry.controller.downcast_mut::() { - self.animation_editor.sync_to_model( - ¤t_scene_entry.selection, - engine.user_interfaces.first_mut(), - &ui_scene.ui, - ); - self.absm_editor.sync_to_model( - ¤t_scene_entry.selection, - &ui_scene.ui, - engine.user_interfaces.first_mut(), - ); self.world_viewer.sync_to_model( &UiSceneWorldViewerDataProvider { ui: &mut ui_scene.ui, @@ -1782,31 +1707,10 @@ impl Editor { .leave_preview_mode(game_scene, engine); self.audio_preview_panel .leave_preview_mode(game_scene, engine); - self.animation_editor.try_leave_preview_mode( - &mut engine.scenes[game_scene.scene].graph, - engine.user_interfaces.first(), - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.try_leave_preview_mode( - &entry.selection, - &mut engine.scenes[game_scene.scene].graph, - engine.user_interfaces.first_mut(), - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - } else if let Some(ui_scene) = entry.controller.downcast_mut::() { - self.animation_editor.try_leave_preview_mode( - &mut ui_scene.ui, - self.engine.user_interfaces.first(), - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.try_leave_preview_mode( - &entry.selection, - &mut ui_scene.ui, - self.engine.user_interfaces.first_mut(), - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); } } + + for_each_plugin!(self.plugins => on_leave_preview_mode(self)); } pub fn is_in_preview_mode(&mut self) -> bool { @@ -1833,8 +1737,6 @@ impl Editor { self.particle_system_control_panel.is_in_preview_mode() || self.camera_control_panel.is_in_preview_mode() || self.audio_preview_panel.is_in_preview_mode() - || self.animation_editor.is_in_preview_mode() - || self.absm_editor.is_in_preview_mode() || self.light_panel.is_in_preview_mode() || self.export_window.is_some() || is_any_plugin_in_preview_mode @@ -2024,8 +1926,6 @@ impl Editor { } self.world_viewer.clear(ui); - self.animation_editor.clear(ui); - self.absm_editor.clear(ui); self.poll_ui_messages(); @@ -2033,6 +1933,9 @@ impl Editor { self.scene_viewer .on_current_scene_changed(self.scenes.current_scene_entry_mut(), &mut self.engine); + + for_each_plugin!(self.plugins => on_scene_changed(self)); + self.sync_to_model(); self.poll_ui_messages(); } @@ -2120,22 +2023,6 @@ impl Editor { )); } - fn open_material_editor(&mut self, material: MaterialResource) { - let engine = &mut self.engine; - - self.material_editor.set_material(Some(material), engine); - - engine - .user_interfaces - .first_mut() - .send_message(WindowMessage::open( - self.material_editor.window, - MessageDirection::ToWidget, - true, - true, - )); - } - fn poll_ui_messages(&mut self) -> usize { let mut processed = 0; @@ -2251,7 +2138,6 @@ impl Editor { self.handle_modes(); self.log.update(&self.settings, &mut self.engine); - self.material_editor.update(&mut self.engine); self.asset_browser .update(&mut self.engine, &self.message_sender); if let Some(export_window) = self.export_window.as_mut() { @@ -2270,20 +2156,9 @@ impl Editor { } self.light_panel.update(game_scene, &mut self.engine); - self.animation_editor.update( - &entry.selection, - self.engine.user_interfaces.first(), - &self.engine.scenes[game_scene.scene].graph, - ); self.audio_preview_panel .update(&entry.selection, game_scene, &self.engine); self.scene_viewer.update(game_scene, &mut self.engine); - } else if let Some(ui_scene) = entry.controller.downcast_ref::() { - self.animation_editor.update( - &entry.selection, - self.engine.user_interfaces.first(), - &ui_scene.ui, - ); } } @@ -2291,11 +2166,13 @@ impl Editor { overlay_pass.borrow_mut().pictogram_size = self.settings.debugging.pictogram_size; } + self.processed_ui_messages = 0; let mut iterations = 1; while iterations > 0 { iterations -= 1; let ui_messages_processed_count = self.poll_ui_messages(); + self.processed_ui_messages += ui_messages_processed_count; let mut needs_sync = false; @@ -2304,8 +2181,6 @@ impl Editor { for_each_plugin!(self.plugins => on_message(&message, self)); editor_messages_processed_count += 1; - self.path_fixer - .handle_message(&message, self.engine.user_interfaces.first()); self.save_scene_dialog .handle_message(&message, &self.message_sender); @@ -2330,47 +2205,13 @@ impl Editor { game_scene, &mut self.engine, ); - self.collider_control_panel.handle_message( - &message, - &self.engine, - game_scene, - &entry.selection, - ); self.audio_preview_panel.handle_message( &message, &entry.selection, game_scene, &mut self.engine, ); - self.animation_editor.handle_message( - &message, - &mut self.engine.scenes[game_scene.scene].graph, - self.engine.user_interfaces.first(), - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.handle_message( - &message, - &entry.selection, - &mut self.engine.scenes[game_scene.scene].graph, - self.engine.user_interfaces.first_mut(), - game_scene.graph_switches.node_overrides.as_mut().unwrap(), - ); - } else if let Some(ui_scene) = entry.controller.downcast_mut::() { - self.animation_editor.handle_message( - &message, - &mut ui_scene.ui, - self.engine.user_interfaces.first(), - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); - self.absm_editor.handle_message( - &message, - &entry.selection, - &mut ui_scene.ui, - self.engine.user_interfaces.first_mut(), - ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), - ); } - needs_sync |= entry .controller @@ -2425,14 +2266,6 @@ impl Editor { self.configure(working_directory); needs_sync = true; } - Message::OpenSettings => { - self.menu.file_menu.settings.open( - self.engine.user_interfaces.first_mut(), - &self.settings, - &self.message_sender, - ); - } - Message::OpenMaterialEditor(material) => self.open_material_editor(material), Message::OpenNodeRemovalDialog => { if let Some(entry) = self.scenes.current_scene_entry_ref() { // TODO @@ -2491,13 +2324,6 @@ impl Editor { } } } - Message::OpenAnimationEditor => { - self.animation_editor - .open(self.engine.user_interfaces.first()); - } - Message::OpenAbsmEditor => { - self.absm_editor.open(self.engine.user_interfaces.first()) - } Message::ShowDocumentation(doc) => { self.doc_window .open(doc, self.engine.user_interfaces.first()); @@ -2558,20 +2384,6 @@ impl Editor { ); } - if let Some(game_scene) = controller.downcast_ref::() { - self.absm_editor.update( - &entry.selection, - &mut self.engine.scenes[game_scene.scene].graph, - self.engine.user_interfaces.first_mut(), - ); - } else if let Some(ui_scene) = controller.downcast_mut::() { - self.absm_editor.update( - &entry.selection, - &mut ui_scene.ui, - self.engine.user_interfaces.first_mut(), - ); - } - if let Some(interaction_mode) = entry .current_interaction_mode .and_then(|current_mode| entry.interaction_modes.get_mut(¤t_mode)) diff --git a/editor/src/light.rs b/editor/src/light.rs index 60ce7053e..d6797d59c 100644 --- a/editor/src/light.rs +++ b/editor/src/light.rs @@ -38,10 +38,8 @@ use crate::fyrox::{ CancellationToken, Lightmap, LightmapGenerationError, LightmapInputData, ProgressIndicator, }, }; -use crate::{ - inspector::editors::make_property_editors_container, message::MessageSender, scene::GameScene, - Engine, MSG_SYNC_FLAG, -}; +use crate::plugins::inspector::editors::make_property_editors_container; +use crate::{message::MessageSender, scene::GameScene, Engine, MSG_SYNC_FLAG}; use std::{ path::PathBuf, sync::mpsc::{Receiver, Sender}, diff --git a/editor/src/log.rs b/editor/src/log.rs index 81b9a0ad9..144bb4ffa 100644 --- a/editor/src/log.rs +++ b/editor/src/log.rs @@ -137,7 +137,7 @@ impl LogPanel { ctx, 24.0, 24.0, - load_image(include_bytes!("../resources/clear.png")), + load_image!("../resources/clear.png"), "Clear the log.", Some(0), ); diff --git a/editor/src/menu/animation.rs b/editor/src/menu/animation.rs deleted file mode 100644 index 55ccdae2b..000000000 --- a/editor/src/menu/animation.rs +++ /dev/null @@ -1,93 +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::pool::Handle, - gui::{menu::MenuItemMessage, message::UiMessage, BuildContext, UiNode}, - scene::{ - animation::{absm::prelude::*, prelude::*}, - base::BaseBuilder, - node::Node, - }, -}; -use crate::menu::create_menu_item; - -pub struct AnimationMenu { - pub menu: Handle, - create_animation_player: Handle, - create_absm: Handle, -} - -impl AnimationMenu { - pub fn new(ctx: &mut BuildContext) -> Self { - let create_animation_player; - let create_absm; - - let menu = create_menu_item( - "Animation", - vec![ - { - create_animation_player = create_menu_item("Animation Player", vec![], ctx); - create_animation_player - }, - { - create_absm = create_menu_item("Animation Blending State Machine", vec![], ctx); - create_absm - }, - ], - ctx, - ); - - Self { - menu, - create_animation_player, - create_absm, - } - } - - pub fn handle_ui_message(&mut self, message: &UiMessage) -> Option { - if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.create_animation_player { - let node = - AnimationPlayerBuilder::new(BaseBuilder::new().with_name("Animation Player")) - .build_node(); - Some(node) - } else if message.destination() == self.create_absm { - let mut machine = Machine::default(); - - let mut layer = MachineLayer::new(); - layer.set_name("Base Layer"); - - machine.add_layer(layer); - - let node = AnimationBlendingStateMachineBuilder::new( - BaseBuilder::new().with_name("Animation Blending State Machine"), - ) - .with_machine(machine) - .build_node(); - Some(node) - } else { - None - } - } else { - None - } - } -} diff --git a/editor/src/menu/create.rs b/editor/src/menu/create.rs index 70189ae09..6de81f994 100644 --- a/editor/src/menu/create.rs +++ b/editor/src/menu/create.rs @@ -20,49 +20,17 @@ use crate::{ command::{Command, CommandGroup}, - create_terrain_layer_material, fyrox::{ - asset::untyped::ResourceKind, - core::{ - algebra::{Matrix4, Vector3}, - math::TriangleDefinition, - pool::Handle, - }, + core::{algebra::Vector3, pool::Handle}, + engine::{Engine, SerializationContext}, + fxhash::FxHashMap, gui::{ menu::MenuItemMessage, message::MessageDirection, message::UiMessage, widget::WidgetMessage, BuildContext, UiNode, UserInterface, }, - material::{Material, MaterialResource}, - resource::texture::PLACEHOLDER, - scene::{ - base::BaseBuilder, - camera::CameraBuilder, - decal::DecalBuilder, - light::{ - directional::DirectionalLightBuilder, point::PointLightBuilder, - spot::SpotLightBuilder, BaseLightBuilder, - }, - mesh::{ - surface::{SurfaceBuilder, SurfaceData, SurfaceResource}, - MeshBuilder, - }, - navmesh::NavigationalMeshBuilder, - node::Node, - particle_system::{ - emitter::{base::BaseEmitterBuilder, sphere::SphereEmitterBuilder}, - ParticleSystemBuilder, - }, - pivot::PivotBuilder, - sound::{listener::ListenerBuilder, SoundBuilder}, - sprite::SpriteBuilder, - terrain::{Layer, TerrainBuilder}, - }, - utils::navmesh::Navmesh, - }, - menu::{ - animation::AnimationMenu, create_menu_item, create_root_menu_item, dim2::Dim2Menu, - physics::PhysicsMenu, physics2d::Physics2dMenu, ui::UiMenu, + scene::node::Node, }, + menu::{create_menu_item, create_root_menu_item, ui::UiMenu}, message::MessageSender, scene::{ commands::graph::{AddNodeCommand, MoveNodeCommand}, @@ -72,7 +40,11 @@ use crate::{ ui_scene::UiScene, Mode, }; -use fyrox::engine::Engine; +use fyrox::core::log::Log; +use fyrox::graph::constructor::{VariantConstructor, VariantResult}; +use fyrox::gui::constructor::WidgetConstructorContainer; +use fyrox::gui::menu::SortingPredicate; +use fyrox::scene::graph::Graph; pub struct CreateEntityRootMenu { pub menu: Handle, @@ -80,10 +52,21 @@ pub struct CreateEntityRootMenu { } impl CreateEntityRootMenu { - pub fn new(ctx: &mut BuildContext) -> Self { - let (sub_menus, root_items) = CreateEntityMenu::new(ctx); + 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", root_items, ctx); + let menu = create_root_menu_item("Create", sub_menus.root_items.clone(), ctx); + + ctx.inner().send_message(MenuItemMessage::sort( + menu, + MessageDirection::ToWidget, + SortingPredicate::sort_by_text(), + )); Self { menu, sub_menus } } @@ -94,11 +77,11 @@ impl CreateEntityRootMenu { sender: &MessageSender, controller: &mut dyn SceneController, selection: &Selection, - engine: &Engine, + engine: &mut Engine, ) { if let Some(node) = self .sub_menus - .handle_ui_message(message, sender, controller, selection) + .handle_ui_message(message, sender, controller, selection, engine) { if let Some(game_scene) = controller.downcast_ref::() { let scene = &engine.scenes[game_scene.scene]; @@ -134,203 +117,59 @@ impl CreateEntityRootMenu { } pub struct CreateEntityMenu { - create_pivot: Handle, - create_cube: Handle, - create_cone: Handle, - create_sphere: Handle, - create_cylinder: Handle, - create_quad: Handle, - create_decal: Handle, - create_point_light: Handle, - create_spot_light: Handle, - create_directional_light: Handle, - create_navmesh: Handle, - create_terrain: Handle, - create_camera: Handle, - create_sprite: Handle, - create_particle_system: Handle, - create_listener: Handle, - create_sound_source: Handle, - physics_menu: PhysicsMenu, - physics2d_menu: Physics2dMenu, - dim2_menu: Dim2Menu, - animation_menu: AnimationMenu, ui_menu: UiMenu, - - mesh_menu: Handle, - sound_menu: Handle, - light_menu: Handle, -} - -fn placeholder_material() -> MaterialResource { - let mut material = Material::standard(); - material.bind("diffuseTexture", PLACEHOLDER.resource()); - MaterialResource::new_ok(ResourceKind::Embedded, material) + pub root_items: Vec>, + constructor_views: FxHashMap, VariantConstructor>, } impl CreateEntityMenu { - pub fn new(ctx: &mut BuildContext) -> (Self, Vec>) { - let create_cube; - let create_cone; - let create_sphere; - let create_cylinder; - let create_quad; - let create_point_light; - let create_spot_light; - let create_directional_light; - let create_camera; - let create_sprite; - let create_decal; - let create_navmesh; - let create_particle_system; - let create_terrain; - let create_pivot; - let create_sound_source; - let create_listener; - let physics_menu = PhysicsMenu::new(ctx); - let physics2d_menu = Physics2dMenu::new(ctx); - let dim2_menu = Dim2Menu::new(ctx); - let animation_menu = AnimationMenu::new(ctx); - let mesh_menu; - let sound_menu; - let light_menu; + 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 ui_menu = UiMenu::new(UiMenu::default_entries(), "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, 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, vec![], ctx); + root_items.push(group); + group + }); + ctx.sender() + .send(MenuItemMessage::add_item( + group, + MessageDirection::ToWidget, + item, + )) + .unwrap() + } + } + } - let items = vec![ - ui_menu.menu, - { - create_pivot = create_menu_item("Pivot", vec![], ctx); - create_pivot - }, - { - mesh_menu = create_menu_item( - "Mesh", - vec![ - { - create_cube = create_menu_item("Cube", vec![], ctx); - create_cube - }, - { - create_sphere = create_menu_item("Sphere", vec![], ctx); - create_sphere - }, - { - create_cylinder = create_menu_item("Cylinder", vec![], ctx); - create_cylinder - }, - { - create_cone = create_menu_item("Cone", vec![], ctx); - create_cone - }, - { - create_quad = create_menu_item("Quad", vec![], ctx); - create_quad - }, - ], - ctx, - ); - mesh_menu - }, - { - sound_menu = create_menu_item( - "Sound", - vec![ - { - create_sound_source = create_menu_item("Source", vec![], ctx); - create_sound_source - }, - { - create_listener = create_menu_item("Listener", vec![], ctx); - create_listener - }, - ], - ctx, - ); - sound_menu - }, - { - light_menu = create_menu_item( - "Light", - vec![ - { - create_directional_light = - create_menu_item("Directional Light", vec![], ctx); - create_directional_light - }, - { - create_spot_light = create_menu_item("Spot Light", vec![], ctx); - create_spot_light - }, - { - create_point_light = create_menu_item("Point Light", vec![], ctx); - create_point_light - }, - ], - ctx, - ); - light_menu - }, - physics_menu.menu, - physics2d_menu.menu, - dim2_menu.menu, - animation_menu.menu, - { - create_camera = create_menu_item("Camera", vec![], ctx); - create_camera - }, - { - create_sprite = create_menu_item("Sprite (3D)", vec![], ctx); - create_sprite - }, - { - create_particle_system = create_menu_item("Particle System", vec![], ctx); - create_particle_system - }, - { - create_terrain = create_menu_item("Terrain", vec![], ctx); - create_terrain - }, - { - create_decal = create_menu_item("Decal", vec![], ctx); - create_decal - }, - { - create_navmesh = create_menu_item("Navmesh", vec![], ctx); - create_navmesh - }, - ]; + for root_item in root_items.iter() { + ctx.inner().send_message(MenuItemMessage::sort( + *root_item, + MessageDirection::ToWidget, + SortingPredicate::sort_by_text(), + )) + } - ( - Self { - create_cube, - create_cone, - create_sphere, - create_cylinder, - create_quad, - create_point_light, - create_spot_light, - create_directional_light, - create_camera, - create_sprite, - create_particle_system, - create_pivot, - create_terrain, - create_sound_source, - create_listener, - create_navmesh, - create_decal, - physics_menu, - physics2d_menu, - dim2_menu, - animation_menu, - ui_menu, - mesh_menu, - light_menu, - sound_menu, - }, - items, - ) + Self { + ui_menu, + constructor_views, + root_items, + } } pub fn on_scene_changed(&self, controller: &dyn SceneController, ui: &UserInterface) { @@ -342,24 +181,13 @@ impl CreateEntityMenu { is_ui_scene, )); - for widget in [ - self.mesh_menu, - self.light_menu, - self.create_camera, - self.create_sprite, - self.create_particle_system, - self.create_pivot, - self.create_terrain, - self.sound_menu, - self.create_navmesh, - self.create_decal, - self.physics_menu.menu, - self.physics2d_menu.menu, - self.dim2_menu.menu, - self.animation_menu.menu, - ] { + for widget in self.root_items.iter() { + if *widget == self.ui_menu.menu { + continue; + } + ui.send_message(WidgetMessage::enabled( - widget, + *widget, MessageDirection::ToWidget, !is_ui_scene, )); @@ -372,168 +200,24 @@ impl CreateEntityMenu { sender: &MessageSender, controller: &mut dyn SceneController, selection: &Selection, + engine: &mut Engine, ) -> Option { if let Some(ui_scene) = controller.downcast_mut::() { self.ui_menu .handle_ui_message(sender, message, ui_scene, selection); + } else if let Some(game_scene) = controller.downcast_mut::() { + let graph = &mut engine.scenes[game_scene.scene].graph; + if let Some(MenuItemMessage::Click) = message.data::() { + if let Some(constructor) = self.constructor_views.get(&message.destination()) { + if let VariantResult::Owned(node) = constructor(graph) { + return Some(node); + } else { + Log::err("Unsupported"); + } + } + } } - self.physics_menu - .handle_ui_message(message) - .or_else(|| self.physics2d_menu.handle_ui_message(message)) - .or_else(|| self.dim2_menu.handle_ui_message(message)) - .or_else(|| self.animation_menu.handle_ui_message(message)) - .or_else(|| { - if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.create_cube { - Some( - MeshBuilder::new(BaseBuilder::new().with_name("Cube")) - .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( - ResourceKind::Embedded, - SurfaceData::make_cube(Matrix4::identity()), - )) - .with_material(placeholder_material()) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_spot_light { - Some( - SpotLightBuilder::new(BaseLightBuilder::new( - BaseBuilder::new().with_name("SpotLight"), - )) - .with_distance(10.0) - .with_hotspot_cone_angle(45.0f32.to_radians()) - .with_falloff_angle_delta(2.0f32.to_radians()) - .build_node(), - ) - } else if message.destination() == self.create_pivot { - Some(PivotBuilder::new(BaseBuilder::new().with_name("Pivot")).build_node()) - } else if message.destination() == self.create_point_light { - Some( - PointLightBuilder::new(BaseLightBuilder::new( - BaseBuilder::new().with_name("PointLight"), - )) - .with_radius(10.0) - .build_node(), - ) - } else if message.destination() == self.create_directional_light { - Some( - DirectionalLightBuilder::new(BaseLightBuilder::new( - BaseBuilder::new().with_name("DirectionalLight"), - )) - .build_node(), - ) - } else if message.destination() == self.create_cone { - Some( - MeshBuilder::new(BaseBuilder::new().with_name("Cone")) - .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( - ResourceKind::Embedded, - SurfaceData::make_cone(16, 0.5, 1.0, &Matrix4::identity()), - )) - .with_material(placeholder_material()) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_cylinder { - Some( - MeshBuilder::new(BaseBuilder::new().with_name("Cylinder")) - .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( - ResourceKind::Embedded, - SurfaceData::make_cylinder( - 16, - 0.5, - 1.0, - true, - &Matrix4::identity(), - ), - )) - .with_material(placeholder_material()) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_sphere { - Some( - MeshBuilder::new(BaseBuilder::new().with_name("Sphere")) - .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( - ResourceKind::Embedded, - SurfaceData::make_sphere(16, 16, 0.5, &Matrix4::identity()), - )) - .with_material(placeholder_material()) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_quad { - Some( - MeshBuilder::new(BaseBuilder::new().with_name("Quad")) - .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( - ResourceKind::Embedded, - SurfaceData::make_quad(&Matrix4::identity()), - )) - .with_material(placeholder_material()) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_camera { - Some( - CameraBuilder::new(BaseBuilder::new().with_name("Camera")).build_node(), - ) - } else if message.destination() == self.create_navmesh { - let navmesh = Navmesh::new( - vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])], - vec![ - Vector3::new(-1.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, -1.0), - Vector3::new(-1.0, 0.0, -1.0), - ], - ); - Some( - NavigationalMeshBuilder::new(BaseBuilder::new().with_name("Navmesh")) - .with_navmesh(navmesh) - .build_node(), - ) - } else if message.destination() == self.create_sprite { - Some( - SpriteBuilder::new(BaseBuilder::new().with_name("Sprite")).build_node(), - ) - } else if message.destination() == self.create_sound_source { - Some(SoundBuilder::new(BaseBuilder::new().with_name("Sound")).build_node()) - } else if message.destination() == self.create_particle_system { - Some( - ParticleSystemBuilder::new( - BaseBuilder::new().with_name("ParticleSystem"), - ) - .with_emitters(vec![SphereEmitterBuilder::new( - BaseEmitterBuilder::new() - .with_max_particles(100) - .resurrect_particles(true), - ) - .with_radius(1.0) - .build()]) - .build_node(), - ) - } else if message.destination() == self.create_terrain { - Some( - TerrainBuilder::new(BaseBuilder::new().with_name("Terrain")) - .with_layers(vec![Layer { - material: create_terrain_layer_material(), - ..Default::default() - }]) - .build_node(), - ) - } else if message.destination() == self.create_decal { - Some(DecalBuilder::new(BaseBuilder::new().with_name("Decal")).build_node()) - } else if message.destination() == self.create_listener { - Some( - ListenerBuilder::new(BaseBuilder::new().with_name("Listener")) - .build_node(), - ) - } else { - None - } - } else { - None - } - }) + None } } diff --git a/editor/src/menu/dim2.rs b/editor/src/menu/dim2.rs deleted file mode 100644 index b1dd8cedd..000000000 --- a/editor/src/menu/dim2.rs +++ /dev/null @@ -1,83 +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::pool::Handle, - gui::{menu::MenuItemMessage, message::UiMessage, BuildContext, UiNode}, - scene::{ - base::BaseBuilder, dim2::rectangle::RectangleBuilder, node::Node, - tilemap::TileMapBuilder, - }, - }, - menu::create_menu_item, -}; - -pub struct Dim2Menu { - pub menu: Handle, - create_sprite: Handle, - create_tile_map: Handle, -} - -impl Dim2Menu { - pub fn new(ctx: &mut BuildContext) -> Self { - let create_sprite; - let create_tile_map; - - let menu = create_menu_item( - "2D", - vec![ - { - create_sprite = create_menu_item("Rectangle (2D Sprite)", vec![], ctx); - create_sprite - }, - { - create_tile_map = create_menu_item("Tile Map", vec![], ctx); - create_tile_map - }, - ], - ctx, - ); - - Self { - menu, - create_sprite, - create_tile_map, - } - } - - pub fn handle_ui_message(&mut self, message: &UiMessage) -> Option { - if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.create_sprite { - let node = - RectangleBuilder::new(BaseBuilder::new().with_name("Sprite (2D)")).build_node(); - Some(node) - } else if message.destination() == self.create_tile_map { - let node = - TileMapBuilder::new(BaseBuilder::new().with_name("Tile Map")).build_node(); - Some(node) - } else { - None - } - } else { - None - } - } -} diff --git a/editor/src/menu/edit.rs b/editor/src/menu/edit.rs index 481862e11..8ec6f3003 100644 --- a/editor/src/menu/edit.rs +++ b/editor/src/menu/edit.rs @@ -37,10 +37,10 @@ use crate::{ pub struct EditMenu { pub menu: Handle, - undo: Handle, - redo: Handle, - copy: Handle, - paste: Handle, + pub undo: Handle, + pub redo: Handle, + pub copy: Handle, + pub paste: Handle, } impl EditMenu { diff --git a/editor/src/menu/file.rs b/editor/src/menu/file.rs index 5ca082142..5eadd0032 100644 --- a/editor/src/menu/file.rs +++ b/editor/src/menu/file.rs @@ -36,27 +36,25 @@ use crate::{ menu::{create_menu_item, create_menu_item_shortcut, create_root_menu_item}, message::MessageSender, scene::container::EditorSceneEntry, - settings::{recent::RecentFiles, Settings, SettingsWindow}, + settings::{recent::RecentFiles, Settings}, Engine, Message, Mode, Panels, SaveSceneConfirmationDialogAction, }; use std::path::PathBuf; pub struct FileMenu { pub menu: Handle, - new_scene: Handle, - new_ui_scene: Handle, + pub new_scene: Handle, + pub new_ui_scene: Handle, pub save: Handle, pub save_as: Handle, pub save_all: Handle, - load: Handle, + pub load: Handle, pub close_scene: Handle, - exit: Handle, - pub open_settings: Handle, - configure: Handle, + pub exit: Handle, + pub configure: Handle, pub save_file_selector: Handle, pub load_file_selector: Handle, - configure_message: Handle, - pub settings: SettingsWindow, + pub configure_message: Handle, pub recent_files_container: Handle, pub recent_files: Vec>, pub open_scene_settings: Handle, @@ -83,7 +81,6 @@ impl FileMenu { let save_all; let close_scene; let load; - let open_settings; let open_scene_settings; let configure; let exit; @@ -135,10 +132,6 @@ impl FileMenu { close_scene = create_menu_item_shortcut("Close Scene", "Ctrl+Q", vec![], ctx); close_scene }, - { - open_settings = create_menu_item("Editor Settings...", vec![], ctx); - open_settings - }, { open_scene_settings = create_menu_item("Scene Settings...", vec![], ctx); open_scene_settings @@ -183,10 +176,8 @@ impl FileMenu { close_scene, load, exit, - open_settings, configure, configure_message, - settings: SettingsWindow::new(engine), recent_files_container, recent_files, open_scene_settings, @@ -243,9 +234,6 @@ impl FileMenu { settings: &mut Settings, panels: &mut Panels, ) { - self.settings - .handle_message(message, engine, settings, sender); - if let Some(FileSelectorMessage::Commit(path)) = message.data::() { if message.destination() == self.save_file_selector { if let Some(game_scene) = entry { @@ -254,13 +242,6 @@ impl FileMenu { path: path.to_owned(), }); } - engine - .user_interfaces - .first_mut() - .send_message(WidgetMessage::remove( - self.save_file_selector, - MessageDirection::ToWidget, - )); self.save_file_selector = Handle::NONE; } else if message.destination() == self.load_file_selector { sender.send(Message::LoadScene(path.to_owned())); @@ -336,9 +317,6 @@ impl FileMenu { ExportWindow::new(&mut engine.user_interfaces.first_mut().build_ctx()); export_window.open(engine.user_interfaces.first()); *panels.export_window = Some(export_window); - } else if message.destination() == self.open_settings { - self.settings - .open(engine.user_interfaces.first_mut(), settings, sender); } else if message.destination() == self.open_scene_settings { panels.scene_settings.open(engine.user_interfaces.first()); } else if let Some(recent_file) = self diff --git a/editor/src/menu/help.rs b/editor/src/menu/help.rs index ffcbbf778..e9a15ab22 100644 --- a/editor/src/menu/help.rs +++ b/editor/src/menu/help.rs @@ -26,8 +26,8 @@ use crate::menu::{create_menu_item, create_root_menu_item}; pub struct HelpMenu { pub menu: Handle, - open_book: Handle, - open_api_reference: Handle, + pub open_book: Handle, + pub open_api_reference: Handle, } impl HelpMenu { diff --git a/editor/src/menu/mod.rs b/editor/src/menu/mod.rs index a86539d8f..ca6e43f8b 100644 --- a/editor/src/menu/mod.rs +++ b/editor/src/menu/mod.rs @@ -19,7 +19,6 @@ // SOFTWARE. use crate::{ - animation::AnimationEditor, export::ExportWindow, fyrox::{ core::{algebra::Vector2, pool::Handle}, @@ -39,32 +38,27 @@ use crate::{ send_sync_message, settings::Settings, stats::StatisticsWindow, - utils::ragdoll::RagdollWizard, - AbsmEditor, CurveEditorWindow, Engine, Mode, SceneSettingsWindow, + Engine, Mode, SceneSettingsWindow, }; use std::path::PathBuf; -pub mod animation; pub mod create; -pub mod dim2; pub mod edit; pub mod file; pub mod help; -pub mod physics; -pub mod physics2d; pub mod ui; pub mod utils; pub mod view; pub struct Menu { pub menu: Handle, - create_entity_menu: CreateEntityRootMenu, - edit_menu: EditMenu, + pub create_entity_menu: CreateEntityRootMenu, + pub edit_menu: EditMenu, pub file_menu: FileMenu, - view_menu: ViewMenu, - message_sender: MessageSender, - utils_menu: UtilsMenu, - help_menu: HelpMenu, + pub view_menu: ViewMenu, + pub message_sender: MessageSender, + pub utils_menu: UtilsMenu, + pub help_menu: HelpMenu, } pub struct Panels<'b> { @@ -78,12 +72,7 @@ pub struct Panels<'b> { pub world_outliner_window: Handle, pub asset_window: Handle, pub configurator_window: Handle, - pub path_fixer: Handle, - pub curve_editor: &'b CurveEditorWindow, - pub absm_editor: &'b AbsmEditor, pub scene_settings: &'b SceneSettingsWindow, - pub animation_editor: &'b AnimationEditor, - pub ragdoll_wizard: &'b RagdollWizard, pub export_window: &'b mut Option, pub statistics_window: &'b mut Option, } @@ -133,7 +122,11 @@ 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(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); diff --git a/editor/src/menu/physics.rs b/editor/src/menu/physics.rs deleted file mode 100644 index 9af5cfd44..000000000 --- a/editor/src/menu/physics.rs +++ /dev/null @@ -1,141 +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::pool::Handle, - gui::{menu::MenuItemMessage, message::UiMessage, BuildContext, UiNode}, - scene::{ - base::BaseBuilder, collider::*, joint::*, node::Node, ragdoll::RagdollBuilder, - rigidbody::RigidBodyBuilder, - }, -}; -use crate::menu::create_menu_item; - -pub struct PhysicsMenu { - pub menu: Handle, - create_rigid_body: Handle, - create_revolute_joint: Handle, - create_ball_joint: Handle, - create_prismatic_joint: Handle, - create_fixed_joint: Handle, - create_collider: Handle, - create_ragdoll: Handle, -} - -impl PhysicsMenu { - pub fn new(ctx: &mut BuildContext) -> Self { - let create_rigid_body; - let create_collider; - let create_revolute_joint; - let create_ball_joint; - let create_prismatic_joint; - let create_fixed_joint; - let create_ragdoll; - let menu = create_menu_item( - "Physics", - vec![ - { - create_rigid_body = create_menu_item("Rigid Body", vec![], ctx); - create_rigid_body - }, - { - create_collider = create_menu_item("Collider", vec![], ctx); - create_collider - }, - { - create_revolute_joint = create_menu_item("Revolute Joint", vec![], ctx); - create_revolute_joint - }, - { - create_ball_joint = create_menu_item("Ball Joint", vec![], ctx); - create_ball_joint - }, - { - create_prismatic_joint = create_menu_item("Prismatic Joint", vec![], ctx); - create_prismatic_joint - }, - { - create_fixed_joint = create_menu_item("Fixed Joint", vec![], ctx); - create_fixed_joint - }, - { - create_ragdoll = create_menu_item("Ragdoll", vec![], ctx); - create_ragdoll - }, - ], - ctx, - ); - - Self { - menu, - create_rigid_body, - create_revolute_joint, - create_ball_joint, - create_prismatic_joint, - create_fixed_joint, - create_collider, - create_ragdoll, - } - } - - pub fn handle_ui_message(&mut self, message: &UiMessage) -> Option { - if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.create_rigid_body { - Some(RigidBodyBuilder::new(BaseBuilder::new().with_name("Rigid Body")).build_node()) - } else if message.destination() == self.create_revolute_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Revolute Joint")) - .with_params(JointParams::RevoluteJoint(Default::default())) - .build_node(), - ) - } else if message.destination() == self.create_ball_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Ball Joint")) - .with_params(JointParams::BallJoint(Default::default())) - .build_node(), - ) - } else if message.destination() == self.create_prismatic_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint")) - .with_params(JointParams::PrismaticJoint(Default::default())) - .build_node(), - ) - } else if message.destination() == self.create_fixed_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint")) - .with_params(JointParams::FixedJoint(Default::default())) - .build_node(), - ) - } else if message.destination == self.create_collider { - Some( - ColliderBuilder::new(BaseBuilder::new().with_name("Collider")) - .with_shape(ColliderShape::Cuboid(Default::default())) - .build_node(), - ) - } else if message.destination == self.create_ragdoll { - Some(RagdollBuilder::new(BaseBuilder::new().with_name("Ragdoll")).build_node()) - } else { - None - } - } else { - None - } - } -} diff --git a/editor/src/menu/physics2d.rs b/editor/src/menu/physics2d.rs deleted file mode 100644 index f7fde19b1..000000000 --- a/editor/src/menu/physics2d.rs +++ /dev/null @@ -1,123 +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::pool::Handle, - gui::{menu::MenuItemMessage, message::UiMessage, BuildContext, UiNode}, - scene::{ - base::BaseBuilder, - dim2::{collider::*, joint::*, rigidbody::RigidBodyBuilder}, - node::Node, - }, -}; -use crate::menu::create_menu_item; - -pub struct Physics2dMenu { - pub menu: Handle, - create_rigid_body: Handle, - create_ball_joint: Handle, - create_prismatic_joint: Handle, - create_fixed_joint: Handle, - create_collider: Handle, -} - -impl Physics2dMenu { - pub fn new(ctx: &mut BuildContext) -> Self { - let create_rigid_body; - let create_collider; - let create_ball_joint; - let create_prismatic_joint; - let create_fixed_joint; - let menu = create_menu_item( - "Physics 2D", - vec![ - { - create_rigid_body = create_menu_item("Rigid Body", vec![], ctx); - create_rigid_body - }, - { - create_collider = create_menu_item("Collider", vec![], ctx); - create_collider - }, - { - create_ball_joint = create_menu_item("Ball Joint", vec![], ctx); - create_ball_joint - }, - { - create_prismatic_joint = create_menu_item("Prismatic Joint", vec![], ctx); - create_prismatic_joint - }, - { - create_fixed_joint = create_menu_item("Fixed Joint", vec![], ctx); - create_fixed_joint - }, - ], - ctx, - ); - - Self { - menu, - create_rigid_body, - create_ball_joint, - create_prismatic_joint, - create_fixed_joint, - create_collider, - } - } - - pub fn handle_ui_message(&mut self, message: &UiMessage) -> Option { - if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.create_rigid_body { - Some( - RigidBodyBuilder::new(BaseBuilder::new().with_name("Rigid Body 2D")) - .build_node(), - ) - } else if message.destination() == self.create_ball_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Ball Joint 2D")) - .with_params(JointParams::BallJoint(Default::default())) - .build_node(), - ) - } else if message.destination() == self.create_prismatic_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint 2D")) - .with_params(JointParams::PrismaticJoint(Default::default())) - .build_node(), - ) - } else if message.destination() == self.create_fixed_joint { - Some( - JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint 2D")) - .with_params(JointParams::FixedJoint(Default::default())) - .build_node(), - ) - } else if message.destination == self.create_collider { - Some( - ColliderBuilder::new(BaseBuilder::new().with_name("Collider 2D")) - .with_shape(ColliderShape::Cuboid(Default::default())) - .build_node(), - ) - } else { - None - } - } else { - None - } - } -} diff --git a/editor/src/menu/ui.rs b/editor/src/menu/ui.rs index 71c3490d1..1f55f2832 100644 --- a/editor/src/menu/ui.rs +++ b/editor/src/menu/ui.rs @@ -18,231 +18,73 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::gui::selector::SelectorBuilder; -use crate::fyrox::{ - core::pool::Handle, - fxhash::FxHashMap, - gui::{ - absm::{AbsmEventProviderBuilder, AnimationBlendingStateMachineBuilder}, - animation::AnimationPlayerBuilder, - border::BorderBuilder, - button::ButtonBuilder, - canvas::CanvasBuilder, - check_box::CheckBoxBuilder, - decorator::DecoratorBuilder, - dropdown_list::DropdownListBuilder, - expander::ExpanderBuilder, - grid::GridBuilder, - image::ImageBuilder, - list_view::ListViewBuilder, - menu::MenuItemMessage, - menu::{MenuBuilder, MenuItemBuilder}, - message::UiMessage, - messagebox::MessageBoxBuilder, - nine_patch::NinePatchBuilder, - numeric::NumericUpDownBuilder, - path::PathEditorBuilder, - popup::PopupBuilder, - progress_bar::ProgressBarBuilder, - screen::ScreenBuilder, - scroll_bar::ScrollBarBuilder, - scroll_viewer::ScrollViewerBuilder, - searchbar::SearchBarBuilder, - stack_panel::StackPanelBuilder, - tab_control::TabControlBuilder, - text::TextBuilder, - text_box::TextBoxBuilder, - tree::{TreeBuilder, TreeRootBuilder}, - uuid::UuidEditorBuilder, - vector_image::VectorImageBuilder, - widget::WidgetBuilder, - window::WindowBuilder, - wrap_panel::WrapPanelBuilder, - BuildContext, UiNode, - }, -}; use crate::{ + fyrox::{ + core::{log::Log, pool::Handle}, + fxhash::FxHashMap, + graph::constructor::{VariantConstructor, VariantResult}, + gui::{ + constructor::WidgetConstructorContainer, menu::MenuItemMessage, + message::MessageDirection, message::UiMessage, BuildContext, UiNode, UserInterface, + }, + }, menu::create_menu_item, message::MessageSender, scene::Selection, ui_scene::{commands::graph::AddWidgetCommand, UiScene}, }; +use fyrox::gui::menu::SortingPredicate; pub struct UiMenu { pub menu: Handle, - constructors: FxHashMap, UiMenuEntry>, -} - -#[allow(clippy::type_complexity)] -pub struct UiMenuEntry { - pub name: String, - pub constructor: Box Handle>, -} - -impl UiMenuEntry { - pub fn new(name: P, constructor: F) -> Self - where - P: AsRef, - F: FnMut(&str, &mut BuildContext) -> Handle + 'static, - { - Self { - name: name.as_ref().to_owned(), - constructor: Box::new(constructor), - } - } + constructor_views: FxHashMap, VariantConstructor>, } impl UiMenu { - pub fn default_entries() -> Vec { - vec![ - UiMenuEntry::new("Screen", |name, ctx| { - ScreenBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Button", |name, ctx| { - ButtonBuilder::new( - WidgetBuilder::new() - .with_width(100.0) - .with_height(20.0) - .with_name(name), - ) - .build(ctx) - }), - UiMenuEntry::new("Border", |name, ctx| { - BorderBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Image", |name, ctx| { - ImageBuilder::new( - WidgetBuilder::new() - .with_height(32.0) - .with_width(32.0) - .with_name(name), - ) - .build(ctx) - }), - UiMenuEntry::new("Canvas", |name, ctx| { - CanvasBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Grid", |name, ctx| { - GridBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("CheckBox", |name, ctx| { - CheckBoxBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Decorator", |name, ctx| { - DecoratorBuilder::new(BorderBuilder::new(WidgetBuilder::new().with_name(name))) - .build(ctx) - }), - UiMenuEntry::new("DropdownList", |name, ctx| { - DropdownListBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Expander", |name, ctx| { - ExpanderBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("ListView", |name, ctx| { - ListViewBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Menu", |name, ctx| { - MenuBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("MenuItem", |name, ctx| { - MenuItemBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("MessageBox", |name, ctx| { - MessageBoxBuilder::new(WindowBuilder::new(WidgetBuilder::new().with_name(name))) - .build(ctx) - }), - UiMenuEntry::new("NinePatch", |name, ctx| { - NinePatchBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("NumericUpDownF32", |name, ctx| { - NumericUpDownBuilder::::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("NumericUpDownI32", |name, ctx| { - NumericUpDownBuilder::::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("PathEditor", |name, ctx| { - PathEditorBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Popup", |name, ctx| { - PopupBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("ProgressBar", |name, ctx| { - ProgressBarBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("ScrollBar", |name, ctx| { - ScrollBarBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("ScrollViewer", |name, ctx| { - ScrollViewerBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("SearchBar", |name, ctx| { - SearchBarBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("StackPanel", |name, ctx| { - StackPanelBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("TabControl", |name, ctx| { - TabControlBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Text", |name, ctx| { - TextBuilder::new(WidgetBuilder::new().with_name(name)) - .with_text("Text") - .build(ctx) - }), - UiMenuEntry::new("TextBox", |name, ctx| { - TextBoxBuilder::new(WidgetBuilder::new().with_name(name)) - .with_text("Text") - .build(ctx) - }), - UiMenuEntry::new("Tree", |name, ctx| { - TreeBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("TreeRoot", |name, ctx| { - TreeRootBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("UuidEditor", |name, ctx| { - UuidEditorBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("VectorImage", |name, ctx| { - VectorImageBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Window", |name, ctx| { - WindowBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("WrapPanel", |name, ctx| { - WrapPanelBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("AnimationPlayer", |name, ctx| { - AnimationPlayerBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("AnimationBlendingStateMachine", |name, ctx| { - AnimationBlendingStateMachineBuilder::new(WidgetBuilder::new().with_name(name)) - .build(ctx) - }), - UiMenuEntry::new("AbsmEventProvider", |name, ctx| { - AbsmEventProviderBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - UiMenuEntry::new("Selector", |name, ctx| { - SelectorBuilder::new(WidgetBuilder::new().with_name(name)).build(ctx) - }), - ] - } + 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, 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, vec![], ctx); + root_items.push(group); + group + }); + ctx.inner().send_message(MenuItemMessage::add_item( + group, + MessageDirection::ToWidget, + item, + )); + } + } + } - pub fn new(entries: Vec, name: &str, ctx: &mut BuildContext) -> Self { - let items = entries - .iter() - .map(|e| create_menu_item(&e.name, Default::default(), ctx)) - .collect::>(); + let menu = create_menu_item(name, root_items.clone(), ctx); - let constructors = entries - .into_iter() - .zip(items.iter().cloned()) - .map(|(entry, node)| (node, entry)) - .collect::>(); + for root_item in root_items.iter().chain(&[menu]) { + ctx.inner().send_message(MenuItemMessage::sort( + *root_item, + MessageDirection::ToWidget, + SortingPredicate::sort_by_text(), + )) + } - let menu = create_menu_item(name, items, ctx); - - Self { menu, constructors } + Self { + menu, + constructor_views, + } } pub fn handle_ui_message( @@ -253,15 +95,18 @@ impl UiMenu { selection: &Selection, ) { if let Some(MenuItemMessage::Click) = message.data::() { - if let Some(entry) = self.constructors.get_mut(&message.destination()) { - let ui_node_handle = (entry.constructor)(&entry.name, &mut scene.ui.build_ctx()); - 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() + if let Some(constructor) = self.constructor_views.get_mut(&message.destination()) { + 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 { - Handle::NONE - }; - sender.do_command(AddWidgetCommand::new(sub_graph, parent, true)); + Log::err("Unsupported"); + } } } } diff --git a/editor/src/menu/utils.rs b/editor/src/menu/utils.rs index e392e5158..f8f931a08 100644 --- a/editor/src/menu/utils.rs +++ b/editor/src/menu/utils.rs @@ -18,74 +18,34 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::{ - asset::core::pool::Handle, - gui::{ - menu::MenuItemMessage, - message::{MessageDirection, UiMessage}, - window::WindowMessage, - BuildContext, UiNode, UserInterface, +use crate::{ + fyrox::{ + asset::core::pool::Handle, + gui::{menu::MenuItemMessage, message::UiMessage, BuildContext, UiNode, UserInterface}, }, + menu::{create_menu_item, create_root_menu_item, Panels}, + stats::StatisticsWindow, }; -use crate::menu::{create_menu_item, create_root_menu_item, Panels}; -use crate::stats::StatisticsWindow; pub struct UtilsMenu { pub menu: Handle, - open_path_fixer: Handle, - open_curve_editor: Handle, - absm_editor: Handle, - animation_editor: Handle, - ragdoll_wizard: Handle, - rendering_statistics: Handle, + pub rendering_statistics: Handle, } impl UtilsMenu { pub fn new(ctx: &mut BuildContext) -> Self { - let open_path_fixer; - let open_curve_editor; - let absm_editor; - let animation_editor; - let ragdoll_wizard; let rendering_statistics; let menu = create_root_menu_item( "Utils", - vec![ - { - open_path_fixer = create_menu_item("Path Fixer", vec![], ctx); - open_path_fixer - }, - { - open_curve_editor = create_menu_item("Curve Editor", vec![], ctx); - open_curve_editor - }, - { - absm_editor = create_menu_item("ABSM Editor", vec![], ctx); - absm_editor - }, - { - animation_editor = create_menu_item("Animation Editor", vec![], ctx); - animation_editor - }, - { - ragdoll_wizard = create_menu_item("Ragdoll Wizard", vec![], ctx); - ragdoll_wizard - }, - { - rendering_statistics = create_menu_item("Rendering Statistics", vec![], ctx); - rendering_statistics - }, - ], + vec![{ + rendering_statistics = create_menu_item("Rendering Statistics", vec![], ctx); + rendering_statistics + }], ctx, ); Self { menu, - open_path_fixer, - open_curve_editor, - absm_editor, - animation_editor, - ragdoll_wizard, rendering_statistics, } } @@ -97,22 +57,7 @@ impl UtilsMenu { ui: &mut UserInterface, ) { if let Some(MenuItemMessage::Click) = message.data::() { - if message.destination() == self.open_path_fixer { - ui.send_message(WindowMessage::open_modal( - panels.path_fixer, - MessageDirection::ToWidget, - true, - true, - )); - } else if message.destination() == self.open_curve_editor { - panels.curve_editor.open(ui); - } else if message.destination() == self.absm_editor { - panels.absm_editor.open(ui); - } else if message.destination() == self.animation_editor { - panels.animation_editor.open(ui); - } else if message.destination() == self.ragdoll_wizard { - panels.ragdoll_wizard.open(ui); - } else if message.destination() == self.rendering_statistics { + if message.destination() == self.rendering_statistics { *panels.statistics_window = Some(StatisticsWindow::new( &mut ui.build_ctx(), panels.scene_frame, diff --git a/editor/src/menu/view.rs b/editor/src/menu/view.rs index aae70f4b1..bc572a22f 100644 --- a/editor/src/menu/view.rs +++ b/editor/src/menu/view.rs @@ -36,16 +36,16 @@ use crate::{ pub struct ViewMenu { pub menu: Handle, - inspector: Handle, - world_viewer: Handle, - asset_browser: Handle, - light_panel: Handle, - log_panel: Handle, - nav_mesh: Handle, - audio: Handle, - command_stack: Handle, - save_layout: Handle, - load_layout: Handle, + pub inspector: Handle, + pub world_viewer: Handle, + pub asset_browser: Handle, + pub light_panel: Handle, + pub log_panel: Handle, + pub nav_mesh: Handle, + pub audio: Handle, + pub command_stack: Handle, + pub save_layout: Handle, + pub load_layout: Handle, } fn switch_window_state(window: Handle, ui: &UserInterface, center: bool) { diff --git a/editor/src/message.rs b/editor/src/message.rs index c35ab6fec..1e45d1306 100644 --- a/editor/src/message.rs +++ b/editor/src/message.rs @@ -34,6 +34,7 @@ use crate::{ SaveSceneConfirmationDialogAction, }; use fyrox::scene::tilemap::tileset::TileSetResource; +use std::sync::mpsc::channel; use std::{path::PathBuf, sync::mpsc::Sender}; #[derive(Debug)] @@ -61,7 +62,6 @@ pub enum Message { Exit { force: bool, }, - OpenSettings, OpenAnimationEditor, OpenAbsmEditor, OpenMaterialEditor(MaterialResource), @@ -108,6 +108,13 @@ pub enum Message { #[derive(Clone, Debug)] pub struct MessageSender(pub Sender); +impl Default for MessageSender { + fn default() -> Self { + let (rx, _) = channel(); + Self(rx) + } +} + unsafe impl Send for MessageSender {} unsafe impl Sync for MessageSender {} diff --git a/editor/src/plugin.rs b/editor/src/plugin.rs index 6980b2592..696be3ff5 100644 --- a/editor/src/plugin.rs +++ b/editor/src/plugin.rs @@ -72,6 +72,11 @@ pub trait EditorPlugin: BaseEditorPlugin { /// [`crate::Mode::Edit`]. fn on_mode_changed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {} + /// This method is called when active scene was changed. It could happen if a user opens or loads + /// a new scene, closes existing scene so the active scene changes to previous in the list of + /// scenes (if any). + fn on_scene_changed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {} + /// This method is called when a UI message was extracted from the message queue. It should be used to react to user /// changes, for example a user could click a button, then a [`fyrox::gui::button::ButtonMessage::Click`] will be /// passed to this method. It then can be used to perform some other action. @@ -97,6 +102,12 @@ pub trait EditorPlugin: BaseEditorPlugin { /// about suspension. fn on_resumed(&mut self, #[allow(unused_variables)] editor: &mut Editor) {} + /// This method is called when the editor leaves preview mode. Usually this method is used to + /// rollback scene changes to the state in which scene objects were before entering the preview + /// mode. This method is typically called by the editor before execution of any command and before + /// saving (to prevent "leakage" of preview mode changes into the saved scene). + fn on_leave_preview_mode(&mut self, #[allow(unused_variables)] editor: &mut Editor) {} + /// This method is used to tell the editor, whether your plugin is in preview mode or not. Preview mode is a special /// state of the editor, when it modifies a content of some scene every frame and discards these changes when the /// preview mode is disabled. diff --git a/editor/src/absm/blendspace.rs b/editor/src/plugins/absm/blendspace.rs similarity index 96% rename from editor/src/absm/blendspace.rs rename to editor/src/plugins/absm/blendspace.rs index 3fdb7e8d0..0be029a31 100644 --- a/editor/src/absm/blendspace.rs +++ b/editor/src/plugins/absm/blendspace.rs @@ -47,22 +47,19 @@ use crate::fyrox::{ widget::{Widget, WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, Control, HorizontalAlignment, RcUiNodeHandle, Thickness, UiNode, - UserInterface, VerticalAlignment, BRUSH_DARK, BRUSH_LIGHT, BRUSH_LIGHTEST, + UserInterface, VerticalAlignment, }, }; -use crate::{ - absm::{ - command::blend::{ - AddBlendSpacePointCommand, RemoveBlendSpacePointCommand, - SetBlendSpacePointPositionCommand, - }, - selection::{AbsmSelection, SelectedEntity}, +use crate::plugins::absm::{ + command::blend::{ + AddBlendSpacePointCommand, RemoveBlendSpacePointCommand, SetBlendSpacePointPositionCommand, }, - menu::create_menu_item, - message::MessageSender, - send_sync_message, + selection::{AbsmSelection, SelectedEntity}, }; +use crate::{menu::create_menu_item, message::MessageSender, send_sync_message}; use fyrox::gui::menu::ContextMenuBuilder; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ cell::Cell, fmt::{Debug, Formatter}, @@ -175,7 +172,7 @@ fn make_points>>( BlendSpaceFieldPointBuilder::new( WidgetBuilder::new() .with_context_menu(context_menu.clone()) - .with_background(BRUSH_LIGHTEST) + .with_background(ctx.style.get_or_default(Style::BRUSH_LIGHTEST)) .with_foreground(Brush::Solid(Color::WHITE)) .with_desired_position(p), i, @@ -532,14 +529,14 @@ impl BlendSpaceFieldBuilder { .with_clip_to_bounds(false) .with_preview_messages(true) .with_context_menu(menu.clone()) - .build(), + .build(ctx), points: Default::default(), min_values: self.min_values, max_values: self.max_values, snap_step: self.snap_step, point_positions: Default::default(), triangles: Default::default(), - grid_brush: BRUSH_LIGHT, + grid_brush: ctx.style.get_or_default(Style::BRUSH_LIGHT), sampling_point: Vector2::new(0.25, 0.5), drag_context: None, field_context_menu: ContextMenu { @@ -635,7 +632,7 @@ impl BlendSpaceFieldPointBuilder { ) .with_width(10.0) .with_height(10.0) - .build(), + .build(ctx), selected: false, }; @@ -661,8 +658,8 @@ impl BlendSpaceEditor { .on_row(0) .on_column(1) .with_margin(Thickness::uniform(15.0)) - .with_foreground(BRUSH_LIGHTEST) - .with_background(BRUSH_DARK), + .with_foreground(ctx.style.get_or_default(Style::BRUSH_LIGHTEST)) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARK)), ) .build(ctx); @@ -970,3 +967,17 @@ impl BlendSpaceEditor { } } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::blendspace::{BlendSpaceFieldBuilder, BlendSpaceFieldPointBuilder}; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + BlendSpaceFieldPointBuilder::new(WidgetBuilder::new(), 0).build(ctx) + }); + test_widget_deletion(|ctx| BlendSpaceFieldBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/absm/canvas.rs b/editor/src/plugins/absm/canvas.rs similarity index 97% rename from editor/src/absm/canvas.rs rename to editor/src/plugins/absm/canvas.rs index e9d6fd93a..07c55fd8d 100644 --- a/editor/src/absm/canvas.rs +++ b/editor/src/plugins/absm/canvas.rs @@ -39,36 +39,34 @@ use crate::fyrox::{ BuildContext, Control, UiNode, UserInterface, }, }; -use crate::{ - absm::{ - connection::{self, Connection}, - node::AbsmBaseNode, - segment::SegmentMessage, - selectable::{Selectable, SelectableMessage}, - socket::{Socket, SocketDirection, SocketMessage}, - transition::{self, TransitionView}, - }, - utils::fetch_node_screen_center_ui, +use crate::plugins::absm::{ + connection::{self, Connection}, + node::AbsmBaseNode, + segment::SegmentMessage, + selectable::{Selectable, SelectableMessage}, + socket::{Socket, SocketDirection, SocketMessage}, + transition::{self, TransitionView}, }; +use crate::utils::fetch_node_screen_center_ui; use std::{ cell::Cell, ops::{Deref, DerefMut}, }; #[derive(Debug, Clone, PartialEq, Visit, Reflect, Default)] -pub(super) struct Entry { +pub(crate) struct Entry { pub node: Handle, pub initial_position: Vector2, } #[derive(Debug, Clone, PartialEq, Visit, Reflect, Default)] -pub(super) struct DragContext { +pub(crate) struct DragContext { initial_cursor_position: Vector2, entries: Vec, } #[derive(Debug, Clone, PartialEq, Visit, Reflect)] -pub(super) enum Mode { +pub(crate) enum Mode { Normal, Drag { drag_context: DragContext, @@ -86,7 +84,7 @@ pub(super) enum Mode { } #[derive(Debug, Clone, PartialEq)] -pub(super) enum AbsmCanvasMessage { +pub(crate) enum AbsmCanvasMessage { SwitchMode(Mode), CommitTransition { source_node: Handle, @@ -641,7 +639,7 @@ impl AbsmCanvasBuilder { .widget_builder .with_preview_messages(true) .with_clip_to_bounds(false) - .build(), + .build(ctx), selection: Default::default(), view_position: Default::default(), initial_view_position: Default::default(), @@ -655,3 +653,14 @@ impl AbsmCanvasBuilder { ctx.add_node(UiNode::new(canvas)) } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::canvas::AbsmCanvasBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| AbsmCanvasBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/absm/command/blend.rs b/editor/src/plugins/absm/command/blend.rs similarity index 97% rename from editor/src/absm/command/blend.rs rename to editor/src/plugins/absm/command/blend.rs index 8eacdf247..0a9931765 100644 --- a/editor/src/absm/command/blend.rs +++ b/editor/src/plugins/absm/command/blend.rs @@ -25,8 +25,9 @@ use crate::fyrox::{ node::blendspace::BlendSpacePoint, BlendPose, IndexedBlendInput, PoseNode, }, }; +use crate::plugins::absm::command::fetch_machine; use crate::{ - absm::command::fetch_machine, command::CommandTrait, define_push_element_to_collection_command, + command::CommandTrait, define_push_element_to_collection_command, define_set_collection_element_command, }; use std::fmt::Debug; diff --git a/editor/src/absm/command/mod.rs b/editor/src/plugins/absm/command/mod.rs similarity index 100% rename from editor/src/absm/command/mod.rs rename to editor/src/plugins/absm/command/mod.rs diff --git a/editor/src/absm/connection.rs b/editor/src/plugins/absm/connection.rs similarity index 87% rename from editor/src/absm/connection.rs rename to editor/src/plugins/absm/connection.rs index 18e11772c..be0b8e919 100644 --- a/editor/src/absm/connection.rs +++ b/editor/src/plugins/absm/connection.rs @@ -32,7 +32,8 @@ use crate::fyrox::{ BuildContext, Control, UiNode, UserInterface, }, }; -use crate::{absm::segment::Segment, utils::fetch_node_screen_center}; +use crate::plugins::absm::segment::Segment; +use crate::utils::fetch_node_screen_center; use std::ops::{Deref, DerefMut}; const PICKED_BRUSH: Brush = Brush::Solid(Color::opaque(100, 100, 100)); @@ -146,21 +147,23 @@ impl ConnectionBuilder { } pub fn build(self, canvas: Handle, ctx: &mut BuildContext) -> Handle { - let canvas_ref = &ctx[canvas]; + let canvas_ref = ctx.try_get_node(canvas); let connection = Connection { widget: self .widget_builder .with_foreground(NORMAL_BRUSH) .with_clip_to_bounds(false) - .build(), + .build(ctx), segment: Segment { source: self.source_socket, source_pos: canvas_ref - .screen_to_local(fetch_node_screen_center(self.source_socket, ctx)), + .map(|c| c.screen_to_local(fetch_node_screen_center(self.source_socket, ctx))) + .unwrap_or_default(), dest: self.dest_socket, dest_pos: canvas_ref - .screen_to_local(fetch_node_screen_center(self.dest_socket, ctx)), + .map(|c| c.screen_to_local(fetch_node_screen_center(self.dest_socket, ctx))) + .unwrap_or_default(), }, source_node: self.source_node, dest_node: self.dest_node, @@ -169,3 +172,16 @@ impl ConnectionBuilder { ctx.add_node(UiNode::new(connection)) } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::connection::ConnectionBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + ConnectionBuilder::new(WidgetBuilder::new()).build(Default::default(), ctx) + }); + } +} diff --git a/editor/src/absm/mod.rs b/editor/src/plugins/absm/mod.rs similarity index 72% rename from editor/src/absm/mod.rs rename to editor/src/plugins/absm/mod.rs index e96a2bede..5a6b43fec 100644 --- a/editor/src/absm/mod.rs +++ b/editor/src/plugins/absm/mod.rs @@ -39,21 +39,25 @@ use crate::fyrox::{ BuildContext, UiNode, UserInterface, }, }; -use crate::{ - absm::{ - blendspace::BlendSpaceEditor, - command::blend::{AddBlendSpacePointCommand, AddInputCommand, AddPoseSourceCommand}, - node::{AbsmNode, AbsmNodeMessage}, - parameter::ParameterPanel, - selection::AbsmSelection, - state_graph::StateGraphViewer, - state_viewer::StateViewer, - toolbar::{Toolbar, ToolbarAction}, - }, - message::MessageSender, - scene::Selection, - Message, +use crate::menu::create_menu_item; +use crate::plugin::EditorPlugin; +use crate::plugins::absm::{ + blendspace::BlendSpaceEditor, + command::blend::{AddBlendSpacePointCommand, AddInputCommand, AddPoseSourceCommand}, + node::{AbsmNode, AbsmNodeMessage}, + parameter::ParameterPanel, + selection::AbsmSelection, + state_graph::StateGraphViewer, + state_viewer::StateViewer, + toolbar::{Toolbar, ToolbarAction}, }; +use crate::scene::GameScene; +use crate::ui_scene::UiScene; +use crate::{message::MessageSender, scene::Selection, Editor, Message}; +use fyrox::core::some_or_return; +use fyrox::gui::dock::DockingManagerMessage; +use fyrox::gui::menu::MenuItemMessage; +use fyrox::gui::widget::WidgetMessage; use std::{any::Any, fmt::Debug}; mod blendspace; @@ -197,6 +201,8 @@ pub struct AbsmEditor { } impl AbsmEditor { + const WINDOW_NAME: &'static str = "AsbmEditor"; + pub fn new(ctx: &mut BuildContext, sender: MessageSender) -> Self { let state_graph_viewer = StateGraphViewer::new(ctx); let state_viewer = StateViewer::new(ctx); @@ -249,7 +255,7 @@ impl AbsmEditor { let window = WindowBuilder::new( WidgetBuilder::new() - .with_name("AsbmEditor") + .with_name(Self::WINDOW_NAME) .with_width(800.0) .with_height(500.0), ) @@ -392,6 +398,22 @@ impl AbsmEditor { } } + fn destroy(self, ui: &UserInterface, docking_manager: Handle) { + ui.send_message(DockingManagerMessage::remove_floating_window( + docking_manager, + MessageDirection::ToWidget, + self.window, + )); + ui.send_message(WidgetMessage::remove( + self.blend_space_editor.window, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); + } + pub fn sync_to_model( &mut self, editor_selection: &Selection, @@ -686,3 +708,208 @@ impl AbsmEditor { } } } + +#[derive(Default)] +pub struct AbsmEditorPlugin { + absm_editor: Option, + open_absm_editor: Handle, +} + +impl AbsmEditorPlugin { + fn get_or_create_absm_editor( + &mut self, + ui: &mut UserInterface, + sender: &MessageSender, + ) -> &mut AbsmEditor { + self.absm_editor + .get_or_insert_with(|| AbsmEditor::new(&mut ui.build_ctx(), sender.clone())) + } +} + +impl EditorPlugin for AbsmEditorPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + + if let Some(layout) = editor.settings.windows.layout.as_ref() { + if layout.has_window(AbsmEditor::WINDOW_NAME) { + self.get_or_create_absm_editor(ui, &editor.message_sender); + } + } + + let ctx = &mut ui.build_ctx(); + self.open_absm_editor = create_menu_item("ABSM Editor", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_absm_editor, + )); + } + + fn on_sync_to_model(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let absm_editor = some_or_return!(self.absm_editor.as_mut()); + let ui = editor.engine.user_interfaces.first_mut(); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let graph = &editor.engine.scenes[game_scene.scene].graph; + absm_editor.sync_to_model(&entry.selection, graph, ui); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + absm_editor.sync_to_model(&entry.selection, &ui_scene.ui, ui); + } + } + + fn on_scene_changed(&mut self, editor: &mut Editor) { + let absm_editor = some_or_return!(self.absm_editor.as_mut()); + let ui = editor.engine.user_interfaces.first_mut(); + absm_editor.clear(ui); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_absm_editor { + editor.message_sender.send(Message::OpenAbsmEditor); + } + } + + let mut absm_editor = some_or_return!(self.absm_editor.take()); + + if let Some(WindowMessage::Close) = message.data() { + if message.destination() == absm_editor.window { + self.on_leave_preview_mode(editor); + + absm_editor.destroy( + editor.engine.user_interfaces.first(), + editor.docking_manager, + ); + + return; + } + } + + if let Some(entry) = editor.scenes.current_scene_entry_mut() { + let ui = editor.engine.user_interfaces.first_mut(); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let graph = &mut editor.engine.scenes[game_scene.scene].graph; + absm_editor.handle_ui_message( + message, + &editor.message_sender, + &entry.selection, + graph, + ui, + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + absm_editor.handle_ui_message( + message, + &editor.message_sender, + &entry.selection, + &mut ui_scene.ui, + ui, + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } + + self.absm_editor = Some(absm_editor); + } + + fn on_leave_preview_mode(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let absm_editor = some_or_return!(self.absm_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let engine = &mut editor.engine; + absm_editor.try_leave_preview_mode( + &entry.selection, + &mut engine.scenes[game_scene.scene].graph, + engine.user_interfaces.first_mut(), + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + absm_editor.try_leave_preview_mode( + &entry.selection, + &mut ui_scene.ui, + editor.engine.user_interfaces.first_mut(), + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } + + fn is_in_preview_mode(&self, _editor: &Editor) -> bool { + let absm_editor = some_or_return!(self.absm_editor.as_ref(), false); + absm_editor.is_in_preview_mode() + } + + fn on_update(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let absm_editor = some_or_return!(self.absm_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_ref::() { + absm_editor.update( + &entry.selection, + &mut editor.engine.scenes[game_scene.scene].graph, + editor.engine.user_interfaces.first_mut(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + absm_editor.update( + &entry.selection, + &mut ui_scene.ui, + editor.engine.user_interfaces.first_mut(), + ); + } + } + + fn on_message(&mut self, message: &Message, editor: &mut Editor) { + if let Message::OpenAbsmEditor = message { + let ui = editor.engine.user_interfaces.first_mut(); + let absm_editor = self.get_or_create_absm_editor(ui, &editor.message_sender); + + absm_editor.open(ui); + + ui.send_message(DockingManagerMessage::add_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + absm_editor.window, + )); + + self.on_sync_to_model(editor); + } + + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let absm_editor = some_or_return!(self.absm_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_mut::() { + absm_editor.handle_message( + message, + &entry.selection, + &mut editor.engine.scenes[game_scene.scene].graph, + editor.engine.user_interfaces.first_mut(), + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + absm_editor.handle_message( + message, + &entry.selection, + &mut ui_scene.ui, + editor.engine.user_interfaces.first_mut(), + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } +} + +#[cfg(test)] +mod test { + use crate::plugins::absm::AbsmEditor; + use fyrox::core::algebra::Vector2; + use fyrox::core::pool::Handle; + use fyrox::gui::UserInterface; + + #[test] + fn test_deletion() { + let screen_size = Vector2::new(100.0, 100.0); + let mut ui = UserInterface::new(screen_size); + let editor = AbsmEditor::new(&mut ui.build_ctx(), Default::default()); + editor.destroy(&ui, Handle::NONE); + ui.update(screen_size, 1.0 / 60.0, &Default::default()); + while ui.poll_message().is_some() {} + // Only root node must be alive. + assert_eq!(ui.nodes().alive_count(), 1); + } +} diff --git a/editor/src/absm/node.rs b/editor/src/plugins/absm/node.rs similarity index 98% rename from editor/src/absm/node.rs rename to editor/src/plugins/absm/node.rs index 6d3bac04b..e119bfe47 100644 --- a/editor/src/absm/node.rs +++ b/editor/src/plugins/absm/node.rs @@ -18,10 +18,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::absm::{ - selectable::{Selectable, SelectableMessage}, - BORDER_COLOR, NORMAL_BACKGROUND, SELECTED_BACKGROUND, -}; use crate::fyrox::{ core::{ color::Color, pool::Handle, reflect::prelude::*, type_traits::prelude::*, uuid::uuid, @@ -42,6 +38,10 @@ use crate::fyrox::{ VerticalAlignment, }, }; +use crate::plugins::absm::{ + selectable::{Selectable, SelectableMessage}, + BORDER_COLOR, NORMAL_BACKGROUND, SELECTED_BACKGROUND, +}; use std::{ fmt::{Debug, Formatter}, ops::{Deref, DerefMut}, @@ -496,7 +496,7 @@ where .build(ctx); let node = AbsmNode { - widget: self.widget_builder.with_child(background).build(), + widget: self.widget_builder.with_child(background).build(ctx), background, selectable: Default::default(), model_handle: self.model_handle, @@ -516,3 +516,15 @@ where ctx.add_node(UiNode::new(node)) } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::node::AbsmNodeBuilder; + use fyrox::scene::node::Node; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| AbsmNodeBuilder::::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/absm/parameter.rs b/editor/src/plugins/absm/parameter.rs similarity index 96% rename from editor/src/absm/parameter.rs rename to editor/src/plugins/absm/parameter.rs index e915be41f..b7974d7ea 100644 --- a/editor/src/absm/parameter.rs +++ b/editor/src/plugins/absm/parameter.rs @@ -39,10 +39,10 @@ use crate::fyrox::{ BuildContext, UiNode, UserInterface, }, }; +use crate::plugins::absm::command::fetch_machine; +use crate::plugins::inspector::editors::make_property_editors_container; use crate::{ - absm::command::fetch_machine, command::make_command, - inspector::editors::make_property_editors_container, message::MessageSender, Message, - MessageDirection, MSG_SYNC_FLAG, + command::make_command, message::MessageSender, Message, MessageDirection, MSG_SYNC_FLAG, }; use std::sync::Arc; diff --git a/editor/src/absm/segment.rs b/editor/src/plugins/absm/segment.rs similarity index 100% rename from editor/src/absm/segment.rs rename to editor/src/plugins/absm/segment.rs diff --git a/editor/src/absm/selectable.rs b/editor/src/plugins/absm/selectable.rs similarity index 100% rename from editor/src/absm/selectable.rs rename to editor/src/plugins/absm/selectable.rs diff --git a/editor/src/absm/selection.rs b/editor/src/plugins/absm/selection.rs similarity index 100% rename from editor/src/absm/selection.rs rename to editor/src/plugins/absm/selection.rs diff --git a/editor/src/absm/socket.rs b/editor/src/plugins/absm/socket.rs similarity index 95% rename from editor/src/absm/socket.rs rename to editor/src/plugins/absm/socket.rs index cdbf8eb48..49f69ba8b 100644 --- a/editor/src/absm/socket.rs +++ b/editor/src/plugins/absm/socket.rs @@ -227,7 +227,7 @@ impl SocketBuilder { .build(ctx); let socket = Socket { - widget: self.widget_builder.with_child(grid).build(), + widget: self.widget_builder.with_child(grid).build(ctx), click_position: Default::default(), parent_node: self.parent_node, direction: self.direction, @@ -239,3 +239,14 @@ impl SocketBuilder { ctx.add_node(UiNode::new(socket)) } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::socket::SocketBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| SocketBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/absm/state_graph/context.rs b/editor/src/plugins/absm/state_graph/context.rs similarity index 97% rename from editor/src/absm/state_graph/context.rs rename to editor/src/plugins/absm/state_graph/context.rs index becf6f3b5..7542269e7 100644 --- a/editor/src/absm/state_graph/context.rs +++ b/editor/src/plugins/absm/state_graph/context.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use super::fetch_state_node_model_handle; use crate::fyrox::{ core::pool::Handle, generic_animation::machine::{Machine, State, Transition}, @@ -31,17 +32,17 @@ use crate::fyrox::{ BuildContext, RcUiNodeHandle, UiNode, UserInterface, }, }; -use crate::{ - absm::{ - canvas::{AbsmCanvas, AbsmCanvasMessage, Mode}, - command::{ - AddStateCommand, AddTransitionCommand, DeleteStateCommand, DeleteTransitionCommand, - SetMachineEntryStateCommand, - }, - node::{AbsmNode, AbsmNodeMessage}, - selection::SelectedEntity, - transition::TransitionView, +use crate::plugins::absm::{ + canvas::{AbsmCanvas, AbsmCanvasMessage, Mode}, + command::{ + AddStateCommand, AddTransitionCommand, DeleteStateCommand, DeleteTransitionCommand, + SetMachineEntryStateCommand, }, + node::{AbsmNode, AbsmNodeMessage}, + selection::SelectedEntity, + transition::TransitionView, +}; +use crate::{ command::{Command, CommandGroup}, menu::create_menu_item, message::MessageSender, @@ -50,8 +51,6 @@ use crate::{ use fyrox::gui::menu::ContextMenuBuilder; use std::fmt::Debug; -use super::fetch_state_node_model_handle; - pub struct CanvasContextMenu { create_state: Handle, connect_all_nodes: Handle, diff --git a/editor/src/absm/state_graph/mod.rs b/editor/src/plugins/absm/state_graph/mod.rs similarity index 97% rename from editor/src/absm/state_graph/mod.rs rename to editor/src/plugins/absm/state_graph/mod.rs index 6d4f6c02e..ffd877097 100644 --- a/editor/src/absm/state_graph/mod.rs +++ b/editor/src/plugins/absm/state_graph/mod.rs @@ -31,17 +31,17 @@ use crate::fyrox::{ BuildContext, Thickness, UiNode, UserInterface, }, }; +use crate::plugins::absm::{ + canvas::{AbsmCanvas, AbsmCanvasBuilder, AbsmCanvasMessage}, + command::{AddTransitionCommand, MoveStateNodeCommand}, + fetch_selection, + node::{AbsmNode, AbsmNodeBuilder, AbsmNodeMessage}, + selection::{AbsmSelection, SelectedEntity}, + state_graph::context::{CanvasContextMenu, NodeContextMenu, TransitionContextMenu}, + transition::{TransitionBuilder, TransitionMessage, TransitionView}, + NORMAL_BACKGROUND, NORMAL_ROOT_COLOR, SELECTED_BACKGROUND, SELECTED_ROOT_COLOR, +}; use crate::{ - absm::{ - canvas::{AbsmCanvas, AbsmCanvasBuilder, AbsmCanvasMessage}, - command::{AddTransitionCommand, MoveStateNodeCommand}, - fetch_selection, - node::{AbsmNode, AbsmNodeBuilder, AbsmNodeMessage}, - selection::{AbsmSelection, SelectedEntity}, - state_graph::context::{CanvasContextMenu, NodeContextMenu, TransitionContextMenu}, - transition::{TransitionBuilder, TransitionMessage, TransitionView}, - NORMAL_BACKGROUND, NORMAL_ROOT_COLOR, SELECTED_BACKGROUND, SELECTED_ROOT_COLOR, - }, command::{Command, CommandGroup}, message::MessageSender, scene::{commands::ChangeSelectionCommand, Selection}, diff --git a/editor/src/absm/state_viewer/context.rs b/editor/src/plugins/absm/state_viewer/context.rs similarity index 97% rename from editor/src/absm/state_viewer/context.rs rename to editor/src/plugins/absm/state_viewer/context.rs index a7e318217..5d057eb0a 100644 --- a/editor/src/absm/state_viewer/context.rs +++ b/editor/src/plugins/absm/state_viewer/context.rs @@ -35,19 +35,19 @@ use crate::fyrox::{ BuildContext, RcUiNodeHandle, UiNode, UserInterface, }, }; -use crate::{ - absm::{ - command::{ - blend::{ - SetBlendAnimationByIndexInputPoseSourceCommand, - SetBlendAnimationsPoseSourceCommand, SetBlendSpacePoseSourceCommand, - }, - AddPoseNodeCommand, DeletePoseNodeCommand, SetStateRootPoseCommand, +use crate::plugins::absm::{ + command::{ + blend::{ + SetBlendAnimationByIndexInputPoseSourceCommand, SetBlendAnimationsPoseSourceCommand, + SetBlendSpacePoseSourceCommand, }, - connection::Connection, - node::AbsmNode, - selection::SelectedEntity, + AddPoseNodeCommand, DeletePoseNodeCommand, SetStateRootPoseCommand, }, + connection::Connection, + node::AbsmNode, + selection::SelectedEntity, +}; +use crate::{ command::{Command, CommandGroup}, menu::create_menu_item, message::MessageSender, diff --git a/editor/src/absm/state_viewer/mod.rs b/editor/src/plugins/absm/state_viewer/mod.rs similarity index 97% rename from editor/src/absm/state_viewer/mod.rs rename to editor/src/plugins/absm/state_viewer/mod.rs index a2a95be01..0a30a0ca8 100644 --- a/editor/src/absm/state_viewer/mod.rs +++ b/editor/src/plugins/absm/state_viewer/mod.rs @@ -38,24 +38,24 @@ use crate::fyrox::{ }, }; use crate::message::MessageSender; -use crate::{ - absm::{ - canvas::{AbsmCanvasBuilder, AbsmCanvasMessage}, - command::{ - blend::{ - SetBlendAnimationByIndexInputPoseSourceCommand, - SetBlendAnimationsPoseSourceCommand, SetBlendSpacePoseSourceCommand, - }, - MovePoseNodeCommand, +use crate::plugins::absm::{ + canvas::{AbsmCanvasBuilder, AbsmCanvasMessage}, + command::{ + blend::{ + SetBlendAnimationByIndexInputPoseSourceCommand, SetBlendAnimationsPoseSourceCommand, + SetBlendSpacePoseSourceCommand, }, - connection::{Connection, ConnectionBuilder}, - fetch_selection, - node::{AbsmNode, AbsmNodeBuilder, AbsmNodeMessage}, - selection::{AbsmSelection, SelectedEntity}, - socket::{Socket, SocketBuilder, SocketDirection}, - state_viewer::context::{CanvasContextMenu, ConnectionContextMenu, NodeContextMenu}, - NORMAL_BACKGROUND, NORMAL_ROOT_COLOR, SELECTED_BACKGROUND, SELECTED_ROOT_COLOR, + MovePoseNodeCommand, }, + connection::{Connection, ConnectionBuilder}, + fetch_selection, + node::{AbsmNode, AbsmNodeBuilder, AbsmNodeMessage}, + selection::{AbsmSelection, SelectedEntity}, + socket::{Socket, SocketBuilder, SocketDirection}, + state_viewer::context::{CanvasContextMenu, ConnectionContextMenu, NodeContextMenu}, + NORMAL_BACKGROUND, NORMAL_ROOT_COLOR, SELECTED_BACKGROUND, SELECTED_ROOT_COLOR, +}; +use crate::{ scene::{commands::ChangeSelectionCommand, Selection}, send_sync_message, }; diff --git a/editor/src/absm/toolbar.rs b/editor/src/plugins/absm/toolbar.rs similarity index 96% rename from editor/src/absm/toolbar.rs rename to editor/src/plugins/absm/toolbar.rs index a0201e051..d7f5e57ee 100644 --- a/editor/src/absm/toolbar.rs +++ b/editor/src/plugins/absm/toolbar.rs @@ -36,16 +36,15 @@ use crate::fyrox::{ widget::{WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, Orientation, Thickness, UiNode, UserInterface, VerticalAlignment, - BRUSH_BRIGHT, }, }; +use crate::plugins::absm::{ + animation_container_ref, + command::{AddLayerCommand, RemoveLayerCommand, SetLayerMaskCommand, SetLayerNameCommand}, + fetch_selection, machine_container_ref, + selection::AbsmSelection, +}; use crate::{ - absm::{ - animation_container_ref, - command::{AddLayerCommand, RemoveLayerCommand, SetLayerMaskCommand, SetLayerNameCommand}, - fetch_selection, machine_container_ref, - selection::AbsmSelection, - }, command::{Command, CommandGroup}, gui::make_dropdown_list_option, load_image, @@ -57,6 +56,8 @@ use crate::{ }, send_sync_message, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; pub struct Toolbar { pub panel: Handle, @@ -156,9 +157,9 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background(ctx.style.get_or_default(Style::BRUSH_BRIGHT)), ) - .with_opt_texture(load_image(include_bytes!("../../resources/filter.png"))) + .with_opt_texture(load_image!("../../../resources/filter.png")) .build(ctx), ) .build(ctx); diff --git a/editor/src/absm/transition.rs b/editor/src/plugins/absm/transition.rs similarity index 93% rename from editor/src/absm/transition.rs rename to editor/src/plugins/absm/transition.rs index 46a900c67..0479eefb4 100644 --- a/editor/src/absm/transition.rs +++ b/editor/src/plugins/absm/transition.rs @@ -39,13 +39,11 @@ use crate::fyrox::{ BuildContext, Control, UiNode, UserInterface, }, }; -use crate::{ - absm::{ - segment::Segment, - selectable::{Selectable, SelectableMessage}, - }, - utils::fetch_node_center, +use crate::plugins::absm::{ + segment::Segment, + selectable::{Selectable, SelectableMessage}, }; +use crate::utils::fetch_node_center; use std::ops::{Deref, DerefMut}; const PICKED_COLOR: Color = Color::opaque(100, 100, 100); @@ -203,7 +201,7 @@ impl TransitionBuilder { .with_foreground(NORMAL_BRUSH.clone()) .with_clip_to_bounds(false) .with_need_update(true) - .build(), + .build(ctx), segment: Segment { source: self.source, source_pos: fetch_node_center(self.source, ctx), @@ -218,3 +216,16 @@ impl TransitionBuilder { ctx.add_node(UiNode::new(transition)) } } + +#[cfg(test)] +mod test { + use crate::plugins::absm::transition::TransitionBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + TransitionBuilder::new(WidgetBuilder::new()).build(Default::default(), ctx) + }); + } +} diff --git a/editor/src/animation/command/mod.rs b/editor/src/plugins/animation/command/mod.rs similarity index 99% rename from editor/src/animation/command/mod.rs rename to editor/src/plugins/animation/command/mod.rs index 3fc9f3f9a..085728f52 100644 --- a/editor/src/animation/command/mod.rs +++ b/editor/src/plugins/animation/command/mod.rs @@ -32,8 +32,8 @@ use crate::fyrox::{ }, graph::{BaseSceneGraph, SceneGraphNode}, }; +use crate::plugins::animation::selection::AnimationSelection; use crate::{ - animation::selection::AnimationSelection, command::{CommandContext, CommandTrait}, scene::{commands::GameSceneContext, Selection}, ui_scene::commands::UiSceneContext, diff --git a/editor/src/animation/mod.rs b/editor/src/plugins/animation/mod.rs similarity index 77% rename from editor/src/animation/mod.rs rename to editor/src/plugins/animation/mod.rs index 91a33b8ed..37b78b640 100644 --- a/editor/src/animation/mod.rs +++ b/editor/src/plugins/animation/mod.rs @@ -19,7 +19,36 @@ // SOFTWARE. use crate::{ - animation::{ + command::{Command, CommandGroup}, + fyrox::{ + asset::manager::ResourceManager, + core::{ + algebra::Vector2, color::Color, log::Log, math::curve::Curve, math::Rect, + pool::ErasedHandle, pool::Handle, some_or_return, uuid::Uuid, + variable::InheritableVariable, + }, + fxhash::FxHashSet, + generic_animation::{signal::AnimationSignal, AnimationContainer}, + graph::{BaseSceneGraph, PrefabData, SceneGraph, SceneGraphNode}, + gui::{ + border::BorderBuilder, + brush::Brush, + check_box::CheckBoxMessage, + curve::{CurveEditorBuilder, CurveEditorMessage, HighlightZone}, + dock::DockingManagerMessage, + grid::{Column, GridBuilder, Row}, + menu::MenuItemMessage, + message::{MessageDirection, UiMessage}, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, UiNode, UserInterface, + }, + resource::model::AnimationSource, + }, + menu::create_menu_item, + message::MessageSender, + plugin::EditorPlugin, + plugins::animation::{ command::{ AddAnimationSignal, MoveAnimationSignal, RemoveAnimationSignal, ReplaceTrackCurveCommand, @@ -30,35 +59,13 @@ use crate::{ toolbar::{Toolbar, ToolbarAction}, track::TrackList, }, - command::{Command, CommandGroup}, - fyrox::{ - asset::manager::ResourceManager, - core::{ - algebra::Vector2, log::Log, math::Rect, pool::ErasedHandle, pool::Handle, uuid::Uuid, - variable::InheritableVariable, - }, - fxhash::FxHashSet, - generic_animation::{signal::AnimationSignal, AnimationContainer}, - graph::{BaseSceneGraph, PrefabData, SceneGraph, SceneGraphNode}, - gui::{ - border::BorderBuilder, - check_box::CheckBoxMessage, - curve::{CurveEditorBuilder, CurveEditorMessage, HighlightZone}, - grid::{Column, GridBuilder, Row}, - message::{MessageDirection, UiMessage}, - widget::{WidgetBuilder, WidgetMessage}, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, UiNode, UserInterface, BRUSH_DARK, BRUSH_PRIMARY, - }, - resource::model::AnimationSource, - }, - message::MessageSender, - scene::{commands::ChangeSelectionCommand, Selection}, - send_sync_message, Message, + scene::{commands::ChangeSelectionCommand, GameScene, Selection}, + send_sync_message, + ui_scene::UiScene, + Editor, Message, }; -use fyrox::core::color::Color; -use fyrox::core::math::curve::Curve; -use fyrox::gui::brush::Brush; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::any::{Any, TypeId}; pub mod command; @@ -194,6 +201,8 @@ where } impl AnimationEditor { + const WINDOW_NAME: &'static str = "AnimationEditor"; + pub fn new(ctx: &mut BuildContext) -> Self { let curve_editor; let ruler; @@ -225,7 +234,9 @@ impl AnimationEditor { .with_child({ curve_editor = CurveEditorBuilder::new( WidgetBuilder::new() - .with_background(BRUSH_DARK) + .with_background( + ctx.style.get_or_default(Style::BRUSH_DARK), + ) .on_row(1), ) .with_show_x_values(false) @@ -263,7 +274,7 @@ impl AnimationEditor { let window = WindowBuilder::new( WidgetBuilder::new() - .with_name("AnimationEditor") + .with_name(Self::WINDOW_NAME) .with_width(600.0) .with_height(500.0), ) @@ -634,6 +645,19 @@ impl AnimationEditor { } } + pub fn destroy(self, ui: &UserInterface, docking_manager: Handle) { + self.toolbar.destroy(ui); + ui.send_message(DockingManagerMessage::remove_floating_window( + docking_manager, + MessageDirection::ToWidget, + self.window, + )); + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); + } + pub fn clear(&mut self, ui: &UserInterface) { self.toolbar.clear(ui); self.track_list.clear(ui); @@ -706,7 +730,7 @@ impl AnimationEditor { animation.time_slice().end - animation.time_slice().start, 200000.0, ), - brush: BRUSH_PRIMARY, + brush: ui.style.get_or_default(Style::BRUSH_PRIMARY), }], ), ); @@ -899,3 +923,205 @@ impl AnimationEditor { ); } } + +#[derive(Default)] +pub struct AnimationEditorPlugin { + animation_editor: Option, + open_animation_editor: Handle, +} + +impl AnimationEditorPlugin { + fn get_or_create_animation_editor(&mut self, ui: &mut UserInterface) -> &mut AnimationEditor { + self.animation_editor + .get_or_insert_with(|| AnimationEditor::new(&mut ui.build_ctx())) + } +} + +impl EditorPlugin for AnimationEditorPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + + if let Some(layout) = editor.settings.windows.layout.as_ref() { + if layout.has_window(AnimationEditor::WINDOW_NAME) { + self.get_or_create_animation_editor(ui); + } + } + + let ctx = &mut ui.build_ctx(); + self.open_animation_editor = create_menu_item("Animation Editor", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_animation_editor, + )); + } + + fn on_sync_to_model(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let animation_editor = some_or_return!(self.animation_editor.as_mut()); + let ui = editor.engine.user_interfaces.first_mut(); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let graph = &editor.engine.scenes[game_scene.scene].graph; + animation_editor.sync_to_model(&entry.selection, ui, graph); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + animation_editor.sync_to_model(&entry.selection, ui, &ui_scene.ui); + } + } + + fn on_scene_changed(&mut self, editor: &mut Editor) { + let animation_editor = some_or_return!(self.animation_editor.as_mut()); + let ui = editor.engine.user_interfaces.first_mut(); + animation_editor.clear(ui); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_animation_editor { + editor.message_sender.send(Message::OpenAnimationEditor); + } + } + + let mut animation_editor = some_or_return!(self.animation_editor.take()); + + if let Some(WindowMessage::Close) = message.data() { + if message.destination() == animation_editor.window { + self.on_leave_preview_mode(editor); + + animation_editor.destroy( + editor.engine.user_interfaces.first(), + editor.docking_manager, + ); + + return; + } + } + + if let Some(entry) = editor.scenes.current_scene_entry_mut() { + let ui = editor.engine.user_interfaces.first_mut(); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let graph = &mut editor.engine.scenes[game_scene.scene].graph; + animation_editor.handle_ui_message( + message, + &entry.selection, + graph, + game_scene.scene_content_root, + ui, + &editor.engine.resource_manager, + &editor.message_sender, + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + let ui_root = ui_scene.ui.root(); + animation_editor.handle_ui_message( + message, + &entry.selection, + &mut ui_scene.ui, + ui_root, + ui, + &editor.engine.resource_manager, + &editor.message_sender, + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } + + self.animation_editor = Some(animation_editor); + } + + fn on_leave_preview_mode(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let animation_editor = some_or_return!(self.animation_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_mut::() { + let engine = &mut editor.engine; + animation_editor.try_leave_preview_mode( + &mut engine.scenes[game_scene.scene].graph, + engine.user_interfaces.first(), + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + animation_editor.try_leave_preview_mode( + &mut ui_scene.ui, + editor.engine.user_interfaces.first(), + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } + + fn is_in_preview_mode(&self, _editor: &Editor) -> bool { + let animation_editor = some_or_return!(self.animation_editor.as_ref(), false); + animation_editor.is_in_preview_mode() + } + + fn on_update(&mut self, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let animation_editor = some_or_return!(self.animation_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_ref::() { + animation_editor.update( + &entry.selection, + editor.engine.user_interfaces.first(), + &editor.engine.scenes[game_scene.scene].graph, + ); + } else if let Some(ui_scene) = entry.controller.downcast_ref::() { + animation_editor.update( + &entry.selection, + editor.engine.user_interfaces.first(), + &ui_scene.ui, + ); + } + } + + fn on_message(&mut self, message: &Message, editor: &mut Editor) { + if let Message::OpenAnimationEditor = message { + let ui = editor.engine.user_interfaces.first_mut(); + let animation_editor = self.get_or_create_animation_editor(ui); + + animation_editor.open(ui); + + ui.send_message(DockingManagerMessage::add_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + animation_editor.window, + )); + + self.on_sync_to_model(editor); + } + + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let animation_editor = some_or_return!(self.animation_editor.as_mut()); + if let Some(game_scene) = entry.controller.downcast_mut::() { + animation_editor.handle_message( + message, + &mut editor.engine.scenes[game_scene.scene].graph, + editor.engine.user_interfaces.first(), + game_scene.graph_switches.node_overrides.as_mut().unwrap(), + ); + } else if let Some(ui_scene) = entry.controller.downcast_mut::() { + animation_editor.handle_message( + message, + &mut ui_scene.ui, + editor.engine.user_interfaces.first(), + ui_scene.ui_update_switches.node_overrides.as_mut().unwrap(), + ); + } + } +} + +#[cfg(test)] +mod test { + use crate::plugins::animation::AnimationEditor; + use fyrox::core::algebra::Vector2; + use fyrox::core::pool::Handle; + use fyrox::gui::UserInterface; + + #[test] + fn test_deletion() { + let screen_size = Vector2::new(100.0, 100.0); + let mut ui = UserInterface::new(screen_size); + let editor = AnimationEditor::new(&mut ui.build_ctx()); + editor.destroy(&ui, Handle::NONE); + ui.update(screen_size, 1.0 / 60.0, &Default::default()); + while ui.poll_message().is_some() {} + // Only root node must be alive. + assert_eq!(ui.nodes().alive_count(), 1); + } +} diff --git a/editor/src/animation/ruler.rs b/editor/src/plugins/animation/ruler.rs similarity index 96% rename from editor/src/animation/ruler.rs rename to editor/src/plugins/animation/ruler.rs index 09d69c4c3..bdfba9397 100644 --- a/editor/src/animation/ruler.rs +++ b/editor/src/plugins/animation/ruler.rs @@ -39,13 +39,14 @@ use crate::fyrox::{ popup::{Placement, PopupMessage}, stack_panel::StackPanelBuilder, widget::{Widget, WidgetBuilder, WidgetMessage}, - BuildContext, Control, RcUiNodeHandle, UiNode, UserInterface, BRUSH_BRIGHT, BRUSH_DARKER, - BRUSH_LIGHTER, BRUSH_LIGHTEST, + BuildContext, Control, RcUiNodeHandle, UiNode, UserInterface, }, }; use crate::menu::create_menu_item; use fyrox::gui::curve::{CurveTransformCell, STANDARD_GRID_SIZE}; use fyrox::gui::menu::ContextMenuBuilder; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, @@ -263,9 +264,9 @@ impl Control for Ruler { Vector2::new(x, local_bounds.h()), ]); let brush = if signal.selected { - BRUSH_BRIGHT + ctx.style.get_or_default(Style::BRUSH_BRIGHT) } else { - BRUSH_LIGHTEST + ctx.style.get_or_default(Style::BRUSH_LIGHTEST) }; ctx.commit(self.clip_bounds(), brush, CommandTexture::None, None); } @@ -459,9 +460,9 @@ impl RulerBuilder { .widget_builder .with_preview_messages(true) .with_context_menu(context_menu.menu.clone()) - .with_background(BRUSH_DARKER) - .with_foreground(BRUSH_LIGHTER) - .build(), + .with_background(ctx.style.get_or_default(Style::BRUSH_DARKER)) + .with_foreground(ctx.style.get_or_default(Style::BRUSH_LIGHTER)) + .build(ctx), transform: Default::default(), text: RefCell::new(FormattedTextBuilder::new(ctx.default_font()).build()), value: self.value, @@ -473,3 +474,14 @@ impl RulerBuilder { ctx.add_node(UiNode::new(ruler)) } } + +#[cfg(test)] +mod test { + use crate::plugins::animation::ruler::RulerBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| RulerBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/animation/selection.rs b/editor/src/plugins/animation/selection.rs similarity index 100% rename from editor/src/animation/selection.rs rename to editor/src/plugins/animation/selection.rs diff --git a/editor/src/animation/thumb.rs b/editor/src/plugins/animation/thumb.rs similarity index 90% rename from editor/src/animation/thumb.rs rename to editor/src/plugins/animation/thumb.rs index 0b5f02069..9555d8cf6 100644 --- a/editor/src/animation/thumb.rs +++ b/editor/src/plugins/animation/thumb.rs @@ -36,10 +36,12 @@ use crate::fyrox::{ draw::{CommandTexture, Draw, DrawingContext}, message::{MessageDirection, UiMessage}, widget::{Widget, WidgetBuilder}, - BuildContext, Control, UiNode, UserInterface, BRUSH_BRIGHT, + BuildContext, Control, UiNode, UserInterface, }, }; use fyrox::gui::curve::CurveTransformCell; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] @@ -141,8 +143,8 @@ impl ThumbBuilder { widget: self .widget_builder .with_hit_test_visibility(false) - .with_foreground(BRUSH_BRIGHT) - .build(), + .with_foreground(ctx.style.get_or_default(Style::BRUSH_BRIGHT)) + .build(ctx), transform: CurveTransformCell::default(), position: 0.0, }; @@ -150,3 +152,14 @@ impl ThumbBuilder { ctx.add_node(UiNode::new(ruler)) } } + +#[cfg(test)] +mod test { + use crate::plugins::animation::thumb::ThumbBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| ThumbBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/animation/toolbar.rs b/editor/src/plugins/animation/toolbar.rs similarity index 94% rename from editor/src/animation/toolbar.rs rename to editor/src/plugins/animation/toolbar.rs index 4d38587ba..07b71147d 100644 --- a/editor/src/animation/toolbar.rs +++ b/editor/src/plugins/animation/toolbar.rs @@ -45,20 +45,20 @@ use crate::fyrox::{ widget::{WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_BRIGHT, BRUSH_LIGHT, + VerticalAlignment, }, resource::model::AnimationSource, }; -use crate::{ - animation::{ - command::{ - AddAnimationCommand, RemoveAnimationCommand, ReplaceAnimationCommand, - SetAnimationEnabledCommand, SetAnimationLoopingCommand, SetAnimationNameCommand, - SetAnimationRootMotionSettingsCommand, SetAnimationSpeedCommand, - SetAnimationTimeSliceCommand, - }, - selection::AnimationSelection, +use crate::plugins::animation::{ + command::{ + AddAnimationCommand, RemoveAnimationCommand, ReplaceAnimationCommand, + SetAnimationEnabledCommand, SetAnimationLoopingCommand, SetAnimationNameCommand, + SetAnimationRootMotionSettingsCommand, SetAnimationSpeedCommand, + SetAnimationTimeSliceCommand, }, + selection::AnimationSelection, +}; +use crate::{ command::{Command, CommandGroup}, gui::make_dropdown_list_option_universal, load_image, @@ -70,6 +70,8 @@ use crate::{ }, send_sync_message, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::path::Path; enum ImportMode { @@ -329,6 +331,13 @@ impl RootMotionDropdownArea { } } + pub fn destroy(self, ui: &UserInterface) { + ui.send_message(WidgetMessage::remove( + self.popup, + MessageDirection::ToWidget, + )); + } + pub fn sync_to_model( &self, animation: &Animation>, @@ -417,7 +426,7 @@ impl Toolbar { let panel = BorderBuilder::new( WidgetBuilder::new() .on_row(0) - .with_foreground(BRUSH_LIGHT) + .with_foreground(ctx.style.get_or_default(Style::BRUSH_LIGHT)) .with_child( StackPanelBuilder::new( WidgetBuilder::new() @@ -468,11 +477,11 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/import.png" - ))) + .with_opt_texture(load_image!("../../../resources/import.png")) .build(ctx), ) .build(ctx); @@ -498,11 +507,13 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/reimport.png" - ))) + .with_opt_texture(load_image!( + "../../../resources/reimport.png" + )) .build(ctx), ) .build(ctx); @@ -527,11 +538,11 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/rename.png" - ))) + .with_opt_texture(load_image!("../../../resources/rename.png")) .build(ctx), ) .build(ctx); @@ -582,11 +593,11 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/copy.png" - ))) + .with_opt_texture(load_image!("../../../resources/copy.png")) .build(ctx), ) .build(ctx); @@ -638,11 +649,11 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/speed.png" - ))) + .with_opt_texture(load_image!("../../../resources/speed.png")) .build(ctx), ) .with_child({ @@ -666,11 +677,11 @@ impl Toolbar { .with_width(18.0) .with_height(18.0) .with_margin(Thickness::uniform(1.0)) - .with_background(BRUSH_BRIGHT), + .with_background( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/time.png" - ))) + .with_opt_texture(load_image!("../../../resources/time.png")) .build(ctx), ) .with_child({ @@ -751,7 +762,9 @@ impl Toolbar { .with_content( VectorImageBuilder::new( WidgetBuilder::new() - .with_foreground(BRUSH_BRIGHT) + .with_foreground( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + ) .with_tooltip(make_simple_tooltip(ctx, "Play/Pause")), ) .with_primitives(vec![ @@ -782,9 +795,9 @@ impl Toolbar { .with_tooltip(make_simple_tooltip(ctx, "Stop Playback")), ) .with_content( - VectorImageBuilder::new( - WidgetBuilder::new().with_foreground(BRUSH_BRIGHT), - ) + VectorImageBuilder::new(WidgetBuilder::new().with_foreground( + ctx.style.get_or_default(Style::BRUSH_BRIGHT), + )) .with_primitives(vec![Primitive::RectangleFilled { rect: Rect::new(0.0, 0.0, 16.0, 16.0), }]) @@ -808,7 +821,7 @@ impl Toolbar { ) .build(ctx); - let file_selector = FileSelectorBuilder::new( + let import_file_selector = FileSelectorBuilder::new( WindowBuilder::new(WidgetBuilder::new().with_width(300.0).with_height(400.0)) .open(false) .with_title(WindowTitle::text("Select Animation To Import")), @@ -842,7 +855,7 @@ impl Toolbar { import, reimport, node_selector, - import_file_selector: file_selector, + import_file_selector, selected_import_root: Default::default(), looping, enabled, @@ -852,6 +865,18 @@ impl Toolbar { } } + pub fn destroy(self, ui: &UserInterface) { + ui.send_message(WidgetMessage::remove( + self.node_selector, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.import_file_selector, + MessageDirection::ToWidget, + )); + self.root_motion_dropdown_area.destroy(ui); + } + pub fn handle_ui_message( &mut self, message: &UiMessage, diff --git a/editor/src/animation/track.rs b/editor/src/plugins/animation/track.rs similarity index 95% rename from editor/src/animation/track.rs rename to editor/src/plugins/animation/track.rs index fb3b911fb..dd5b1ddfd 100644 --- a/editor/src/animation/track.rs +++ b/editor/src/plugins/animation/track.rs @@ -20,15 +20,15 @@ #![allow(clippy::manual_map)] -use crate::{ - animation::{ - animation_container_ref, - command::{ - AddTrackCommand, RemoveTrackCommand, SetTrackEnabledCommand, SetTrackTargetCommand, - SetTrackValueBindingCommand, - }, - selection::{AnimationSelection, SelectedEntity}, +use crate::plugins::animation::{ + animation_container_ref, + command::{ + AddTrackCommand, RemoveTrackCommand, SetTrackEnabledCommand, SetTrackTargetCommand, + SetTrackValueBindingCommand, }, + selection::{AnimationSelection, SelectedEntity}, +}; +use crate::{ command::{Command, CommandGroup}, fyrox::{ core::{ @@ -72,7 +72,7 @@ use crate::{ widget::{Widget, WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, Control, Orientation, RcUiNodeHandle, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_BRIGHT, BRUSH_TEXT, + VerticalAlignment, }, resource::texture::TextureBytes, }, @@ -92,8 +92,11 @@ use crate::{ send_sync_message, utils, }; use fyrox::generic_animation::track::TrackBinding; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use fyrox::renderer::framework::DrawParameters; use fyrox::scene::mesh::buffer::{TriangleBuffer, VertexBuffer}; +use fyrox::scene::sound::Samples; use std::{ any::TypeId, cmp::Ordering, @@ -369,7 +372,7 @@ impl Control for TrackView { self.name_text, MessageDirection::ToWidget, if result.is_ok() { - BRUSH_TEXT + ui.style.get_or_default(Style::BRUSH_TEXT) } else { Brush::Solid(Color::RED) }, @@ -547,13 +550,11 @@ impl Toolbar { .with_content( ImageBuilder::new( WidgetBuilder::new() - .with_background(BRUSH_BRIGHT) + .with_background(ctx.style.get_or_default(Style::BRUSH_BRIGHT)) .with_width(16.0) .with_height(16.0), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/collapse.png" - ))) + .with_opt_texture(load_image!("../../../resources/collapse.png")) .build(ctx), ) .build(ctx); @@ -569,11 +570,11 @@ impl Toolbar { .with_content( ImageBuilder::new( WidgetBuilder::new() - .with_background(BRUSH_BRIGHT) + .with_background(ctx.style.get_or_default(Style::BRUSH_BRIGHT)) .with_width(16.0) .with_height(16.0), ) - .with_opt_texture(load_image(include_bytes!("../../resources/expand.png"))) + .with_opt_texture(load_image!("../../../resources/expand.png")) .build(ctx), ) .build(ctx); @@ -675,9 +676,7 @@ impl TrackList { ctx, 22.0, 22.0, - load_image(include_bytes!( - "../../resources/property_track.png" - )), + load_image!("../../../resources/property_track.png"), "Add Property Track.\n\ Create generic property binding to a numeric property.", Some(0), @@ -689,9 +688,7 @@ impl TrackList { ctx, 22.0, 22.0, - load_image(include_bytes!( - "../../resources/position_track.png" - )), + load_image!("../../../resources/position_track.png"), "Add Position Track.\n\ Creates a binding to a local position of a node. \ Such binding is much more performant than generic \ @@ -705,7 +702,7 @@ impl TrackList { ctx, 22.0, 22.0, - load_image(include_bytes!("../../resources/scaling_track.png")), + load_image!("../../../resources/scaling_track.png"), "Add Scale Track.\n\ Creates a binding to a local scale of a node. \ Such binding is much more performant than generic \ @@ -719,9 +716,7 @@ impl TrackList { ctx, 22.0, 22.0, - load_image(include_bytes!( - "../../resources/rotation_track.png" - )), + load_image!("../../../resources/rotation_track.png"), "Add Rotation Track.\n\ Creates a binding to a local rotation of a node. \ Such binding is much more performant than generic \ @@ -879,7 +874,7 @@ impl TrackList { match self.property_binding_mode { PropertyBindingMode::Generic => { self.property_selector = - Self::open_property_selector(graph, (*first).into(), ui); + Self::open_property_selector(graph, (*first).into(), None, ui); } PropertyBindingMode::Position => { sender.do_command(AddTrackCommand::new( @@ -1103,6 +1098,7 @@ impl TrackList { fn open_property_selector( graph: &G, node: Handle, + existing_binding: Option<&ValueBinding>, ui: &mut UserInterface, ) -> Handle where @@ -1121,6 +1117,7 @@ impl TrackList { && type_id != TypeId::of::() // Makes no sense to animate drawing parameters. && type_id != TypeId::of::() + && type_id != TypeId::of::() }); }); } @@ -1147,6 +1144,21 @@ impl TrackList { UnitQuaternion }))) + .with_selected_property_paths( + existing_binding + .and_then(|binding| { + if let ValueBinding::Property { name, value_type } = binding { + Some(vec![PropertyDescriptorData { + name: name.to_string(), + path: name.to_string(), + type_id: value_type.into_type_id(), + }]) + } else { + None + } + }) + .unwrap_or_default(), + ) .with_property_descriptors(descriptors) .build(&mut ui.build_ctx()); @@ -1174,10 +1186,25 @@ impl TrackList { return; }; - if let Some(binding) = animation.track_bindings().get(&first_selected_track) { - self.context_menu.property_rebinding_selector = - Self::open_property_selector(graph, binding.target(), ui); - } + let tracks_data_state = animation.tracks_data().state(); + let Some(tracks_data) = tracks_data_state.data_ref() else { + return; + }; + + let Some(binding) = animation.track_bindings().get(&first_selected_track) else { + return; + }; + + let Some(track) = tracks_data + .tracks + .iter() + .find(|track| track.id() == first_selected_track) + else { + return; + }; + + self.context_menu.property_rebinding_selector = + Self::open_property_selector(graph, binding.target(), Some(track.value_binding()), ui); } fn rebind_property( @@ -1615,3 +1642,17 @@ impl TrackList { } } } + +#[cfg(test)] +mod test { + use crate::plugins::animation::track::TrackViewBuilder; + use fyrox::gui::tree::TreeBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + TrackViewBuilder::new(TreeBuilder::new(WidgetBuilder::new())).build(ctx) + }); + } +} diff --git a/editor/src/plugins/collider/mod.rs b/editor/src/plugins/collider/mod.rs index 3b88b95be..b2459676b 100644 --- a/editor/src/plugins/collider/mod.rs +++ b/editor/src/plugins/collider/mod.rs @@ -29,6 +29,7 @@ mod cuboid; mod cuboid2d; mod cylinder; mod dummy; +mod panel; mod segment; mod segment2d; mod triangle; @@ -45,19 +46,29 @@ use crate::{ math::{plane::Plane, Matrix4Ext}, pool::Handle, reflect::Reflect, + some_or_return, type_traits::prelude::*, Uuid, }, engine::Engine, graph::{BaseSceneGraph, SceneGraph, SceneGraphNode}, - gui::{BuildContext, UiNode}, + gui::{ + dock::DockingManagerMessage, + message::{MessageDirection, UiMessage}, + BuildContext, UiNode, + }, material::{ shader::{ShaderResource, ShaderResourceExtension}, Material, MaterialResource, }, scene::{ - base::BaseBuilder, collider::Collider, collider::ColliderShape, dim2, node::Node, - sprite::SpriteBuilder, Scene, + base::BaseBuilder, + collider::{Collider, ColliderShape}, + dim2, + node::Node, + sprite::Sprite, + sprite::SpriteBuilder, + Scene, }, }, interaction::{ @@ -71,14 +82,13 @@ use crate::{ ball::BallShapeGizmo, ball2d::Ball2DShapeGizmo, capsule::CapsuleShapeGizmo, capsule2d::Capsule2DShapeGizmo, cone::ConeShapeGizmo, cuboid::CuboidShapeGizmo, cuboid2d::Cuboid2DShapeGizmo, cylinder::CylinderShapeGizmo, dummy::DummyShapeGizmo, - segment::SegmentShapeGizmo, segment2d::Segment2DShapeGizmo, triangle::TriangleShapeGizmo, - triangle2d::Triangle2DShapeGizmo, + panel::ColliderControlPanel, segment::SegmentShapeGizmo, segment2d::Segment2DShapeGizmo, + triangle::TriangleShapeGizmo, triangle2d::Triangle2DShapeGizmo, }, scene::{commands::GameSceneContext, controller::SceneController, GameScene, Selection}, settings::Settings, Editor, Message, }; -use fyrox::scene::sprite::Sprite; fn try_get_collider_shape(collider: Handle, scene: &Scene) -> Option { scene @@ -272,7 +282,7 @@ fn make_handle(scene: &mut Scene, root: Handle, visible: bool) -> Handle, +} + +impl EditorPlugin for ColliderPlugin { + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let game_scene = some_or_return!(entry.controller.downcast_mut::()); + let panel = some_or_return!(self.panel.as_mut()); + panel.handle_ui_message( + message, + &editor.engine, + game_scene, + &entry.selection, + &editor.message_sender, + ); + } -impl EditorPlugin for ColliderShapePlugin { fn on_message(&mut self, message: &Message, editor: &mut Editor) { - let Some(entry) = editor.scenes.current_scene_entry_mut() else { - return; - }; - - let Some(selection) = entry.selection.as_graph() else { - return; - }; - - let Some(game_scene) = entry.controller.downcast_mut::() else { - return; - }; + let entry = some_or_return!(editor.scenes.current_scene_entry_mut()); + let selection = some_or_return!(entry.selection.as_graph()); + let game_scene = some_or_return!(entry.controller.downcast_mut::()); let scene = &mut editor.engine.scenes[game_scene.scene]; @@ -688,36 +705,50 @@ impl EditorPlugin for ColliderShapePlugin { mode.shape_gizmo.destroy(scene); } - for node_handle in selection.nodes().iter() { - if let Some(collider) = scene.graph.try_get(*node_handle) { - if collider.component_ref::().is_none() - && collider - .component_ref::() - .is_none() - { - continue; - } + let first_selected_collider = selection.nodes().iter().find(|h| { + scene.graph.has_component::(**h) + || scene.graph.has_component::(**h) + }); - let shape_gizmo = make_shape_gizmo( - *node_handle, - scene, - game_scene.editor_objects_root, - false, - ); + if let Some(first_selected_collider) = first_selected_collider { + let shape_gizmo = make_shape_gizmo( + *first_selected_collider, + scene, + game_scene.editor_objects_root, + false, + ); - let move_gizmo = MoveGizmo::new(game_scene, &mut editor.engine); + let move_gizmo = MoveGizmo::new(game_scene, &mut editor.engine); - entry.interaction_modes.add(ColliderShapeInteractionMode { - collider: *node_handle, - shape_gizmo, - move_gizmo, - drag_context: None, - selected_handle: Default::default(), - message_sender: editor.message_sender.clone(), - }); + entry.interaction_modes.add(ColliderShapeInteractionMode { + collider: *first_selected_collider, + shape_gizmo, + move_gizmo, + drag_context: None, + selected_handle: Default::default(), + message_sender: editor.message_sender.clone(), + }); - break; + if self.panel.is_none() { + let ui = editor.engine.user_interfaces.first_mut(); + let panel = + ColliderControlPanel::new(editor.scene_viewer.frame(), &mut ui.build_ctx()); + ui.send_message(DockingManagerMessage::add_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + panel.window, + )); + panel.open(ui); + self.panel = Some(panel); } + } else if let Some(panel) = self.panel.take() { + let ui = editor.engine.user_interfaces.first(); + ui.send_message(DockingManagerMessage::remove_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + panel.window, + )); + panel.destroy(ui); } } } diff --git a/editor/src/physics/mod.rs b/editor/src/plugins/collider/panel.rs similarity index 76% rename from editor/src/physics/mod.rs rename to editor/src/plugins/collider/panel.rs index 3960daea3..6f3e1154e 100644 --- a/editor/src/physics/mod.rs +++ b/editor/src/plugins/collider/panel.rs @@ -18,30 +18,28 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::command::{Command, CommandGroup, SetPropertyCommand}; -use crate::fyrox::graph::{BaseSceneGraph, SceneGraph}; -use crate::fyrox::{ - core::{algebra::Vector3, pool::Handle, reflect::Reflect}, - engine::Engine, - gui::{ - button::{ButtonBuilder, ButtonMessage}, - message::{MessageDirection, UiMessage}, - stack_panel::StackPanelBuilder, - utils::make_simple_tooltip, - widget::WidgetBuilder, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, HorizontalAlignment, Thickness, UiNode, VerticalAlignment, - }, - scene::{ - collider::{Collider, ColliderShape}, - node::Node, - }, -}; -use crate::scene::commands::GameSceneContext; use crate::{ + command::{Command, CommandGroup, SetPropertyCommand}, + fyrox::{ + core::{algebra::Vector3, pool::Handle, reflect::Reflect}, + engine::Engine, + graph::{BaseSceneGraph, SceneGraph}, + gui::{ + button::{ButtonBuilder, ButtonMessage}, + message::{MessageDirection, UiMessage}, + stack_panel::StackPanelBuilder, + utils::make_simple_tooltip, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, HorizontalAlignment, Thickness, UiNode, UserInterface, VerticalAlignment, + }, + scene::{ + collider::{Collider, ColliderShape}, + node::Node, + }, + }, message::MessageSender, - scene::{GameScene, Selection}, - Message, + scene::{commands::GameSceneContext, GameScene, Selection}, }; pub struct ColliderControlPanel { @@ -81,6 +79,7 @@ impl ColliderControlPanel { .with_height(50.0) .with_name("ColliderControlPanel"), ) + .open(false) .with_title(WindowTitle::text("Collider Control Panel")) .with_content( StackPanelBuilder::new( @@ -109,49 +108,24 @@ impl ColliderControlPanel { } } - pub fn handle_message( - &self, - message: &Message, - engine: &Engine, - game_scene: &GameScene, - selection: &Selection, - ) { - if let Message::SelectionChanged { .. } = message { - let mut collider_selected = false; + pub fn open(&self, ui: &UserInterface) { + ui.send_message(WindowMessage::open_and_align( + self.window, + MessageDirection::ToWidget, + self.scene_frame, + HorizontalAlignment::Right, + VerticalAlignment::Top, + Thickness::uniform(1.0), + false, + false, + )); + } - if let Some(selection) = selection.as_graph() { - for selected in selection.nodes() { - let scene = &engine.scenes[game_scene.scene]; - - collider_selected = - scene.graph.try_get_of_type::(*selected).is_some(); - } - } - - if collider_selected { - engine - .user_interfaces - .first() - .send_message(WindowMessage::open_and_align( - self.window, - MessageDirection::ToWidget, - self.scene_frame, - HorizontalAlignment::Right, - VerticalAlignment::Top, - Thickness::uniform(1.0), - false, - false, - )); - } else { - engine - .user_interfaces - .first() - .send_message(WindowMessage::close( - self.window, - MessageDirection::ToWidget, - )); - } - } + pub fn destroy(self, ui: &UserInterface) { + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); } pub fn handle_ui_message( diff --git a/editor/src/curve_editor.rs b/editor/src/plugins/curve_editor.rs similarity index 83% rename from editor/src/curve_editor.rs rename to editor/src/plugins/curve_editor.rs index 49a9a83c3..0433ca919 100644 --- a/editor/src/curve_editor.rs +++ b/editor/src/plugins/curve_editor.rs @@ -18,36 +18,39 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::{ - asset::{untyped::ResourceKind, Resource}, - core::{ - color::Color, futures::executor::block_on, math::curve::Curve, pool::Handle, - type_traits::prelude::*, visitor::prelude::*, - }, - engine::Engine, - gui::{ - border::BorderBuilder, - brush::Brush, - button::{ButtonBuilder, ButtonMessage}, - curve::{CurveEditorBuilder, CurveEditorMessage}, - file_browser::{FileBrowserMode, FileSelectorMessage}, - grid::{Column, GridBuilder, Row}, - menu::{MenuBuilder, MenuItemBuilder, MenuItemContent, MenuItemMessage}, - message::{MessageDirection, UiMessage}, - messagebox::{MessageBoxBuilder, MessageBoxResult}, - stack_panel::StackPanelBuilder, - widget::{WidgetBuilder, WidgetMessage}, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - }, - resource::curve::{CurveResource, CurveResourceState}, -}; use crate::{ command::{Command, CommandContext, CommandStack, CommandTrait}, + fyrox::{ + asset::{untyped::ResourceKind, Resource}, + core::{ + color::Color, futures::executor::block_on, math::curve::Curve, pool::Handle, + type_traits::prelude::*, visitor::prelude::*, + }, + engine::Engine, + gui::{ + border::BorderBuilder, + brush::Brush, + button::{ButtonBuilder, ButtonMessage}, + curve::{CurveEditorBuilder, CurveEditorMessage}, + file_browser::{FileBrowserMode, FileSelectorMessage}, + grid::{Column, GridBuilder, Row}, + menu::{MenuBuilder, MenuItemBuilder, MenuItemContent, MenuItemMessage}, + message::{MessageDirection, UiMessage}, + messagebox::{MessageBoxBuilder, MessageBoxResult}, + stack_panel::StackPanelBuilder, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, + }, + resource::curve::{CurveResource, CurveResourceState}, + }, + menu::create_menu_item, + plugin::EditorPlugin, send_sync_message, utils::create_file_selector, - MessageBoxButtons, MessageBoxMessage, MSG_SYNC_FLAG, + Editor, MessageBoxButtons, MessageBoxMessage, MSG_SYNC_FLAG, }; +use fyrox::core::some_or_return; use std::{fmt::Debug, path::PathBuf}; #[derive(Debug, ComponentProvider)] @@ -277,6 +280,7 @@ impl CurveEditorWindow { .add_column(Column::stretch()) .build(ctx), ) + .with_remove_on_close(true) .with_title(WindowTitle::text("Curve Editor")) .build(ctx); @@ -301,9 +305,23 @@ impl CurveEditorWindow { } } - fn close(&mut self, ui: &UserInterface) { - self.clear(ui); - + fn destroy(self, ui: &UserInterface) { + ui.send_message(WidgetMessage::remove( + self.cancel_message_box, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.save_changes_message_box, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.load_file_selector, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.save_file_selector, + MessageDirection::ToWidget, + )); ui.send_message(WindowMessage::close( self.window, MessageDirection::ToWidget, @@ -381,27 +399,6 @@ impl CurveEditorWindow { )); } - fn clear(&mut self, ui: &UserInterface) { - self.path = Default::default(); - self.backup = Default::default(); - self.command_stack.clear(&mut CurveEditorContext {}); - self.curve_resource = None; - self.sync_title(ui); - ui.send_message(WidgetMessage::enabled( - self.curve_editor, - MessageDirection::ToWidget, - false, - )); - send_sync_message( - ui, - CurveEditorMessage::sync( - self.curve_editor, - MessageDirection::ToWidget, - Default::default(), - ), - ); - } - fn revert(&self) { if let Some(curve_resource) = self.curve_resource.as_ref() { curve_resource.data_ref().curve = self.backup.clone(); @@ -423,7 +420,7 @@ impl CurveEditorWindow { )); } - pub fn handle_ui_message(&mut self, message: &UiMessage, engine: &mut Engine) { + pub fn handle_ui_message(mut self, message: &UiMessage, engine: &mut Engine) -> Option { let ui = &engine.user_interfaces.first_mut(); if let Some(ButtonMessage::Click) = message.data() { @@ -436,7 +433,8 @@ impl CurveEditorWindow { None, )); } else { - self.close(ui); + self.destroy(ui); + return None; } } else if message.destination() == self.ok { if self.modified && self.curve_resource.is_some() { @@ -449,10 +447,12 @@ impl CurveEditorWindow { )); } else { self.save(); - self.close(ui); + self.destroy(ui); + return None; } } else { - self.close(ui); + self.destroy(ui); + return None; } } } else if let Some(CurveEditorMessage::Sync(curve)) = message.data() { @@ -525,14 +525,16 @@ impl CurveEditorWindow { match result { MessageBoxResult::No => { self.revert(); - self.close(ui); + self.destroy(ui); + return None; } MessageBoxResult::Yes => { if self.path == PathBuf::default() { self.open_save_file_dialog(ui); } else { self.save(); - self.close(ui); + self.destroy(ui); + return None; } } _ => (), @@ -540,9 +542,52 @@ impl CurveEditorWindow { } else if message.destination() == self.cancel_message_box { if let MessageBoxResult::Yes = result { self.revert(); - self.close(ui); + self.destroy(ui); + return None; } } } + + Some(self) + } +} + +#[derive(Default)] +pub struct CurveEditorPlugin { + curve_editor_window: Option, + open_curve_editor: Handle, +} + +impl CurveEditorPlugin { + fn on_open_curve_editor_clicked(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + let curve_editor = self + .curve_editor_window + .get_or_insert_with(|| CurveEditorWindow::new(ctx)); + curve_editor.open(ui); + } +} + +impl EditorPlugin for CurveEditorPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + self.open_curve_editor = create_menu_item("Curve Editor", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_curve_editor, + )); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_curve_editor { + self.on_open_curve_editor_clicked(editor) + } + } + let curve_editor = some_or_return!(self.curve_editor_window.take()); + self.curve_editor_window = curve_editor.handle_ui_message(message, &mut editor.engine); } } diff --git a/editor/src/inspector/editors/animation.rs b/editor/src/plugins/inspector/editors/animation.rs similarity index 98% rename from editor/src/inspector/editors/animation.rs rename to editor/src/plugins/inspector/editors/animation.rs index 0ac124a0d..cb2558216 100644 --- a/editor/src/inspector/editors/animation.rs +++ b/editor/src/plugins/inspector/editors/animation.rs @@ -38,7 +38,8 @@ use crate::fyrox::{ widget::WidgetBuilder, }, }; -use crate::{gui::make_dropdown_list_option_universal, inspector::EditorEnvironment, Message}; +use crate::plugins::inspector::EditorEnvironment; +use crate::{gui::make_dropdown_list_option_universal, Message}; use std::{ any::TypeId, fmt::{Debug, Formatter}, diff --git a/editor/src/inspector/editors/font.rs b/editor/src/plugins/inspector/editors/font.rs similarity index 96% rename from editor/src/inspector/editors/font.rs rename to editor/src/plugins/inspector/editors/font.rs index 18ea82cbb..4861dd797 100644 --- a/editor/src/inspector/editors/font.rs +++ b/editor/src/plugins/inspector/editors/font.rs @@ -184,7 +184,7 @@ impl FontFieldBuilder { .build(ctx); text_preview }) - .build(); + .build(ctx); let editor = FontField { widget, @@ -245,3 +245,14 @@ impl PropertyEditorDefinition for FontPropertyEditorDefinition { None } } + +#[cfg(test)] +mod test { + use crate::plugins::inspector::editors::font::FontFieldBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| FontFieldBuilder::new(WidgetBuilder::new()).build(ctx)); + } +} diff --git a/editor/src/inspector/editors/handle.rs b/editor/src/plugins/inspector/editors/handle.rs similarity index 94% rename from editor/src/inspector/editors/handle.rs rename to editor/src/plugins/inspector/editors/handle.rs index e68f73e7c..046d0526a 100644 --- a/editor/src/inspector/editors/handle.rs +++ b/editor/src/plugins/inspector/editors/handle.rs @@ -48,12 +48,14 @@ use crate::fyrox::{ scene::node::Node, }; use crate::{ - load_image, + load_image_internal, message::MessageSender, scene::selector::{HierarchyNode, NodeSelectorMessage, NodeSelectorWindowBuilder}, world::graph::item::SceneItem, Message, UiMessage, UiNode, UserInterface, VerticalAlignment, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ any::TypeId, fmt::Debug, @@ -170,14 +172,14 @@ impl Control for HandlePropertyEditor { )); let color = if self.value.is_none() { - Color::ORANGE + Brush::Solid(Color::ORANGE) } else { - fyrox::gui::COLOR_FOREGROUND + ui.style.get_or_default(Style::BRUSH_FOREGROUND) }; ui.send_message(WidgetMessage::foreground( self.text, MessageDirection::ToWidget, - Brush::Solid(color), + color, )); } else { ui.send_message(TextMessage::text( @@ -292,7 +294,7 @@ fn make_icon(data: &[u8], color: Color, ctx: &mut BuildContext) -> Handle, handle: Era handle, }); } + +#[cfg(test)] +mod test { + use crate::plugins::inspector::editors::handle::HandlePropertyEditorBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + HandlePropertyEditorBuilder::new(WidgetBuilder::new(), Default::default()).build(ctx) + }); + } +} diff --git a/editor/src/inspector/editors/mod.rs b/editor/src/plugins/inspector/editors/mod.rs similarity index 94% rename from editor/src/inspector/editors/mod.rs rename to editor/src/plugins/inspector/editors/mod.rs index 69b81e18f..22ae2a857 100644 --- a/editor/src/inspector/editors/mod.rs +++ b/editor/src/plugins/inspector/editors/mod.rs @@ -18,13 +18,23 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::inspector::editors::{ + animation::{ + AnimationContainerPropertyEditorDefinition, AnimationPropertyEditorDefinition, + MachinePropertyEditorDefinition, + }, + font::FontPropertyEditorDefinition, + handle::{EntityKind, NodeHandlePropertyEditorDefinition}, + resource::ResourceFieldPropertyEditorDefinition, + script::ScriptPropertyEditorDefinition, + spritesheet::SpriteSheetFramesContainerEditorDefinition, + surface::SurfaceDataPropertyEditorDefinition, + texture::TexturePropertyEditorDefinition, +}; use crate::{ fyrox::{ asset::{untyped::UntypedResource, Resource}, - core::{ - parking_lot::Mutex, - pool::{ErasedHandle, Handle}, - }, + core::pool::{ErasedHandle, Handle}, gui::{ self, font::FontResource, @@ -38,10 +48,7 @@ use crate::{ }, UiNode, UserInterface, }, - material::{ - shader::{Shader, ShaderResource}, - MaterialResource, - }, + material::shader::{Shader, ShaderResource}, renderer::framework::PolygonFillMode, resource::{ curve::{CurveResource, CurveResourceState}, @@ -77,6 +84,7 @@ use crate::{ BatchingMode, RenderPath, }, node::Node, + particle_system::CoordinateSystem, particle_system::{ emitter::{ base::BaseEmitter, cuboid::CuboidEmitter, cylinder::CylinderEmitter, @@ -97,34 +105,18 @@ use crate::{ SoundBufferResource, Status, }, terrain::{Chunk, Layer}, + tilemap::brush::{TileMapBrush, TileMapBrushResource}, + tilemap::tileset::TileCollider, tilemap::{tileset::TileSet, Tile}, transform::Transform, }, }, - inspector::editors::{ - animation::{ - AnimationContainerPropertyEditorDefinition, AnimationPropertyEditorDefinition, - MachinePropertyEditorDefinition, - }, - font::FontPropertyEditorDefinition, - handle::{EntityKind, NodeHandlePropertyEditorDefinition}, - material::MaterialPropertyEditorDefinition, - resource::ResourceFieldPropertyEditorDefinition, - script::ScriptPropertyEditorDefinition, - spritesheet::SpriteSheetFramesContainerEditorDefinition, - surface::SurfaceDataPropertyEditorDefinition, - texture::TexturePropertyEditorDefinition, - }, message::MessageSender, }; -use fyrox::scene::particle_system::CoordinateSystem; -use fyrox::scene::tilemap::brush::{TileMapBrush, TileMapBrushResource}; -use fyrox::scene::tilemap::tileset::TileCollider; pub mod animation; pub mod font; pub mod handle; -pub mod material; pub mod resource; pub mod script; pub mod spritesheet; @@ -228,11 +220,6 @@ pub fn make_property_editors_container(sender: MessageSender) -> PropertyEditorD container.register_inheritable_vec_collection::>(); container.register_inheritable_vec_collection::>(); - container.insert(MaterialPropertyEditorDefinition { - sender: Mutex::new(sender.clone()), - }); - container.insert(InheritablePropertyEditorDefinition::::new()); - container.insert(InheritablePropertyEditorDefinition::>::new()); container.register_inheritable_vec_collection::>(); container.insert(NodeHandlePropertyEditorDefinition::new( diff --git a/editor/src/inspector/editors/resource.rs b/editor/src/plugins/inspector/editors/resource.rs similarity index 93% rename from editor/src/inspector/editors/resource.rs rename to editor/src/plugins/inspector/editors/resource.rs index 3b3e2213a..39e792a35 100644 --- a/editor/src/inspector/editors/resource.rs +++ b/editor/src/plugins/inspector/editors/resource.rs @@ -45,10 +45,8 @@ use crate::fyrox::{ BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment, }, }; -use crate::{ - asset::item::AssetItem, inspector::EditorEnvironment, load_image, message::MessageSender, - Message, -}; +use crate::plugins::inspector::EditorEnvironment; +use crate::{asset::item::AssetItem, load_image, message::MessageSender, Message}; use fyrox::core::PhantomDataSendSync; use std::{ any::TypeId, @@ -286,9 +284,9 @@ where .with_height(16.0) .with_margin(Thickness::uniform(1.0)), ) - .with_opt_texture(load_image(include_bytes!( - "../../../resources/sound_source.png" - ))) + .with_opt_texture(load_image!( + "../../../../resources/sound_source.png" + )) .build(ctx), ) .with_child({ @@ -321,7 +319,7 @@ where .build(ctx), ) .with_allow_drop(true) - .build(), + .build(ctx), name, resource_manager, resource: self.resource, @@ -421,3 +419,20 @@ where None } } + +#[cfg(test)] +mod test { + + use crate::plugins::inspector::editors::resource::ResourceFieldBuilder; + use fyrox::asset::manager::ResourceManager; + use fyrox::resource::model::Model; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + ResourceFieldBuilder::::new(WidgetBuilder::new(), Default::default()) + .build(ctx, ResourceManager::new(Default::default())) + }); + } +} diff --git a/editor/src/inspector/editors/script.rs b/editor/src/plugins/inspector/editors/script.rs similarity index 99% rename from editor/src/inspector/editors/script.rs rename to editor/src/plugins/inspector/editors/script.rs index b86926311..84af8d5f0 100644 --- a/editor/src/inspector/editors/script.rs +++ b/editor/src/plugins/inspector/editors/script.rs @@ -48,9 +48,9 @@ use crate::fyrox::{ }, script::Script, }; +use crate::plugins::inspector::EditorEnvironment; use crate::{ gui::make_dropdown_list_option, - inspector::EditorEnvironment, send_sync_message, settings::{general::ScriptEditor, SettingsData}, DropdownListBuilder, MSG_SYNC_FLAG, @@ -265,7 +265,7 @@ impl ScriptPropertyEditorBuilder { .widget_builder .with_preview_messages(true) .with_child(inspector) - .build(), + .build(ctx), selected_script_uuid: script_uuid, variant_selector, open_in_ide_button, diff --git a/editor/src/inspector/editors/spritesheet/mod.rs b/editor/src/plugins/inspector/editors/spritesheet/mod.rs similarity index 98% rename from editor/src/inspector/editors/spritesheet/mod.rs rename to editor/src/plugins/inspector/editors/spritesheet/mod.rs index 1a8f324e4..e01076618 100644 --- a/editor/src/inspector/editors/spritesheet/mod.rs +++ b/editor/src/plugins/inspector/editors/spritesheet/mod.rs @@ -42,7 +42,7 @@ use crate::fyrox::{ }, scene::animation::spritesheet::prelude::*, }; -use crate::inspector::editors::spritesheet::window::SpriteSheetFramesEditorWindow; +use crate::plugins::inspector::editors::spritesheet::window::SpriteSheetFramesEditorWindow; use std::{ any::TypeId, ops::{Deref, DerefMut}, @@ -134,7 +134,7 @@ impl SpriteSheetFramesPropertyEditor { .build(ctx); ctx.add_node(UiNode::new(Self { - widget: WidgetBuilder::new().with_child(grid).build(), + widget: WidgetBuilder::new().with_child(grid).build(ctx), edit_button, container, })) diff --git a/editor/src/inspector/editors/spritesheet/window.rs b/editor/src/plugins/inspector/editors/spritesheet/window.rs similarity index 99% rename from editor/src/inspector/editors/spritesheet/window.rs rename to editor/src/plugins/inspector/editors/spritesheet/window.rs index c7fa6c136..0ccb41aef 100644 --- a/editor/src/inspector/editors/spritesheet/window.rs +++ b/editor/src/plugins/inspector/editors/spritesheet/window.rs @@ -44,7 +44,7 @@ use crate::fyrox::{ }, scene::animation::spritesheet::prelude::*, }; -use crate::inspector::editors::spritesheet::SpriteSheetFramesPropertyEditorMessage; +use crate::plugins::inspector::editors::spritesheet::SpriteSheetFramesPropertyEditorMessage; use std::{ ops::{Deref, DerefMut}, sync::{mpsc::Sender, Arc}, diff --git a/editor/src/inspector/editors/surface.rs b/editor/src/plugins/inspector/editors/surface.rs similarity index 99% rename from editor/src/inspector/editors/surface.rs rename to editor/src/plugins/inspector/editors/surface.rs index 51ba50ec4..1ef8554ee 100644 --- a/editor/src/inspector/editors/surface.rs +++ b/editor/src/plugins/inspector/editors/surface.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::inspector::EditorEnvironment; use crate::{ asset::item::AssetItem, fyrox::{ @@ -45,7 +46,6 @@ use crate::{ }, scene::mesh::surface::{SurfaceData, SurfaceResource}, }, - inspector::EditorEnvironment, message::MessageSender, Message, }; @@ -185,7 +185,7 @@ impl SurfaceDataPropertyEditor { .build(ctx), ) .with_allow_drop(true) - .build(); + .build(ctx); let editor = Self { widget, diff --git a/editor/src/inspector/editors/texture.rs b/editor/src/plugins/inspector/editors/texture.rs similarity index 98% rename from editor/src/inspector/editors/texture.rs rename to editor/src/plugins/inspector/editors/texture.rs index 1a31d862b..1733460ec 100644 --- a/editor/src/inspector/editors/texture.rs +++ b/editor/src/plugins/inspector/editors/texture.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::asset::item::AssetItem; use crate::fyrox::graph::BaseSceneGraph; use crate::fyrox::{ asset::manager::ResourceManager, @@ -42,7 +43,7 @@ use crate::fyrox::{ }, resource::texture::{Texture, TextureResource}, }; -use crate::{asset::item::AssetItem, inspector::EditorEnvironment}; +use crate::plugins::inspector::EditorEnvironment; use std::{ any::TypeId, fmt::{Debug, Formatter}, @@ -161,7 +162,7 @@ impl TextureEditorBuilder { .build(ctx); image }) - .build(); + .build(ctx); let editor = TextureEditor { widget, diff --git a/editor/src/inspector/handlers/mod.rs b/editor/src/plugins/inspector/handlers/mod.rs similarity index 100% rename from editor/src/inspector/handlers/mod.rs rename to editor/src/plugins/inspector/handlers/mod.rs diff --git a/editor/src/inspector/handlers/node/mod.rs b/editor/src/plugins/inspector/handlers/node/mod.rs similarity index 100% rename from editor/src/inspector/handlers/node/mod.rs rename to editor/src/plugins/inspector/handlers/node/mod.rs diff --git a/editor/src/inspector/mod.rs b/editor/src/plugins/inspector/mod.rs similarity index 98% rename from editor/src/inspector/mod.rs rename to editor/src/plugins/inspector/mod.rs index 81ec94266..6a7a384d5 100644 --- a/editor/src/inspector/mod.rs +++ b/editor/src/plugins/inspector/mod.rs @@ -18,8 +18,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::absm::animation_container_ref; +use crate::plugins::inspector::editors::make_property_editors_container; use crate::{ - absm::animation_container_ref, fyrox::{ asset::manager::ResourceManager, core::{ @@ -47,7 +48,6 @@ use crate::{ scene::SceneContainer, }, gui::make_image_button_with_tooltip, - inspector::editors::make_property_editors_container, load_image, message::MessageSender, plugin::EditorPlugin, @@ -207,7 +207,7 @@ impl InspectorPlugin { ctx, 18.0, 18.0, - load_image(include_bytes!("../../resources/doc.png")), + load_image!("../../../resources/doc.png"), "Open Documentation", Some(0), ); diff --git a/editor/src/inspector/editors/material.rs b/editor/src/plugins/material/editor.rs similarity index 96% rename from editor/src/inspector/editors/material.rs rename to editor/src/plugins/material/editor.rs index 1e8d74e63..070128cde 100644 --- a/editor/src/inspector/editors/material.rs +++ b/editor/src/plugins/material/editor.rs @@ -248,7 +248,7 @@ impl MaterialFieldEditorBuilder { .add_column(Column::auto()) .build(ctx), ) - .build(), + .build(ctx), edit, sender, material, @@ -309,3 +309,20 @@ impl PropertyEditorDefinition for MaterialPropertyEditorDefinition { None } } + +#[cfg(test)] +mod test { + use crate::plugins::material::editor::MaterialFieldEditorBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| { + MaterialFieldEditorBuilder::new(WidgetBuilder::new()).build( + ctx, + Default::default(), + Default::default(), + ) + }); + } +} diff --git a/editor/src/material.rs b/editor/src/plugins/material/mod.rs similarity index 55% rename from editor/src/material.rs rename to editor/src/plugins/material/mod.rs index cc3484e14..340fc5eaf 100644 --- a/editor/src/material.rs +++ b/editor/src/plugins/material/mod.rs @@ -18,15 +18,20 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::inspector::{ + editors::resource::{ResourceFieldBuilder, ResourceFieldMessage}, + InspectorPlugin, +}; use crate::{ asset::item::AssetItem, fyrox::{ asset::untyped::ResourceKind, core::{ - algebra::{Matrix4, SMatrix, Vector2, Vector3, Vector4}, + algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4}, color::Color, parking_lot::Mutex, pool::Handle, + some_or_continue, some_or_return, sstorage::ImmutableString, }, fxhash::FxHashMap, @@ -35,8 +40,10 @@ use crate::{ border::BorderBuilder, check_box::{CheckBoxBuilder, CheckBoxMessage}, color::{ColorFieldBuilder, ColorFieldMessage}, + dock::DockingManagerMessage, grid::{Column, GridBuilder, Row}, image::{Image, ImageBuilder, ImageMessage}, + inspector::editors::inherit::InheritablePropertyEditorDefinition, list_view::{ListView, ListViewBuilder, ListViewMessage}, matrix::{MatrixEditorBuilder, MatrixEditorMessage}, menu::{ContextMenuBuilder, MenuItemBuilder, MenuItemContent, MenuItemMessage}, @@ -46,17 +53,15 @@ use crate::{ scroll_viewer::ScrollViewerBuilder, stack_panel::StackPanelBuilder, text::TextBuilder, - vec::{ - Vec2EditorBuilder, Vec2EditorMessage, Vec3EditorBuilder, Vec3EditorMessage, - Vec4EditorBuilder, Vec4EditorMessage, - }, + vec::{Vec2EditorMessage, Vec3EditorMessage, Vec4EditorMessage}, + vec::{VecEditorBuilder, VecEditorMessage}, widget::{WidgetBuilder, WidgetMessage}, - window::{WindowBuilder, WindowTitle}, + window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, RcUiNodeHandle, Thickness, UiNode, UserInterface, VerticalAlignment, }, material::{ shader::{Shader, ShaderResourceKind}, - MaterialProperty, MaterialResource, MaterialResourceBinding, + MaterialProperty, MaterialResource, MaterialResourceBinding, MaterialTextureBinding, }, renderer::framework::gpu_program::{ShaderProperty, ShaderPropertyKind}, resource::texture::Texture, @@ -68,18 +73,20 @@ use crate::{ }, }, }, - inspector::editors::resource::{ResourceFieldBuilder, ResourceFieldMessage}, message::MessageSender, + plugin::EditorPlugin, + plugins::material::editor::MaterialPropertyEditorDefinition, preview::PreviewPanel, scene::commands::material::{ SetMaterialBindingCommand, SetMaterialPropertyGroupPropertyValueCommand, SetMaterialShaderCommand, }, - send_sync_message, Engine, Message, + send_sync_message, Editor, Engine, Message, }; -use fyrox::material::MaterialTextureBinding; use std::sync::Arc; +pub mod editor; + struct TextureContextMenu { popup: RcUiNodeHandle, show_in_asset_browser: Handle, @@ -147,11 +154,7 @@ pub struct MaterialEditor { texture_context_menu: TextureContextMenu, } -fn create_item_container( - ctx: &mut BuildContext, - name: &str, - item: Handle, -) -> Handle { +fn make_item_container(ctx: &mut BuildContext, name: &str, item: Handle) -> Handle { ctx[item].set_column(1); GridBuilder::new( @@ -171,79 +174,7 @@ fn create_item_container( .build(ctx) } -fn create_array_view( - ctx: &mut BuildContext, - value: &[T], - mut item_builder: B, -) -> Handle -where - T: Clone, - B: FnMut(&mut BuildContext, T) -> Handle, -{ - ListViewBuilder::new(WidgetBuilder::new()) - .with_items(value.iter().map(|v| item_builder(ctx, v.clone())).collect()) - .build(ctx) -} - -fn create_float_view(ctx: &mut BuildContext, value: f32) -> Handle { - NumericUpDownBuilder::::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .build(ctx) -} - -fn create_int_view(ctx: &mut BuildContext, value: i32) -> Handle { - NumericUpDownBuilder::::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .with_precision(0) - .build(ctx) -} - -fn create_uint_view(ctx: &mut BuildContext, value: u32) -> Handle { - NumericUpDownBuilder::::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .with_precision(0) - .build(ctx) -} - -fn create_vec2_view(ctx: &mut BuildContext, value: Vector2) -> Handle { - Vec2EditorBuilder::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .build(ctx) -} - -fn create_vec3_view(ctx: &mut BuildContext, value: Vector3) -> Handle { - Vec3EditorBuilder::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .build(ctx) -} - -fn create_vec4_view(ctx: &mut BuildContext, value: Vector4) -> Handle { - Vec4EditorBuilder::new(WidgetBuilder::new().with_height(24.0)) - .with_value(value) - .build(ctx) -} - -fn create_mat_view( - ctx: &mut BuildContext, - value: SMatrix, -) -> Handle { - MatrixEditorBuilder::::new(WidgetBuilder::new()) - .with_value(value) - .build(ctx) -} - -fn sync_array(ui: &UserInterface, handle: Handle, array: &[T], mut message_builder: B) -where - T: Clone, - B: FnMut(&T, Handle) -> UiMessage, -{ - let views = &**ui.try_get_of_type::(handle).unwrap().items; - for (item, view) in array.iter().zip(views) { - send_sync_message(ui, message_builder(item, *view)) - } -} - -fn pad_vec(v: &[T], max_len: usize) -> Vec { +fn pad_vec(v: &[T], max_len: usize) -> Vec { let mut vec = v.to_vec(); for _ in v.len()..max_len { vec.push(T::default()); @@ -251,6 +182,102 @@ fn pad_vec(v: &[T], max_len: usize) -> Vec { vec } +fn make_array_view( + ctx: &mut BuildContext, + value: &[impl UiView], + max_len: usize, +) -> Handle { + let value = pad_vec(value, max_len); + ListViewBuilder::new(WidgetBuilder::new()) + .with_items(value.into_iter().map(|v| v.make_view(ctx)).collect()) + .build(ctx) +} + +fn sync_array(ui: &UserInterface, handle: Handle, array: &[impl UiView]) { + let views = &**ui.try_get_of_type::(handle).unwrap().items; + for (item, view) in array.iter().zip(views) { + send_sync_message(ui, item.into_message(*view)) + } +} + +trait UiView: Default + Copy { + fn into_message(self, item: Handle) -> UiMessage; + fn make_view(self, ctx: &mut BuildContext) -> Handle; + fn send(self, ui: &UserInterface, item: Handle) { + send_sync_message(ui, self.into_message(item)) + } +} + +macro_rules! numeric_ui_view { + ($($ty:ty),*) => { + $(impl UiView for $ty { + fn into_message(self, item: Handle) -> UiMessage { + NumericUpDownMessage::value(item, MessageDirection::ToWidget, self) + } + fn make_view(self, ctx: &mut BuildContext) -> Handle { + NumericUpDownBuilder::new(WidgetBuilder::new().with_height(24.0)) + .with_value(self) + .build(ctx) + } + })* + }; +} +numeric_ui_view!(f32, u32, i32); + +macro_rules! vec_ui_view { + ($($ty:ty),*) => { + $(impl UiView for $ty { + fn into_message(self, item: Handle) -> UiMessage { + VecEditorMessage::value(item, MessageDirection::ToWidget, self) + } + fn make_view(self, ctx: &mut BuildContext) -> Handle { + VecEditorBuilder::new(WidgetBuilder::new().with_height(24.0)) + .with_value(self) + .build(ctx) + } + })* + }; +} +vec_ui_view!(Vector2, Vector3, Vector4); + +macro_rules! mat_ui_view { + ($($ty:ty),*) => { + $(impl UiView for $ty { + fn into_message(self, item: Handle) -> UiMessage { + MatrixEditorMessage::value(item, MessageDirection::ToWidget, self) + } + fn make_view(self, ctx: &mut BuildContext) -> Handle { + MatrixEditorBuilder::new(WidgetBuilder::new()) + .with_value(self) + .build(ctx) + } + })* + }; +} +mat_ui_view!(Matrix2, Matrix3, Matrix4); + +impl UiView for bool { + fn into_message(self, item: Handle) -> UiMessage { + CheckBoxMessage::checked(item, MessageDirection::ToWidget, Some(self)) + } + fn make_view(self, ctx: &mut BuildContext) -> Handle { + CheckBoxBuilder::new(WidgetBuilder::new()) + .checked(Some(self)) + .build(ctx) + } +} + +impl UiView for Color { + fn into_message(self, item: Handle) -> UiMessage { + ColorFieldMessage::color(item, MessageDirection::ToWidget, self) + } + fn make_view(self, ctx: &mut BuildContext) -> Handle { + ColorFieldBuilder::new(WidgetBuilder::new()) + .with_color(self) + .build(ctx) + } +} + impl MaterialEditor { pub fn new(engine: &mut Engine, sender: MessageSender) -> Self { let mut preview = PreviewPanel::new(engine, 350, 400); @@ -338,6 +365,20 @@ impl MaterialEditor { } } + pub fn destroy(self, docking_manager: Handle, engine: &mut Engine) { + self.preview.destroy(engine); + let ui = engine.user_interfaces.first(); + ui.send_message(DockingManagerMessage::remove_floating_window( + docking_manager, + MessageDirection::ToWidget, + self.window, + )); + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); + } + pub fn set_material(&mut self, material: Option, engine: &mut Engine) { self.material = material; @@ -365,19 +406,13 @@ impl MaterialEditor { ); } - let Some(material) = self.material.clone() else { - return; - }; + let material = some_or_return!(self.material.clone()); let mut material_state = material.state(); - let Some(material) = material_state.data() else { - return; - }; + let material = some_or_return!(material_state.data()); let mut shader_state = material.shader().state(); - let Some(shader) = shader_state.data() else { - return; - }; + let shader = some_or_return!(shader_state.data()); for resource in shader.definition.resources.iter() { if resource.is_built_in() { @@ -396,7 +431,7 @@ impl MaterialEditor { .build(&mut ui.build_ctx()); ResourceView { name: resource.name.clone(), - container: create_item_container( + container: make_item_container( &mut ui.build_ctx(), resource.name.as_str(), editor, @@ -435,55 +470,34 @@ impl MaterialEditor { let property_containers = group .iter() .map(|property| { + use ShaderPropertyKind as Kind; let item = match &property.kind { - ShaderPropertyKind::Float(value) => create_float_view(ctx, *value), - ShaderPropertyKind::FloatArray { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_float_view) - } - ShaderPropertyKind::Int(value) => create_int_view(ctx, *value), - ShaderPropertyKind::IntArray { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_int_view) - } - ShaderPropertyKind::UInt(value) => create_uint_view(ctx, *value), - ShaderPropertyKind::UIntArray { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_uint_view) - } - ShaderPropertyKind::Vector2(value) => create_vec2_view(ctx, *value), - ShaderPropertyKind::Vector2Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_vec2_view) - } - ShaderPropertyKind::Vector3(value) => create_vec3_view(ctx, *value), - ShaderPropertyKind::Vector3Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_vec3_view) - } - ShaderPropertyKind::Vector4(value) => create_vec4_view(ctx, *value), - ShaderPropertyKind::Vector4Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_vec4_view) - } - ShaderPropertyKind::Matrix2(value) => create_mat_view(ctx, *value), - ShaderPropertyKind::Matrix2Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_mat_view) - } - ShaderPropertyKind::Matrix3(value) => create_mat_view(ctx, *value), - ShaderPropertyKind::Matrix3Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_mat_view) - } - ShaderPropertyKind::Matrix4(value) => create_mat_view(ctx, *value), - ShaderPropertyKind::Matrix4Array { value, max_len } => { - create_array_view(ctx, &pad_vec(value, *max_len), create_mat_view) - } - ShaderPropertyKind::Bool(value) => CheckBoxBuilder::new(WidgetBuilder::new()) - .checked(Some(*value)) + Kind::Float(value) => value.make_view(ctx), + Kind::FloatArray { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Int(value) => value.make_view(ctx), + Kind::IntArray { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::UInt(value) => value.make_view(ctx), + Kind::UIntArray { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Vector2(value) => value.make_view(ctx), + Kind::Vector2Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Vector3(value) => value.make_view(ctx), + Kind::Vector3Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Vector4(value) => value.make_view(ctx), + Kind::Vector4Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Matrix2(value) => value.make_view(ctx), + Kind::Matrix2Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Matrix3(value) => value.make_view(ctx), + Kind::Matrix3Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Matrix4(value) => value.make_view(ctx), + Kind::Matrix4Array { value, max_len } => make_array_view(ctx, value, *max_len), + Kind::Bool(value) => value.make_view(ctx), + Kind::Color { r, g, b, a } => ColorFieldBuilder::new(WidgetBuilder::new()) + .with_color(Color::from_rgba(*r, *g, *b, *a)) .build(ctx), - ShaderPropertyKind::Color { r, g, b, a } => { - ColorFieldBuilder::new(WidgetBuilder::new()) - .with_color(Color::from_rgba(*r, *g, *b, *a)) - .build(ctx) - } }; property_views.push(item); - create_item_container(ctx, &property.name, item) + make_item_container(ctx, &property.name, item) }) .collect::>(); @@ -493,7 +507,7 @@ impl MaterialEditor { .build(ctx); ResourceView { - container: create_item_container(ctx, name.as_str(), panel), + container: make_item_container(ctx, name.as_str(), panel), name, kind: ResourceViewKind::PropertyGroup { property_views: group @@ -522,14 +536,10 @@ impl MaterialEditor { }; let mut material_state = material.state(); - let Some(material) = material_state.data() else { - return; - }; + let material = some_or_return!(material_state.data()); for (binding_name, binding_value) in material.bindings() { - let Some(view) = self.find_resource_view(binding_name) else { - continue; - }; + let view = some_or_continue!(self.find_resource_view(binding_name)); match binding_value { MaterialResourceBinding::Texture(ref binding) => send_sync_message( ui, @@ -550,187 +560,40 @@ impl MaterialEditor { .unwrap_or_else(|| panic!("Property not found {}", property_name)); match property_value { - MaterialProperty::Float(value) => { - send_sync_message( - ui, - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value, - ), - ); - } - MaterialProperty::FloatArray(value) => { - sync_array(ui, item, value, |value, item| { - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Int(value) => { - send_sync_message( - ui, - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value as f32, - ), - ); - } - MaterialProperty::IntArray(value) => { - sync_array(ui, item, value, |value, item| { - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value as f32, - ) - }) - } - MaterialProperty::UInt(value) => { - send_sync_message( - ui, - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value as f32, - ), - ); - } - MaterialProperty::UIntArray(value) => { - sync_array(ui, item, value, |value, item| { - NumericUpDownMessage::value( - item, - MessageDirection::ToWidget, - *value as f32, - ) - }) - } - MaterialProperty::Vector2(value) => send_sync_message( - ui, - Vec2EditorMessage::value(item, MessageDirection::ToWidget, *value), - ), - MaterialProperty::Vector2Array(value) => { - sync_array(ui, item, value, |value, item| { - Vec2EditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Vector3(value) => send_sync_message( - ui, - Vec3EditorMessage::value(item, MessageDirection::ToWidget, *value), - ), - MaterialProperty::Vector3Array(value) => { - sync_array(ui, item, value, |value, item| { - Vec3EditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Vector4(value) => send_sync_message( - ui, - Vec4EditorMessage::value(item, MessageDirection::ToWidget, *value), - ), - MaterialProperty::Vector4Array(value) => { - sync_array(ui, item, value, |value, item| { - Vec4EditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Matrix2(value) => send_sync_message( - ui, - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ), - ), - MaterialProperty::Matrix2Array(value) => { - sync_array(ui, item, value, |value, item| { - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Matrix3(value) => send_sync_message( - ui, - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ), - ), - MaterialProperty::Matrix3Array(value) => { - sync_array(ui, item, value, |value, item| { - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Matrix4(value) => send_sync_message( - ui, - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ), - ), - MaterialProperty::Matrix4Array(value) => { - sync_array(ui, item, value, |value, item| { - MatrixEditorMessage::value( - item, - MessageDirection::ToWidget, - *value, - ) - }) - } - MaterialProperty::Bool(value) => { - send_sync_message( - ui, - CheckBoxMessage::checked( - item, - MessageDirection::ToWidget, - Some(*value), - ), - ); - } - MaterialProperty::Color(value) => { - send_sync_message( - ui, - ColorFieldMessage::color( - item, - MessageDirection::ToWidget, - *value, - ), - ); - } + MaterialProperty::Float(value) => value.send(ui, item), + MaterialProperty::FloatArray(value) => sync_array(ui, item, value), + MaterialProperty::Int(value) => value.send(ui, item), + MaterialProperty::IntArray(value) => sync_array(ui, item, value), + MaterialProperty::UInt(value) => value.send(ui, item), + MaterialProperty::UIntArray(value) => sync_array(ui, item, value), + MaterialProperty::Vector2(value) => value.send(ui, item), + MaterialProperty::Vector2Array(value) => sync_array(ui, item, value), + MaterialProperty::Vector3(value) => value.send(ui, item), + MaterialProperty::Vector3Array(value) => sync_array(ui, item, value), + MaterialProperty::Vector4(value) => value.send(ui, item), + MaterialProperty::Vector4Array(value) => sync_array(ui, item, value), + MaterialProperty::Matrix2(value) => value.send(ui, item), + MaterialProperty::Matrix2Array(value) => sync_array(ui, item, value), + MaterialProperty::Matrix3(value) => value.send(ui, item), + MaterialProperty::Matrix3Array(value) => sync_array(ui, item, value), + MaterialProperty::Matrix4(value) => value.send(ui, item), + MaterialProperty::Matrix4Array(value) => sync_array(ui, item, value), + MaterialProperty::Bool(value) => value.send(ui, item), + MaterialProperty::Color(value) => value.send(ui, item), } } - - send_sync_message( - ui, - ResourceFieldMessage::value( - self.shader, - MessageDirection::ToWidget, - Some(material.shader().clone()), - ), - ); } } } + + send_sync_message( + ui, + ResourceFieldMessage::value( + self.shader, + MessageDirection::ToWidget, + Some(material.shader().clone()), + ), + ); } pub fn handle_ui_message( @@ -739,9 +602,7 @@ impl MaterialEditor { engine: &mut Engine, sender: &MessageSender, ) { - let Some(material) = self.material.clone() else { - return; - }; + let material = some_or_return!(self.material.clone()); self.preview.handle_message(message, engine); @@ -756,13 +617,11 @@ impl MaterialEditor { )); } } - } else if let Some(PopupMessage::Placement(Placement::Cursor(target))) = - message.data::() - { + } else if let Some(PopupMessage::Placement(Placement::Cursor(target))) = message.data() { if message.destination() == self.texture_context_menu.popup.handle() { self.texture_context_menu.target = *target; } - } else if let Some(MenuItemMessage::Click) = message.data::() { + } else if let Some(MenuItemMessage::Click) = message.data() { if message.destination() == self.texture_context_menu.show_in_asset_browser && self.texture_context_menu.target.is_some() { @@ -800,7 +659,7 @@ impl MaterialEditor { for resource_view in self.resource_views.iter() { match resource_view.kind { ResourceViewKind::Sampler => { - if let Some(WidgetMessage::Drop(handle)) = message.data::() { + if let Some(WidgetMessage::Drop(handle)) = message.data() { if let Some(asset_item) = engine .user_interfaces .first_mut() @@ -834,50 +693,7 @@ impl MaterialEditor { if *property_view == message.destination() && message.direction() == MessageDirection::FromWidget { - let property_value = - if let Some(NumericUpDownMessage::::Value(value)) = - message.data() - { - Some(MaterialProperty::Float(*value)) - } else if let Some(NumericUpDownMessage::::Value(value)) = - message.data() - { - Some(MaterialProperty::Int(*value)) - } else if let Some(NumericUpDownMessage::::Value(value)) = - message.data() - { - Some(MaterialProperty::UInt(*value)) - } else if let Some(Vec2EditorMessage::Value(value)) = message.data() - { - Some(MaterialProperty::Vector2(*value)) - } else if let Some(Vec3EditorMessage::Value(value)) = message.data() - { - Some(MaterialProperty::Vector3(*value)) - } else if let Some(Vec4EditorMessage::Value(value)) = message.data() - { - Some(MaterialProperty::Vector4(*value)) - } else if let Some(ColorFieldMessage::Color(color)) = message.data() - { - Some(MaterialProperty::Color(*color)) - } else if let Some(MatrixEditorMessage::<2, 2, f32>::Value(value)) = - message.data() - { - Some(MaterialProperty::Matrix2(*value)) - } else if let Some(MatrixEditorMessage::<3, 3, f32>::Value(value)) = - message.data() - { - Some(MaterialProperty::Matrix3(*value)) - } else if let Some(MatrixEditorMessage::<4, 4, f32>::Value(value)) = - message.data() - { - Some(MaterialProperty::Matrix4(*value)) - } else if let Some(CheckBoxMessage::Check(Some(value))) = - message.data() - { - Some(MaterialProperty::Bool(*value)) - } else { - None - }; + let property_value = try_extract_message_value(message); if let Some(property_value) = property_value { sender.do_command( @@ -900,3 +716,98 @@ impl MaterialEditor { self.preview.update(engine) } } + +fn try_extract_message_value(message: &UiMessage) -> Option { + if let Some(NumericUpDownMessage::::Value(value)) = message.data() { + Some(MaterialProperty::Float(*value)) + } else if let Some(NumericUpDownMessage::::Value(value)) = message.data() { + Some(MaterialProperty::Int(*value)) + } else if let Some(NumericUpDownMessage::::Value(value)) = message.data() { + Some(MaterialProperty::UInt(*value)) + } else if let Some(Vec2EditorMessage::Value(value)) = message.data() { + Some(MaterialProperty::Vector2(*value)) + } else if let Some(Vec3EditorMessage::Value(value)) = message.data() { + Some(MaterialProperty::Vector3(*value)) + } else if let Some(Vec4EditorMessage::Value(value)) = message.data() { + Some(MaterialProperty::Vector4(*value)) + } else if let Some(ColorFieldMessage::Color(color)) = message.data() { + Some(MaterialProperty::Color(*color)) + } else if let Some(MatrixEditorMessage::<2, 2, f32>::Value(value)) = message.data() { + Some(MaterialProperty::Matrix2(*value)) + } else if let Some(MatrixEditorMessage::<3, 3, f32>::Value(value)) = message.data() { + Some(MaterialProperty::Matrix3(*value)) + } else if let Some(MatrixEditorMessage::<4, 4, f32>::Value(value)) = message.data() { + Some(MaterialProperty::Matrix4(*value)) + } else if let Some(CheckBoxMessage::Check(Some(value))) = message.data() { + Some(MaterialProperty::Bool(*value)) + } else { + None + } +} + +#[derive(Default)] +pub struct MaterialPlugin { + material_editor: Option, +} + +impl EditorPlugin for MaterialPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let container = &editor.plugins.get_mut::().property_editors; + container.insert(MaterialPropertyEditorDefinition { + sender: Mutex::new(editor.message_sender.clone()), + }); + container.insert(InheritablePropertyEditorDefinition::::new()); + } + + fn on_sync_to_model(&mut self, editor: &mut Editor) { + let material_editor = some_or_return!(self.material_editor.as_mut()); + material_editor.sync_to_model(editor.engine.user_interfaces.first_mut()); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + let mut material_editor = some_or_return!(self.material_editor.take()); + + material_editor.handle_ui_message(message, &mut editor.engine, &editor.message_sender); + + if let Some(WindowMessage::Close) = message.data() { + if message.destination() == material_editor.window { + material_editor.destroy(editor.docking_manager, &mut editor.engine); + return; + } + } + + self.material_editor = Some(material_editor); + } + + fn on_update(&mut self, editor: &mut Editor) { + let material_editor = some_or_return!(self.material_editor.as_mut()); + material_editor.update(&mut editor.engine); + } + + fn on_message(&mut self, message: &Message, editor: &mut Editor) { + let Message::OpenMaterialEditor(material) = message else { + return; + }; + + let engine = &mut editor.engine; + + let material_editor = self + .material_editor + .get_or_insert_with(|| MaterialEditor::new(engine, editor.message_sender.clone())); + + material_editor.set_material(Some(material.clone()), engine); + + let ui = engine.user_interfaces.first_mut(); + ui.send_message(WindowMessage::open( + material_editor.window, + MessageDirection::ToWidget, + true, + true, + )); + ui.send_message(DockingManagerMessage::add_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + material_editor.window, + )); + } +} diff --git a/editor/src/plugins/mod.rs b/editor/src/plugins/mod.rs index 43580492c..e73266e5a 100644 --- a/editor/src/plugins/mod.rs +++ b/editor/src/plugins/mod.rs @@ -18,5 +18,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +pub mod absm; +pub mod animation; pub mod collider; +pub mod curve_editor; +pub mod inspector; +pub mod material; +pub mod path_fixer; +pub mod ragdoll; +pub mod settings; +pub mod stats; pub mod tilemap; diff --git a/editor/src/utils/path_fixer.rs b/editor/src/plugins/path_fixer.rs similarity index 83% rename from editor/src/utils/path_fixer.rs rename to editor/src/plugins/path_fixer.rs index 810d7993d..3a2e24ca5 100644 --- a/editor/src/utils/path_fixer.rs +++ b/editor/src/plugins/path_fixer.rs @@ -21,34 +21,40 @@ //! Special utility that allows you to fix paths to resources. It is very useful if you've //! moved a resource in a file system, but a scene has old path. -use crate::fyrox::asset::untyped::ResourceKind; -use crate::fyrox::graph::{BaseSceneGraph, SceneGraph}; -use crate::fyrox::{ - asset::{manager::ResourceManager, untyped::UntypedResource}, - core::{ - color::Color, futures::executor::block_on, pool::Handle, replace_slashes, visitor::Visitor, +use crate::{ + fyrox::{ + asset::{manager::ResourceManager, untyped::ResourceKind, untyped::UntypedResource}, + core::{ + color::Color, futures::executor::block_on, pool::Handle, replace_slashes, + some_or_return, visitor::Visitor, + }, + engine::SerializationContext, + graph::{BaseSceneGraph, SceneGraph}, + gui::{ + border::BorderBuilder, + brush::Brush, + button::{ButtonBuilder, ButtonMessage}, + decorator::DecoratorBuilder, + file_browser::{FileSelectorBuilder, FileSelectorMessage}, + formatted_text::WrapMode, + grid::{Column, GridBuilder, Row}, + list_view::{ListView, ListViewBuilder, ListViewMessage}, + menu::MenuItemMessage, + message::{MessageDirection, UiMessage}, + stack_panel::StackPanelBuilder, + text::{Text, TextBuilder, TextMessage}, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, + VerticalAlignment, + }, + scene::{Scene, SceneLoader}, }, - engine::SerializationContext, - gui::{ - border::BorderBuilder, - brush::Brush, - button::{ButtonBuilder, ButtonMessage}, - decorator::DecoratorBuilder, - file_browser::{FileSelectorBuilder, FileSelectorMessage}, - formatted_text::WrapMode, - grid::{Column, GridBuilder, Row}, - list_view::{ListView, ListViewBuilder, ListViewMessage}, - message::{MessageDirection, UiMessage}, - stack_panel::StackPanelBuilder, - text::{Text, TextBuilder, TextMessage}, - widget::{WidgetBuilder, WidgetMessage}, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - VerticalAlignment, - }, - scene::{Scene, SceneLoader}, + make_scene_file_filter, + menu::create_menu_item, + plugin::EditorPlugin, + Editor, Message, }; -use crate::{make_scene_file_filter, Message}; use std::{ path::{Path, PathBuf}, sync::Arc, @@ -74,7 +80,10 @@ pub struct PathFixer { fn find_file(name: &Path) -> Vec { let mut files = Vec::new(); - for dir in fyrox::walkdir::WalkDir::new(".").into_iter().flatten() { + for dir in fyrox::walkdir::WalkDir::new("../../..") + .into_iter() + .flatten() + { let path = dir.path(); if let Some(file_name) = path.file_name() { if file_name == name { @@ -216,6 +225,7 @@ impl PathFixer { .add_column(Column::stretch()) .build(ctx), ) + .with_remove_on_close(true) .build(ctx); Self { @@ -269,13 +279,24 @@ impl PathFixer { )); } + fn destroy(self, ui: &UserInterface) { + ui.send_message(WidgetMessage::remove( + self.new_path_selector, + MessageDirection::ToWidget, + )); + ui.send_message(WidgetMessage::remove( + self.scene_selector, + MessageDirection::ToWidget, + )); + } + pub fn handle_ui_message( - &mut self, + mut self, message: &UiMessage, ui: &mut UserInterface, serialization_context: Arc, resource_manager: ResourceManager, - ) { + ) -> Option { if let Some(FileSelectorMessage::Commit(path)) = message.data::() { if message.destination() == self.scene_selector { let message; @@ -492,7 +513,14 @@ impl PathFixer { self.selection.is_some(), )); } + } else if let Some(WindowMessage::Close) = message.data() { + if message.destination() == self.window { + self.destroy(ui); + return None; + } } + + Some(self) } pub fn handle_message(&mut self, message: &Message, ui: &UserInterface) { @@ -509,4 +537,63 @@ impl PathFixer { )); } } + + fn open(&self, ui: &UserInterface) { + ui.send_message(WindowMessage::open_modal( + self.window, + MessageDirection::ToWidget, + true, + true, + )); + } +} + +#[derive(Default)] +pub struct PathFixerPlugin { + path_fixer: Option, + open_path_fixer: Handle, +} + +impl PathFixerPlugin { + fn on_open_curve_editor_clicked(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + let path_fixer = self.path_fixer.get_or_insert_with(|| PathFixer::new(ctx)); + path_fixer.open(ui); + } +} + +impl EditorPlugin for PathFixerPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + self.open_path_fixer = create_menu_item("Path Fixer", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_path_fixer, + )); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_path_fixer { + self.on_open_curve_editor_clicked(editor) + } + } + let path_fixer = some_or_return!(self.path_fixer.take()); + let ui = editor.engine.user_interfaces.first_mut(); + self.path_fixer = path_fixer.handle_ui_message( + message, + ui, + editor.engine.serialization_context.clone(), + editor.engine.resource_manager.clone(), + ); + } + + fn on_message(&mut self, message: &Message, editor: &mut Editor) { + let path_fixer = some_or_return!(self.path_fixer.as_mut()); + let ui = editor.engine.user_interfaces.first_mut(); + path_fixer.handle_message(message, ui); + } } diff --git a/editor/src/utils/ragdoll.rs b/editor/src/plugins/ragdoll.rs similarity index 92% rename from editor/src/utils/ragdoll.rs rename to editor/src/plugins/ragdoll.rs index c3aa313b6..2535beff7 100644 --- a/editor/src/utils/ragdoll.rs +++ b/editor/src/plugins/ragdoll.rs @@ -18,51 +18,54 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::command::{Command, CommandGroup}; -use crate::fyrox::graph::{BaseSceneGraph, SceneGraph}; -use crate::fyrox::{ - core::{ - algebra::{UnitQuaternion, Vector3}, - log::Log, - math::Matrix4Ext, - pool::Handle, - reflect::prelude::*, - }, - gui::{ - button::{ButtonBuilder, ButtonMessage}, - grid::{Column, GridBuilder, Row}, - inspector::{InspectorBuilder, InspectorContext, InspectorMessage, PropertyAction}, - message::{MessageDirection, UiMessage}, - scroll_viewer::ScrollViewerBuilder, - stack_panel::StackPanelBuilder, - utils::make_simple_tooltip, - widget::WidgetBuilder, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - }, - scene::{ - base::BaseBuilder, - collider::{ColliderBuilder, ColliderShape, InteractionGroups}, - graph::Graph, - joint::{BallJoint, JointBuilder, JointParams, RevoluteJoint}, - node::Node, - ragdoll::{Limb, RagdollBuilder}, - rigidbody::{RigidBodyBuilder, RigidBodyType}, - transform::TransformBuilder, - }, -}; +use crate::plugins::inspector::editors::make_property_editors_container; use crate::{ - inspector::editors::make_property_editors_container, + command::{Command, CommandGroup}, + fyrox::{ + core::{ + algebra::{UnitQuaternion, Vector3}, + log::Log, + math::Matrix4Ext, + pool::Handle, + reflect::prelude::*, + some_or_return, + }, + graph::{BaseSceneGraph, SceneGraph}, + gui::{ + button::{ButtonBuilder, ButtonMessage}, + grid::{Column, GridBuilder, Row}, + inspector::{InspectorBuilder, InspectorContext, InspectorMessage, PropertyAction}, + menu::MenuItemMessage, + message::{MessageDirection, UiMessage}, + scroll_viewer::ScrollViewerBuilder, + stack_panel::StackPanelBuilder, + utils::make_simple_tooltip, + widget::WidgetBuilder, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, + }, + scene::{ + base::BaseBuilder, + collider::{ColliderBuilder, ColliderShape, InteractionGroups}, + graph::Graph, + joint::{BallJoint, JointBuilder, JointParams, RevoluteJoint}, + node::Node, + ragdoll::{Limb, RagdollBuilder}, + rigidbody::{RigidBodyBuilder, RigidBodyType}, + transform::TransformBuilder, + }, + }, + menu::create_menu_item, message::MessageSender, + plugin::EditorPlugin, scene::{ commands::{graph::AddModelCommand, ChangeSelectionCommand}, GameScene, Selection, }, world::graph::selection::GraphSelection, - MSG_SYNC_FLAG, + Editor, MSG_SYNC_FLAG, }; -use std::ops::Range; -use std::sync::Arc; +use std::{ops::Range, sync::Arc}; #[derive(Reflect, Debug)] pub struct RagdollPreset { @@ -743,7 +746,7 @@ impl RagdollPreset { try_make_ball_joint( right_up_leg, hips, - "RagdollLeftUpLegHipsBallJoint", + "RagdollRightUpLegHipsBallJoint", Some(BallJointLimits { x: (-80.0f32).to_radians()..80.0f32.to_radians(), y: (-80.0f32).to_radians()..80.0f32.to_radians(), @@ -1219,3 +1222,48 @@ impl RagdollWizard { } } } + +#[derive(Default)] +pub struct RagdollPlugin { + ragdoll_wizard: Option, + open_ragdoll_wizard: Handle, +} + +impl RagdollPlugin { + fn on_open_ragdoll_wizard_clicked(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + let wizard = self + .ragdoll_wizard + .get_or_insert_with(|| RagdollWizard::new(ctx, editor.message_sender.clone())); + wizard.open(ui); + } +} + +impl EditorPlugin for RagdollPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + self.open_ragdoll_wizard = create_menu_item("Ragdoll Wizard", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_ragdoll_wizard, + )); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_ragdoll_wizard { + self.on_open_ragdoll_wizard_clicked(editor) + } + } + + let ui = editor.engine.user_interfaces.first_mut(); + let wizard = some_or_return!(self.ragdoll_wizard.as_mut()); + let current_scene = some_or_return!(editor.scenes.current_scene_entry_mut()); + let game_scene = some_or_return!(current_scene.controller.downcast_mut::()); + let graph = &mut editor.engine.scenes[game_scene.scene].graph; + wizard.handle_ui_message(message, ui, graph, game_scene, &editor.message_sender); + } +} diff --git a/editor/src/plugins/settings.rs b/editor/src/plugins/settings.rs new file mode 100644 index 000000000..a1e5ded50 --- /dev/null +++ b/editor/src/plugins/settings.rs @@ -0,0 +1,324 @@ +// 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. + +//! Settings window plugin. + +use crate::{ + fyrox::{ + core::{log::Log, pool::Handle, some_or_return}, + engine::Engine, + gui::{ + button::{ButtonBuilder, ButtonMessage}, + dock::DockingManagerMessage, + grid::{Column, GridBuilder, Row}, + inspector::{ + editors::{ + collection::VecCollectionPropertyEditorDefinition, + enumeration::EnumPropertyEditorDefinition, + inspectable::InspectablePropertyEditorDefinition, + key::HotKeyPropertyEditorDefinition, PropertyEditorDefinitionContainer, + }, + InspectorBuilder, InspectorContext, InspectorMessage, PropertyAction, + }, + menu::MenuItemMessage, + message::{MessageDirection, UiMessage}, + scroll_viewer::ScrollViewerBuilder, + stack_panel::StackPanelBuilder, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage, WindowTitle}, + HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, + }, + renderer::{CsmSettings, QualitySettings, ShadowMapPrecision}, + }, + menu::create_menu_item, + message::MessageSender, + plugin::EditorPlugin, + settings::{ + build::{BuildCommand, BuildProfile, BuildSettings, EnvironmentVariable}, + camera::CameraSettings, + debugging::DebuggingSettings, + general::{GeneralSettings, ScriptEditor}, + graphics::GraphicsSettings, + keys::{KeyBindings, TerrainKeyBindings}, + model::ModelSettings, + move_mode::MoveInteractionModeSettings, + navmesh::NavmeshSettings, + rotate_mode::RotateInteractionModeSettings, + selection::SelectionSettings, + Settings, + }, + Editor, MSG_SYNC_FLAG, +}; +use std::sync::Arc; + +fn make_property_editors_container( + sender: MessageSender, +) -> Arc { + let container = crate::plugins::inspector::editors::make_property_editors_container(sender); + + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(EnumPropertyEditorDefinition::::new()); + container.insert(EnumPropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::< + MoveInteractionModeSettings, + >::new()); + container.insert(InspectablePropertyEditorDefinition::< + RotateInteractionModeSettings, + >::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(VecCollectionPropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(VecCollectionPropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(VecCollectionPropertyEditorDefinition::::new()); + container.insert(InspectablePropertyEditorDefinition::::new()); + container.insert(HotKeyPropertyEditorDefinition); + Arc::new(container) +} + +pub struct SettingsWindow { + pub window: Handle, + ok: Handle, + default: Handle, + inspector: Handle, +} + +impl SettingsWindow { + pub fn new(engine: &mut Engine) -> Self { + let ok; + let default; + + let ctx = &mut engine.user_interfaces.first_mut().build_ctx(); + + let inspector = InspectorBuilder::new(WidgetBuilder::new()).build(ctx); + + let window = WindowBuilder::new(WidgetBuilder::new().with_width(500.0).with_height(600.0)) + .open(false) + .with_title(WindowTitle::text("Settings")) + .with_content( + GridBuilder::new( + WidgetBuilder::new() + .with_child( + ScrollViewerBuilder::new( + WidgetBuilder::new() + .with_margin(Thickness::uniform(2.0)) + .on_row(0), + ) + .with_content(inspector) + .build(ctx), + ) + .with_child( + StackPanelBuilder::new( + WidgetBuilder::new() + .on_row(1) + .with_horizontal_alignment(HorizontalAlignment::Right) + .with_child({ + default = ButtonBuilder::new( + WidgetBuilder::new() + .with_width(80.0) + .with_margin(Thickness::uniform(1.0)), + ) + .with_text("Default") + .build(ctx); + default + }) + .with_child({ + ok = ButtonBuilder::new( + WidgetBuilder::new() + .with_width(80.0) + .with_margin(Thickness::uniform(1.0)), + ) + .with_text("OK") + .build(ctx); + ok + }), + ) + .with_orientation(Orientation::Horizontal) + .build(ctx), + ), + ) + .add_row(Row::stretch()) + .add_row(Row::strict(25.0)) + .add_column(Column::stretch()) + .build(ctx), + ) + .build(ctx); + + Self { + window, + ok, + default, + inspector, + } + } + + pub fn open(&self, ui: &mut UserInterface, settings: &Settings, sender: &MessageSender) { + ui.send_message(WindowMessage::open( + self.window, + MessageDirection::ToWidget, + true, + true, + )); + + self.sync_to_model(ui, settings, sender); + } + + fn sync_to_model(&self, ui: &mut UserInterface, settings: &Settings, sender: &MessageSender) { + let context = InspectorContext::from_object( + &**settings, + &mut ui.build_ctx(), + make_property_editors_container(sender.clone()), + None, + MSG_SYNC_FLAG, + 0, + true, + Default::default(), + 150.0, + ); + ui.send_message(InspectorMessage::context( + self.inspector, + MessageDirection::ToWidget, + context, + )); + } + + pub fn handle_ui_message( + self, + message: &UiMessage, + engine: &mut Engine, + settings: &mut Settings, + sender: &MessageSender, + docking_manager: Handle, + ) -> Option { + let ui = engine.user_interfaces.first_mut(); + + if let Some(ButtonMessage::Click) = message.data::() { + if message.destination() == self.ok { + ui.send_message(WindowMessage::close( + self.window, + MessageDirection::ToWidget, + )); + } else if message.destination() == self.default { + **settings = Default::default(); + + self.sync_to_model(ui, settings, sender); + } + } else if let Some(InspectorMessage::PropertyChanged(property_changed)) = message.data() { + if message.destination() == self.inspector { + PropertyAction::from_field_kind(&property_changed.value).apply( + &property_changed.path(), + &mut **settings, + &mut Log::verify, + ); + } + } else if let Some(WindowMessage::Close) = message.data() { + if message.destination() == self.window { + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); + ui.send_message(DockingManagerMessage::remove_floating_window( + docking_manager, + MessageDirection::ToWidget, + self.window, + )); + return None; + } + } + + let graphics_context = engine.graphics_context.as_initialized_mut(); + + if settings.graphics.quality != graphics_context.renderer.get_quality_settings() { + if let Err(e) = graphics_context + .renderer + .set_quality_settings(&settings.graphics.quality) + { + Log::err(format!( + "An error occurred at attempt to set new graphics settings: {e:?}" + )); + } else { + Log::info("New graphics quality settings were successfully set!"); + } + } + + Some(self) + } +} + +#[derive(Default)] +pub struct SettingsPlugin { + window: Option, + open_settings: Handle, +} + +impl SettingsPlugin { + fn on_open_settings_clicked(&mut self, editor: &mut Editor) { + let window = self + .window + .get_or_insert_with(|| SettingsWindow::new(&mut editor.engine)); + let ui = editor.engine.user_interfaces.first_mut(); + window.open(ui, &editor.settings, &editor.message_sender); + ui.send_message(DockingManagerMessage::add_floating_window( + editor.docking_manager, + MessageDirection::ToWidget, + window.window, + )); + } +} + +impl EditorPlugin for SettingsPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + self.open_settings = create_menu_item("Editor Settings...", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.file_menu.menu, + MessageDirection::ToWidget, + self.open_settings, + )); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_settings { + self.on_open_settings_clicked(editor); + } + } + + let window = some_or_return!(self.window.take()); + self.window = window.handle_ui_message( + message, + &mut editor.engine, + &mut editor.settings, + &editor.message_sender, + editor.docking_manager, + ); + } +} diff --git a/editor/src/plugins/stats.rs b/editor/src/plugins/stats.rs new file mode 100644 index 000000000..f131d408e --- /dev/null +++ b/editor/src/plugins/stats.rs @@ -0,0 +1,129 @@ +// 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::pool::Handle, + gui::{ + menu::MenuItemMessage, + message::{MessageDirection, UiMessage}, + stack_panel::StackPanelBuilder, + text::{TextBuilder, TextMessage}, + widget::{WidgetBuilder, WidgetMessage}, + window::{WindowBuilder, WindowMessage}, + HorizontalAlignment, Thickness, UiNode, VerticalAlignment, + }, + }, + menu::create_menu_item, + plugin::EditorPlugin, + Editor, +}; +use fyrox::gui::window::WindowTitle; + +/// Editor UI statistics, useful to track number of active widgets and memory consumption. + +#[derive(Default)] +pub struct UiStatisticsPlugin { + window: Handle, + text: Handle, + open_ui_stats: Handle, +} + +impl EditorPlugin for UiStatisticsPlugin { + fn on_start(&mut self, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + let ctx = &mut ui.build_ctx(); + self.open_ui_stats = create_menu_item("Editor UI Statistics", vec![], ctx); + ui.send_message(MenuItemMessage::add_item( + editor.menu.utils_menu.menu, + MessageDirection::ToWidget, + self.open_ui_stats, + )); + } + + fn on_ui_message(&mut self, message: &mut UiMessage, editor: &mut Editor) { + let ui = editor.engine.user_interfaces.first_mut(); + + if let Some(MenuItemMessage::Click) = message.data() { + if message.destination() == self.open_ui_stats && self.window.is_none() { + let ctx = &mut ui.build_ctx(); + self.text = + TextBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(1.0))) + .build(ctx); + self.window = + WindowBuilder::new(WidgetBuilder::new().with_width(200.0).with_height(100.0)) + .with_title(WindowTitle::text("Editor UI Statistics")) + .with_content( + StackPanelBuilder::new(WidgetBuilder::new().with_child(self.text)) + .build(ctx), + ) + .open(false) + .build(ctx); + + ui.send_message(WindowMessage::open_and_align( + self.window, + MessageDirection::ToWidget, + editor.scene_viewer.frame(), + HorizontalAlignment::Right, + VerticalAlignment::Bottom, + Thickness::uniform(1.0), + false, + true, + )); + } + } + + if let Some(WindowMessage::Close) = message.data() { + if message.destination() == self.window { + ui.send_message(WidgetMessage::remove( + self.window, + MessageDirection::ToWidget, + )); + self.window = Handle::NONE; + } + } + } + + fn on_update(&mut self, editor: &mut Editor) { + if self.window.is_none() { + return; + } + + let ui = editor.engine.user_interfaces.first(); + + let total_memory = ui + .nodes() + .iter() + .fold(0, |acc, node| acc + node.self_size()); + + ui.send_message(TextMessage::text( + self.text, + MessageDirection::ToWidget, + format!( + "Widget Count: {}\nMemory Used: {:.3} Mb.\n\ + Drawing Commands: {}\nProcessed Messages: {}\n", + ui.nodes().alive_count(), + total_memory as f32 / (1024.0 * 1024.0), + ui.get_drawing_context().get_commands().len(), + editor.processed_ui_messages + ), + )); + } +} diff --git a/editor/src/plugins/tilemap/mod.rs b/editor/src/plugins/tilemap/mod.rs index 21d5b49d3..4cba22405 100644 --- a/editor/src/plugins/tilemap/mod.rs +++ b/editor/src/plugins/tilemap/mod.rs @@ -28,7 +28,7 @@ mod preview; pub mod tile_set_import; pub mod tileset; -use crate::inspector::InspectorPlugin; +use crate::plugins::inspector::InspectorPlugin; use crate::{ command::SetPropertyCommand, fyrox::{ diff --git a/editor/src/plugins/tilemap/palette.rs b/editor/src/plugins/tilemap/palette.rs index 73565ca1d..c6edc60ed 100644 --- a/editor/src/plugins/tilemap/palette.rs +++ b/editor/src/plugins/tilemap/palette.rs @@ -18,34 +18,32 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::plugins::tilemap::tileset::TileSetTileView; -use crate::{ - absm::selectable::{Selectable, SelectableMessage}, - fyrox::{ - core::{ - algebra::{Matrix3, Point2, Vector2}, - color::Color, - math::{OptionRect, Rect}, - pool::Handle, - reflect::prelude::*, - type_traits::prelude::*, - visitor::prelude::*, - }, - graph::{BaseSceneGraph, SceneGraph}, - gui::{ - brush::Brush, - define_constructor, define_widget_deref, - draw::{CommandTexture, Draw, DrawingContext}, - message::{KeyCode, MessageDirection, MouseButton, UiMessage}, - widget::{Widget, WidgetBuilder, WidgetMessage}, - BuildContext, Control, UiNode, UserInterface, - }, - scene::tilemap::{ - brush::{BrushTile, TileMapBrush}, - tileset::TileSetResource, - }, +use crate::fyrox::{ + core::{ + algebra::{Matrix3, Point2, Vector2}, + color::Color, + math::{OptionRect, Rect}, + pool::Handle, + reflect::prelude::*, + type_traits::prelude::*, + visitor::prelude::*, + }, + graph::{BaseSceneGraph, SceneGraph}, + gui::{ + brush::Brush, + define_constructor, define_widget_deref, + draw::{CommandTexture, Draw, DrawingContext}, + message::{KeyCode, MessageDirection, MouseButton, UiMessage}, + widget::{Widget, WidgetBuilder, WidgetMessage}, + BuildContext, Control, UiNode, UserInterface, + }, + scene::tilemap::{ + brush::{BrushTile, TileMapBrush}, + tileset::TileSetResource, }, }; +use crate::plugins::absm::selectable::{Selectable, SelectableMessage}; +use crate::plugins::tilemap::tileset::TileSetTileView; use fyrox::scene::tilemap::tileset::TileDefinitionHandle; use std::ops::{Deref, DerefMut}; @@ -513,7 +511,7 @@ impl PaletteWidgetBuilder { .with_allow_drop(true) .with_clip_to_bounds(false) .with_children(self.tiles.iter().cloned()) - .build(), + .build(ctx), tiles: self.tiles, view_position: Default::default(), zoom: 1.0, @@ -640,7 +638,7 @@ impl BrushTileViewBuilder { pub fn build(self, ctx: &mut BuildContext) -> Handle { ctx.add_node(UiNode::new(BrushTileView { - widget: self.widget_builder.build(), + widget: self.widget_builder.build(ctx), selectable: Default::default(), definition_handle: self.definition_id, local_position: self.local_position, diff --git a/editor/src/plugins/tilemap/panel.rs b/editor/src/plugins/tilemap/panel.rs index 21f60b801..da6cdaee9 100644 --- a/editor/src/plugins/tilemap/panel.rs +++ b/editor/src/plugins/tilemap/panel.rs @@ -41,8 +41,7 @@ use crate::{ window::{WindowBuilder, WindowMessage, WindowTitle}, wrap_panel::WrapPanelBuilder, BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_BRIGHT_BLUE, BRUSH_DARKER, BRUSH_LIGHT, BRUSH_LIGHTER, - BRUSH_LIGHTEST, + VerticalAlignment, }, scene::{ node::Node, @@ -70,6 +69,8 @@ use crate::{ scene::{commands::GameSceneContext, container::EditorSceneEntry}, Message, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; pub struct TileMapPanel { pub window: Handle, @@ -138,15 +139,17 @@ fn make_drawing_mode_button( ) .with_back( DecoratorBuilder::new( - BorderBuilder::new(WidgetBuilder::new().with_foreground(BRUSH_DARKER)) - .with_pad_by_corner_radius(false) - .with_corner_radius(4.0) - .with_stroke_thickness(Thickness::uniform(1.0)), + BorderBuilder::new( + WidgetBuilder::new().with_foreground(ctx.style.get_or_default(Style::BRUSH_DARKER)), + ) + .with_pad_by_corner_radius(false) + .with_corner_radius(4.0) + .with_stroke_thickness(Thickness::uniform(1.0)), ) - .with_selected_brush(BRUSH_BRIGHT_BLUE) - .with_normal_brush(BRUSH_LIGHT) - .with_hover_brush(BRUSH_LIGHTER) - .with_pressed_brush(BRUSH_LIGHTEST) + .with_selected_brush(ctx.style.get_or_default(Style::BRUSH_BRIGHT_BLUE)) + .with_normal_brush(ctx.style.get_or_default(Style::BRUSH_LIGHT)) + .with_hover_brush(ctx.style.get_or_default(Style::BRUSH_LIGHTER)) + .with_pressed_brush(ctx.style.get_or_default(Style::BRUSH_LIGHTEST)) .build(ctx), ) .with_content( @@ -194,7 +197,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/brush.png")), + load_image!("../../../resources/brush.png"), "Draw with active brush.", Some(0), ); @@ -202,7 +205,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/eraser.png")), + load_image!("../../../resources/eraser.png"), "Erase with active brush.", Some(1), ); @@ -210,7 +213,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/fill.png")), + load_image!("../../../resources/fill.png"), "Flood fill with random tiles from current brush.", Some(2), ); @@ -218,7 +221,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/pipette.png")), + load_image!("../../../resources/pipette.png"), "Pick tiles for drawing from the tile map.", Some(3), ); @@ -226,7 +229,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/rect_fill.png")), + load_image!("../../../resources/rect_fill.png"), "Fill the rectangle using the current brush.", Some(4), ); @@ -234,7 +237,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/nine_slice.png")), + load_image!("../../../resources/nine_slice.png"), "Draw rectangles with fixed corners, but stretchable sides.", Some(5), ); @@ -242,7 +245,7 @@ impl TileMapPanel { ctx, width, height, - load_image(include_bytes!("../../../resources/line.png")), + load_image!("../../../resources/line.png"), "Draw a line using random tiles from the given brush.", Some(6), ); diff --git a/editor/src/plugins/tilemap/preview.rs b/editor/src/plugins/tilemap/preview.rs index 6594d4a45..62c6a182b 100644 --- a/editor/src/plugins/tilemap/preview.rs +++ b/editor/src/plugins/tilemap/preview.rs @@ -93,6 +93,6 @@ impl AssetPreviewGenerator for TileSetPreview { _resource: &UntypedResource, _resource_manager: &ResourceManager, ) -> Option { - load_image(include_bytes!("../../../resources/tile_set.png")) + load_image!("../../../resources/tile_set.png") } } diff --git a/editor/src/plugins/tilemap/tileset.rs b/editor/src/plugins/tilemap/tileset.rs index 288b50011..a47e733e1 100644 --- a/editor/src/plugins/tilemap/tileset.rs +++ b/editor/src/plugins/tilemap/tileset.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::inspector::EditorEnvironment; use crate::{ asset::item::AssetItem, command::{make_command, Command, CommandGroup}, @@ -55,7 +56,6 @@ use crate::{ resource::texture::Texture, scene::tilemap::tileset::{TileDefinition, TileSet, TileSetResource}, }, - inspector::EditorEnvironment, message::MessageSender, plugins::tilemap::{ commands::{AddTileCommand, RemoveTileCommand}, @@ -554,7 +554,7 @@ impl TileSetTileViewBuilder { .widget_builder .with_allow_drag(true) .with_child(decorator) - .build(), + .build(ctx), definition_handle: tile_handle, image, })) diff --git a/editor/src/preview.rs b/editor/src/preview.rs index 196bf2dad..58ad9e68a 100644 --- a/editor/src/preview.rs +++ b/editor/src/preview.rs @@ -217,9 +217,9 @@ impl PreviewPanel { .with_height(18.0) .with_margin(Thickness::uniform(2.0)), ) - .with_opt_texture(load_image(include_bytes!( + .with_opt_texture(load_image!( "../resources/fit.png" - ))) + )) .build(ctx), ) .build(ctx); diff --git a/editor/src/scene/mod.rs b/editor/src/scene/mod.rs index 638834723..1aa2dcf88 100644 --- a/editor/src/scene/mod.rs +++ b/editor/src/scene/mod.rs @@ -18,12 +18,17 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::plugins::absm::{ + command::fetch_machine, + selection::{AbsmSelection, SelectedEntity}, +}; +use crate::plugins::animation::{ + self, command::fetch_animations_container, selection::AnimationSelection, +}; +use crate::plugins::inspector::{ + editors::handle::HandlePropertyEditorMessage, handlers::node::SceneNodePropertyChangedHandler, +}; use crate::{ - absm::{ - command::fetch_machine, - selection::{AbsmSelection, SelectedEntity}, - }, - animation::{self, command::fetch_animations_container, selection::AnimationSelection}, asset::item::AssetItem, audio::AudioBusSelection, camera::{CameraController, PickingOptions}, @@ -77,10 +82,6 @@ use crate::{ }, }, highlight::HighlightRenderPass, - inspector::{ - editors::handle::HandlePropertyEditorMessage, - handlers::node::SceneNodePropertyChangedHandler, - }, interaction::navmesh::selection::NavmeshSelection, message::MessageSender, scene::{ diff --git a/editor/src/scene/property.rs b/editor/src/scene/property.rs index ca26d3336..c7fd7166d 100644 --- a/editor/src/scene/property.rs +++ b/editor/src/scene/property.rs @@ -18,7 +18,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::graph::BaseSceneGraph; use crate::fyrox::{ core::{ algebra::Vector2, make_pretty_type_name, parking_lot::Mutex, pool::Handle, @@ -26,14 +25,15 @@ use crate::fyrox::{ visitor::prelude::*, }, fxhash::FxHashSet, + graph::BaseSceneGraph, gui::{ border::BorderBuilder, button::{ButtonBuilder, ButtonMessage}, define_constructor, define_widget_deref, draw::DrawingContext, grid::{Column, GridBuilder, Row}, - message::{MessageDirection, OsEvent, UiMessage}, - scroll_viewer::ScrollViewerBuilder, + message::{KeyCode, MessageDirection, OsEvent, UiMessage}, + scroll_viewer::{ScrollViewerBuilder, ScrollViewerMessage}, searchbar::{SearchBarBuilder, SearchBarMessage}, stack_panel::StackPanelBuilder, text::TextBuilder, @@ -43,6 +43,8 @@ use crate::fyrox::{ BuildContext, Control, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, }, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ any::TypeId, ops::{Deref, DerefMut}, @@ -52,10 +54,12 @@ use std::{ #[derive(Debug, Clone, PartialEq, Eq)] pub enum PropertySelectorMessage { Selection(Vec), + ChooseFocus, } impl PropertySelectorMessage { define_constructor!(PropertySelectorMessage:Selection => fn selection(Vec), layout: false); + define_constructor!(PropertySelectorMessage:ChooseFocus => fn choose_focus(), layout: false); } pub struct PropertyDescriptor { @@ -296,15 +300,61 @@ pub struct PropertySelector { widget: Widget, #[reflect(hidden)] #[visit(skip)] - selected_property_path: Vec, + selected_property_paths: Vec, tree_root: Handle, search_bar: Handle, + scroll_viewer: Handle, } define_widget_deref!(PropertySelector); uuid_provider!(PropertySelector = "8e58e123-48a1-4e18-9e90-fd35a1669bdc"); +impl PropertySelector { + fn find_selected_tree_items(&self, ui: &UserInterface) -> Vec> { + let mut stack = vec![self.tree_root]; + let mut selected_trees = Vec::new(); + + while let Some(node_handle) = stack.pop() { + let node = ui.node(node_handle); + + if let Some(tree) = node.query_component::() { + if self.selected_property_paths.iter().any(|path| { + path.path + == tree + .user_data_cloned::() + .unwrap() + .path + }) { + selected_trees.push(node_handle); + } + } + + stack.extend_from_slice(node.children()); + } + + selected_trees + } + + fn sync_selection(&self, ui: &UserInterface) { + let selected_trees = self.find_selected_tree_items(ui); + + if let Some(first) = selected_trees.first() { + ui.send_message(ScrollViewerMessage::bring_into_view( + self.scroll_viewer, + MessageDirection::ToWidget, + *first, + )) + } + + ui.send_message(TreeRootMessage::select( + self.tree_root, + MessageDirection::ToWidget, + selected_trees, + )); + } +} + impl Control for PropertySelector { fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) { self.widget.handle_routed_message(ui, message); @@ -327,13 +377,25 @@ impl Control for PropertySelector { .collect(), )); } - } else if let Some(PropertySelectorMessage::Selection(selection)) = message.data() { + } else if let Some(msg) = message.data::() { if message.destination() == self.handle && message.direction() == MessageDirection::ToWidget - && &self.selected_property_path != selection { - self.selected_property_path.clone_from(selection); - ui.send_message(message.reverse()); + match msg { + PropertySelectorMessage::Selection(selection) => { + if &self.selected_property_paths != selection { + self.selected_property_paths.clone_from(selection); + ui.send_message(message.reverse()); + } + } + PropertySelectorMessage::ChooseFocus => { + ui.send_message(WidgetMessage::focus( + self.search_bar, + MessageDirection::ToWidget, + )); + self.sync_selection(ui); + } + } } } else if let Some(SearchBarMessage::Text(filter_text)) = message.data() { if message.destination() == self.search_bar @@ -349,6 +411,7 @@ pub struct PropertySelectorBuilder { widget_builder: WidgetBuilder, property_descriptors: Vec, allowed_types: Option>, + selected_property_paths: Vec, } impl PropertySelectorBuilder { @@ -357,6 +420,7 @@ impl PropertySelectorBuilder { widget_builder, property_descriptors: Default::default(), allowed_types: Default::default(), + selected_property_paths: Default::default(), } } @@ -370,9 +434,29 @@ impl PropertySelectorBuilder { self } + pub fn with_selected_property_paths(mut self, paths: Vec) -> Self { + self.selected_property_paths = paths; + self + } + pub fn build(self, ctx: &mut BuildContext) -> Handle { let tree_root; let search_bar; + + let scroll_viewer = + ScrollViewerBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(1.0))) + .with_content({ + tree_root = TreeRootBuilder::new(WidgetBuilder::new().with_tab_index(Some(1))) + .with_items(make_views_for_property_descriptor_collection( + ctx, + &self.property_descriptors, + self.allowed_types.as_ref(), + )) + .build(ctx); + tree_root + }) + .build(ctx); + let content = GridBuilder::new( WidgetBuilder::new() .with_child({ @@ -380,7 +464,8 @@ impl PropertySelectorBuilder { WidgetBuilder::new() .on_row(0) .on_column(0) - .with_margin(Thickness::uniform(1.0)), + .with_margin(Thickness::uniform(1.0)) + .with_tab_index(Some(0)), ) .build(ctx); search_bar @@ -388,25 +473,10 @@ impl PropertySelectorBuilder { .with_child( BorderBuilder::new( WidgetBuilder::new() - .with_background(fyrox::gui::BRUSH_DARK) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARK)) .on_row(1) .on_column(0) - .with_child( - ScrollViewerBuilder::new( - WidgetBuilder::new().with_margin(Thickness::uniform(1.0)), - ) - .with_content({ - tree_root = TreeRootBuilder::new(WidgetBuilder::new()) - .with_items(make_views_for_property_descriptor_collection( - ctx, - &self.property_descriptors, - self.allowed_types.as_ref(), - )) - .build(ctx); - tree_root - }) - .build(ctx), - ), + .with_child(scroll_viewer), ) .build(ctx), ), @@ -417,10 +487,11 @@ impl PropertySelectorBuilder { .build(ctx); let selector = PropertySelector { - widget: self.widget_builder.with_child(content).build(), - selected_property_path: Default::default(), + widget: self.widget_builder.with_child(content).build(ctx), + selected_property_paths: self.selected_property_paths, tree_root, search_bar, + scroll_viewer, }; ctx.add_node(UiNode::new(selector)) @@ -453,6 +524,24 @@ impl DerefMut for PropertySelectorWindow { } } +impl PropertySelectorWindow { + pub fn confirm(&self, ui: &UserInterface) { + ui.send_message(PropertySelectorMessage::selection( + self.handle, + MessageDirection::FromWidget, + ui.node(self.selector) + .query_component::() + .unwrap() + .selected_property_paths + .clone(), + )); + ui.send_message(WindowMessage::close( + self.handle, + MessageDirection::ToWidget, + )); + } +} + uuid_provider!(PropertySelectorWindow = "725e4a10-eca6-4345-9833-d54dae2f20f2"); impl Control for PropertySelectorWindow { @@ -481,20 +570,7 @@ impl Control for PropertySelectorWindow { if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.ok { - ui.send_message(PropertySelectorMessage::selection( - self.handle, - MessageDirection::FromWidget, - ui.node(self.selector) - .query_component::() - .unwrap() - .selected_property_path - .clone(), - )); - - ui.send_message(WindowMessage::close( - self.handle, - MessageDirection::ToWidget, - )); + self.confirm(ui); } else if message.destination() == self.cancel { ui.send_message(WindowMessage::close( self.handle, @@ -517,6 +593,22 @@ impl Control for PropertySelectorWindow { enabled, )); } + } else if let Some(WindowMessage::Open { .. }) + | Some(WindowMessage::OpenAt { .. }) + | Some(WindowMessage::OpenModal { .. }) + | Some(WindowMessage::OpenAndAlign { .. }) = message.data() + { + ui.send_message(PropertySelectorMessage::choose_focus( + self.selector, + MessageDirection::ToWidget, + )); + } else if let Some(WidgetMessage::KeyDown(KeyCode::Enter | KeyCode::NumpadEnter)) = + message.data() + { + if !message.handled() { + self.confirm(ui); + message.set_handled(true); + } } } @@ -538,6 +630,7 @@ pub struct PropertySelectorWindowBuilder { window_builder: WindowBuilder, property_descriptors: Vec, allowed_types: Option>, + selected_property_paths: Vec, } impl PropertySelectorWindowBuilder { @@ -546,6 +639,7 @@ impl PropertySelectorWindowBuilder { window_builder, property_descriptors: Default::default(), allowed_types: None, + selected_property_paths: Default::default(), } } @@ -559,6 +653,11 @@ impl PropertySelectorWindowBuilder { self } + pub fn with_selected_property_paths(mut self, paths: Vec) -> Self { + self.selected_property_paths = paths; + self + } + pub fn build(self, ctx: &mut BuildContext) -> Handle { let selector; let ok; @@ -572,6 +671,7 @@ impl PropertySelectorWindowBuilder { .on_column(0) .with_margin(Thickness::uniform(1.0)), ) + .with_selected_property_paths(self.selected_property_paths) .with_allowed_types(self.allowed_types.clone()) .with_property_descriptors(self.property_descriptors) .build(ctx); @@ -589,7 +689,8 @@ impl PropertySelectorWindowBuilder { WidgetBuilder::new() .with_enabled(false) .with_width(100.0) - .with_margin(Thickness::uniform(1.0)), + .with_margin(Thickness::uniform(1.0)) + .with_tab_index(Some(2)), ) .with_text("OK") .build(ctx); @@ -599,7 +700,8 @@ impl PropertySelectorWindowBuilder { cancel = ButtonBuilder::new( WidgetBuilder::new() .with_width(100.0) - .with_margin(Thickness::uniform(1.0)), + .with_margin(Thickness::uniform(1.0)) + .with_tab_index(Some(3)), ) .with_text("Cancel") .build(ctx); @@ -611,7 +713,7 @@ impl PropertySelectorWindowBuilder { ), ) .add_row(Row::stretch()) - .add_row(Row::strict(22.0)) + .add_row(Row::strict(26.0)) .add_column(Column::stretch()) .build(ctx); @@ -626,3 +728,18 @@ impl PropertySelectorWindowBuilder { ctx.add_node(UiNode::new(window)) } } + +#[cfg(test)] +mod test { + use crate::scene::property::{PropertySelectorBuilder, PropertySelectorWindowBuilder}; + use fyrox::gui::window::WindowBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| PropertySelectorBuilder::new(WidgetBuilder::new()).build(ctx)); + test_widget_deletion(|ctx| { + PropertySelectorWindowBuilder::new(WindowBuilder::new(WidgetBuilder::new())).build(ctx) + }); + } +} diff --git a/editor/src/scene/selector.rs b/editor/src/scene/selector.rs index 1f247e658..d21ecbb10 100644 --- a/editor/src/scene/selector.rs +++ b/editor/src/scene/selector.rs @@ -18,32 +18,37 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::graph::BaseSceneGraph; -use crate::fyrox::graph::{SceneGraph, SceneGraphNode}; -use crate::fyrox::{ - core::{ - algebra::Vector2, parking_lot::Mutex, pool::ErasedHandle, pool::Handle, - reflect::prelude::*, type_traits::prelude::*, uuid_provider, visitor::prelude::*, - }, - gui::{ - border::BorderBuilder, - button::{ButtonBuilder, ButtonMessage}, - define_constructor, define_widget_deref, - draw::DrawingContext, - grid::{Column, GridBuilder, Row}, - message::{MessageDirection, OsEvent, UiMessage}, - scroll_viewer::ScrollViewerBuilder, - scroll_viewer::ScrollViewerMessage, - searchbar::{SearchBarBuilder, SearchBarMessage}, - stack_panel::StackPanelBuilder, - text::TextBuilder, - tree::{Tree, TreeBuilder, TreeRootBuilder, TreeRootMessage}, - widget::{Widget, WidgetBuilder, WidgetMessage}, - window::{Window, WindowBuilder, WindowMessage}, - BuildContext, Control, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, +use crate::{ + fyrox::{ + core::{ + algebra::Vector2, parking_lot::Mutex, pool::ErasedHandle, pool::Handle, + reflect::prelude::*, type_traits::prelude::*, uuid_provider, visitor::prelude::*, + }, + graph::{BaseSceneGraph, SceneGraph, SceneGraphNode}, + gui::{ + border::BorderBuilder, + button::{ButtonBuilder, ButtonMessage}, + define_constructor, define_widget_deref, + draw::DrawingContext, + grid::{Column, GridBuilder, Row}, + message::{MessageDirection, OsEvent, UiMessage}, + scroll_viewer::ScrollViewerBuilder, + scroll_viewer::ScrollViewerMessage, + searchbar::{SearchBarBuilder, SearchBarMessage}, + stack_panel::StackPanelBuilder, + text::TextBuilder, + tree::{Tree, TreeBuilder, TreeRootBuilder, TreeRootMessage}, + widget::{Widget, WidgetBuilder, WidgetMessage}, + window::{Window, WindowBuilder, WindowMessage}, + BuildContext, Control, HorizontalAlignment, Orientation, Thickness, UiNode, + UserInterface, + }, }, + utils::make_node_name, }; -use crate::utils::make_node_name; +use fyrox::gui::message::KeyCode; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ ops::{Deref, DerefMut}, sync::mpsc::Sender, @@ -143,11 +148,13 @@ pub enum NodeSelectorMessage { #[allow(dead_code)] // Might be used in the future. Hierarchy(HierarchyNode), Selection(Vec), + ChooseFocus, } impl NodeSelectorMessage { define_constructor!(NodeSelectorMessage:Hierarchy => fn hierarchy(HierarchyNode), layout: false); define_constructor!(NodeSelectorMessage:Selection => fn selection(Vec), layout: false); + define_constructor!(NodeSelectorMessage:ChooseFocus => fn choose_focus(), layout: false); } #[derive(Clone)] @@ -219,6 +226,13 @@ impl Control for NodeSelector { ui.send_message(message.reverse()); } } + NodeSelectorMessage::ChooseFocus => { + ui.send_message(WidgetMessage::focus( + self.search_bar, + MessageDirection::ToWidget, + )); + self.sync_selection(ui); + } } } } else if let Some(SearchBarMessage::Text(filter_text)) = message.data() { @@ -329,7 +343,7 @@ impl NodeSelectorBuilder { .map(|h| vec![h.make_view(ctx)]) .unwrap_or_default(); - let tree_root = TreeRootBuilder::new(WidgetBuilder::new()) + let tree_root = TreeRootBuilder::new(WidgetBuilder::new().with_tab_index(Some(1))) .with_items(items) .build(ctx); let search_bar; @@ -337,13 +351,15 @@ impl NodeSelectorBuilder { let content = GridBuilder::new( WidgetBuilder::new() .with_child({ - search_bar = SearchBarBuilder::new(WidgetBuilder::new()).build(ctx); + search_bar = + SearchBarBuilder::new(WidgetBuilder::new().with_tab_index(Some(0))) + .build(ctx); search_bar }) .with_child( BorderBuilder::new( WidgetBuilder::new() - .with_background(fyrox::gui::BRUSH_DARK) + .with_background(ctx.style.get_or_default(Style::BRUSH_DARK)) .on_row(1) .on_column(0) .with_child({ @@ -364,7 +380,7 @@ impl NodeSelectorBuilder { .build(ctx); let selector = NodeSelector { - widget: self.widget_builder.with_child(content).build(), + widget: self.widget_builder.with_child(content).build(ctx), tree_root, search_bar, selected: Default::default(), @@ -398,6 +414,25 @@ impl DerefMut for NodeSelectorWindow { } } +impl NodeSelectorWindow { + fn confirm(&self, ui: &UserInterface) { + ui.send_message(NodeSelectorMessage::selection( + self.handle, + MessageDirection::FromWidget, + ui.node(self.selector) + .query_component::() + .unwrap() + .selected + .clone(), + )); + + ui.send_message(WindowMessage::close( + self.handle, + MessageDirection::ToWidget, + )); + } +} + uuid_provider!(NodeSelectorWindow = "5bb00f15-d6ec-4f0e-af7e-9472b0e290b4"); impl Control for NodeSelectorWindow { @@ -426,20 +461,7 @@ impl Control for NodeSelectorWindow { if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.ok { - ui.send_message(NodeSelectorMessage::selection( - self.handle, - MessageDirection::FromWidget, - ui.node(self.selector) - .query_component::() - .unwrap() - .selected - .clone(), - )); - - ui.send_message(WindowMessage::close( - self.handle, - MessageDirection::ToWidget, - )); + self.confirm(ui); } else if message.destination() == self.cancel { ui.send_message(WindowMessage::close( self.handle, @@ -466,6 +488,22 @@ impl Control for NodeSelectorWindow { )); } } + } else if let Some(WindowMessage::Open { .. }) + | Some(WindowMessage::OpenAt { .. }) + | Some(WindowMessage::OpenModal { .. }) + | Some(WindowMessage::OpenAndAlign { .. }) = message.data() + { + ui.send_message(NodeSelectorMessage::choose_focus( + self.selector, + MessageDirection::ToWidget, + )); + } else if let Some(WidgetMessage::KeyDown(KeyCode::Enter | KeyCode::NumpadEnter)) = + message.data() + { + if !message.handled() { + self.confirm(ui); + message.set_handled(true); + } } } @@ -525,7 +563,8 @@ impl NodeSelectorWindowBuilder { WidgetBuilder::new() .with_enabled(false) .with_width(100.0) - .with_margin(Thickness::uniform(1.0)), + .with_margin(Thickness::uniform(1.0)) + .with_tab_index(Some(2)), ) .with_text("OK") .build(ctx); @@ -535,7 +574,8 @@ impl NodeSelectorWindowBuilder { cancel = ButtonBuilder::new( WidgetBuilder::new() .with_width(100.0) - .with_margin(Thickness::uniform(1.0)), + .with_margin(Thickness::uniform(1.0)) + .with_tab_index(Some(3)), ) .with_text("Cancel") .build(ctx); @@ -565,3 +605,18 @@ impl NodeSelectorWindowBuilder { ctx.add_node(UiNode::new(window)) } } + +#[cfg(test)] +mod test { + use crate::scene::selector::{NodeSelectorBuilder, NodeSelectorWindowBuilder}; + use fyrox::gui::window::WindowBuilder; + use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder}; + + #[test] + fn test_deletion() { + test_widget_deletion(|ctx| NodeSelectorBuilder::new(WidgetBuilder::new()).build(ctx)); + test_widget_deletion(|ctx| { + NodeSelectorWindowBuilder::new(WindowBuilder::new(WidgetBuilder::new())).build(ctx) + }); + } +} diff --git a/editor/src/scene/settings/mod.rs b/editor/src/scene/settings/mod.rs index 2522ee301..6bc69654a 100644 --- a/editor/src/scene/settings/mod.rs +++ b/editor/src/scene/settings/mod.rs @@ -18,41 +18,41 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::fyrox::{ - core::{color::Color, pool::Handle}, - engine::Engine, - gui::{ - inspector::{ - editors::{ - enumeration::EnumPropertyEditorDefinition, PropertyEditorDefinitionContainer, - }, - InspectorBuilder, InspectorContext, InspectorMessage, PropertyFilter, - }, - message::UiMessage, - scroll_viewer::ScrollViewerBuilder, - widget::WidgetBuilder, - window::{WindowBuilder, WindowMessage, WindowTitle}, - BuildContext, UiNode, UserInterface, - }, - resource::texture::TextureResource, - scene::{ - dim2, - graph::{ - physics::{IntegrationParameters, PhysicsWorld}, - Graph, NodePool, - }, - SceneRenderingOptions, - }, - utils::lightmap::Lightmap, -}; +use crate::plugins::inspector::editors::make_property_editors_container; use crate::{ - inspector::editors::make_property_editors_container, message::MessageSender, GameScene, - Message, MessageDirection, MSG_SYNC_FLAG, + command::make_command, + fyrox::{ + core::{color::Color, pool::Handle, reflect::Reflect}, + engine::Engine, + gui::{ + inspector::{ + editors::{ + enumeration::EnumPropertyEditorDefinition, PropertyEditorDefinitionContainer, + }, + InspectorBuilder, InspectorContext, InspectorMessage, PropertyFilter, + }, + message::UiMessage, + scroll_viewer::ScrollViewerBuilder, + widget::WidgetBuilder, + window::{WindowBuilder, WindowMessage, WindowTitle}, + BuildContext, UiNode, UserInterface, + }, + resource::texture::TextureResource, + scene::{ + dim2, + graph::{ + physics::{IntegrationParameters, PhysicsWorld}, + Graph, NodePool, + }, + SceneRenderingOptions, + }, + utils::lightmap::Lightmap, + }, + message::MessageSender, + scene::commands::GameSceneContext, + GameScene, Message, MessageDirection, MSG_SYNC_FLAG, }; - -use crate::command::make_command; -use crate::fyrox::core::reflect::Reflect; -use crate::scene::commands::GameSceneContext; +use fyrox::{graph::SceneGraph, gui::window::Window}; use std::sync::Arc; pub struct SceneSettingsWindow { @@ -104,7 +104,15 @@ impl SceneSettingsWindow { } pub fn sync_to_model(&self, game_scene: &GameScene, engine: &mut Engine) { - let ui = &mut engine.user_interfaces.first_mut(); + let ui = engine.user_interfaces.first_mut(); + if !ui + .try_get_of_type::(self.window) + .unwrap() + .is_globally_visible() + { + return; + } + let scene = &engine.scenes[game_scene.scene]; let context = InspectorContext::from_object( diff --git a/editor/src/scene_viewer/mod.rs b/editor/src/scene_viewer/mod.rs index c24b13dd2..70406d0db 100644 --- a/editor/src/scene_viewer/mod.rs +++ b/editor/src/scene_viewer/mod.rs @@ -48,7 +48,7 @@ use crate::{ widget::{WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - VerticalAlignment, BRUSH_BRIGHT_BLUE, BRUSH_DARKEST, + VerticalAlignment, }, renderer::framework::PolygonFillMode, resource::texture::TextureResource, @@ -68,6 +68,8 @@ use crate::{ DropdownListBuilder, GameScene, Message, Mode, SaveSceneConfirmationDialogAction, SceneContainer, Settings, }; +use fyrox::gui::style::resource::StyleResourceExt; +use fyrox::gui::style::Style; use std::{ ops::Deref, sync::mpsc::{self, Receiver}, @@ -111,7 +113,7 @@ impl GridSnappingMenu { ctx, 22.0, 22.0, - load_image(include_bytes!("../../resources/grid_snapping.png")), + load_image!("../../resources/grid_snapping.png"), "Snapping Options", None, ); @@ -215,7 +217,7 @@ impl GridSnappingMenu { ui.send_message(DecoratorMessage::selected_brush( *button.decorator, MessageDirection::ToWidget, - BRUSH_BRIGHT_BLUE, + ui.style.get_or_default(Style::BRUSH_BRIGHT_BLUE), )); ui.send_message(DecoratorMessage::select( @@ -420,9 +422,7 @@ impl SceneViewer { 0, 200, 0, ))), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/play.png" - ))) + .with_opt_texture(load_image!("../../resources/play.png")) .build(ctx), ) .build(ctx); @@ -445,9 +445,7 @@ impl SceneViewer { 200, 0, 0, ))), ) - .with_opt_texture(load_image(include_bytes!( - "../../resources/stop.png" - ))) + .with_opt_texture(load_image!("../../resources/stop.png")) .build(ctx), ) .build(ctx); @@ -468,7 +466,7 @@ impl SceneViewer { let no_scene_reminder = TextBuilder::new( WidgetBuilder::new() .with_hit_test_visibility(false) - .with_foreground(BRUSH_DARKEST), + .with_foreground(ctx.style.get_or_default(Style::BRUSH_DARKEST)), ) .with_text("No scene loaded. Create a new scene (File -> New Scene) or load existing (File -> Load Scene)") .with_vertical_text_alignment(VerticalAlignment::Center) @@ -718,16 +716,6 @@ impl SceneViewer { } else if message.destination() == self.stop { self.sender.send(Message::SwitchToEditMode); } - } else if let Some(WidgetMessage::MouseDown { button, .. }) = - message.data::() - { - for &mode_button in self.interaction_modes.values() { - if ui.is_node_child_of(message.destination(), mode_button) - && *button == MouseButton::Right - { - self.sender.send(Message::OpenSettings); - } - } } else if let Some(DropdownListMessage::SelectionChanged(Some(index))) = message.data() { if message.direction == MessageDirection::FromWidget { if message.destination() == self.camera_projection { diff --git a/editor/src/settings/mod.rs b/editor/src/settings/mod.rs index 66f22cd14..bbb11cff5 100644 --- a/editor/src/settings/mod.rs +++ b/editor/src/settings/mod.rs @@ -18,52 +18,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::settings::build::{BuildCommand, BuildProfile, EnvironmentVariable}; use crate::{ - fyrox::{ - core::{log::Log, pool::Handle, reflect::prelude::*}, - gui::{ - button::{ButtonBuilder, ButtonMessage}, - grid::{Column, GridBuilder, Row}, - inspector::{ - editors::{ - enumeration::EnumPropertyEditorDefinition, - inspectable::InspectablePropertyEditorDefinition, - key::HotKeyPropertyEditorDefinition, PropertyEditorDefinitionContainer, - }, - InspectorBuilder, InspectorContext, InspectorMessage, PropertyAction, - PropertyChanged, - }, - message::{MessageDirection, UiMessage}, - scroll_viewer::ScrollViewerBuilder, - stack_panel::StackPanelBuilder, - widget::WidgetBuilder, - window::{WindowBuilder, WindowMessage, WindowTitle}, - HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, - }, - renderer::{CsmSettings, QualitySettings, ShadowMapPrecision}, - }, - inspector::editors::make_property_editors_container, - message::MessageSender, + fyrox::core::{log::Log, reflect::prelude::*}, settings::{ - build::BuildSettings, - camera::CameraSettings, - debugging::DebuggingSettings, - general::{GeneralSettings, ScriptEditor}, - graphics::GraphicsSettings, - keys::{KeyBindings, TerrainKeyBindings}, - model::ModelSettings, - move_mode::MoveInteractionModeSettings, - navmesh::NavmeshSettings, - recent::RecentFiles, - rotate_mode::RotateInteractionModeSettings, - scene::SceneSettings, - selection::SelectionSettings, - windows::WindowsSettings, + build::BuildSettings, camera::CameraSettings, debugging::DebuggingSettings, + general::GeneralSettings, graphics::GraphicsSettings, keys::KeyBindings, + model::ModelSettings, move_mode::MoveInteractionModeSettings, navmesh::NavmeshSettings, + recent::RecentFiles, rotate_mode::RotateInteractionModeSettings, scene::SceneSettings, + selection::SelectionSettings, windows::WindowsSettings, }, - Engine, MSG_SYNC_FLAG, }; -use fyrox::gui::inspector::editors::collection::VecCollectionPropertyEditorDefinition; use ron::ser::PrettyConfig; use serde::{Deserialize, Serialize}; use std::sync::mpsc::Sender; @@ -73,7 +37,6 @@ use std::{ io::Write, ops::{Deref, DerefMut}, path::PathBuf, - sync::Arc, }; pub mod build; @@ -91,13 +54,6 @@ pub mod scene; pub mod selection; pub mod windows; -pub struct SettingsWindow { - pub window: Handle, - ok: Handle, - default: Handle, - inspector: Handle, -} - #[derive(Deserialize, Serialize, PartialEq, Clone, Default, Debug, Reflect)] pub struct SettingsData { pub selection: SelectionSettings, @@ -220,191 +176,4 @@ impl SettingsData { Log::info("Settings were successfully saved!"); Ok(()) } - - fn make_property_editors_container( - sender: MessageSender, - ) -> Arc { - let container = make_property_editors_container(sender); - - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(EnumPropertyEditorDefinition::::new()); - container.insert(EnumPropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::< - MoveInteractionModeSettings, - >::new()); - container.insert(InspectablePropertyEditorDefinition::< - RotateInteractionModeSettings, - >::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(VecCollectionPropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(VecCollectionPropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(VecCollectionPropertyEditorDefinition::::new()); - container.insert(InspectablePropertyEditorDefinition::::new()); - container.insert(HotKeyPropertyEditorDefinition); - - Arc::new(container) - } - - fn handle_property_changed(&mut self, property_changed: &PropertyChanged) { - PropertyAction::from_field_kind(&property_changed.value).apply( - &property_changed.path(), - self, - &mut Log::verify, - ); - } -} - -impl SettingsWindow { - pub fn new(engine: &mut Engine) -> Self { - let ok; - let default; - - let ctx = &mut engine.user_interfaces.first_mut().build_ctx(); - - let inspector = InspectorBuilder::new(WidgetBuilder::new()).build(ctx); - - let window = WindowBuilder::new(WidgetBuilder::new().with_width(500.0).with_height(600.0)) - .open(false) - .with_title(WindowTitle::text("Settings")) - .with_content( - GridBuilder::new( - WidgetBuilder::new() - .with_child( - ScrollViewerBuilder::new( - WidgetBuilder::new() - .with_margin(Thickness::uniform(2.0)) - .on_row(0), - ) - .with_content(inspector) - .build(ctx), - ) - .with_child( - StackPanelBuilder::new( - WidgetBuilder::new() - .on_row(1) - .with_horizontal_alignment(HorizontalAlignment::Right) - .with_child({ - default = ButtonBuilder::new( - WidgetBuilder::new() - .with_width(80.0) - .with_margin(Thickness::uniform(1.0)), - ) - .with_text("Default") - .build(ctx); - default - }) - .with_child({ - ok = ButtonBuilder::new( - WidgetBuilder::new() - .with_width(80.0) - .with_margin(Thickness::uniform(1.0)), - ) - .with_text("OK") - .build(ctx); - ok - }), - ) - .with_orientation(Orientation::Horizontal) - .build(ctx), - ), - ) - .add_row(Row::stretch()) - .add_row(Row::strict(25.0)) - .add_column(Column::stretch()) - .build(ctx), - ) - .build(ctx); - - Self { - window, - ok, - default, - inspector, - } - } - - pub fn open(&self, ui: &mut UserInterface, settings: &Settings, sender: &MessageSender) { - ui.send_message(WindowMessage::open( - self.window, - MessageDirection::ToWidget, - true, - true, - )); - - self.sync_to_model(ui, settings, sender); - } - - fn sync_to_model(&self, ui: &mut UserInterface, settings: &Settings, sender: &MessageSender) { - let context = InspectorContext::from_object( - &**settings, - &mut ui.build_ctx(), - SettingsData::make_property_editors_container(sender.clone()), - None, - MSG_SYNC_FLAG, - 0, - true, - Default::default(), - 150.0, - ); - ui.send_message(InspectorMessage::context( - self.inspector, - MessageDirection::ToWidget, - context, - )); - } - - pub fn handle_message( - &mut self, - message: &UiMessage, - engine: &mut Engine, - settings: &mut Settings, - sender: &MessageSender, - ) { - if let Some(ButtonMessage::Click) = message.data::() { - if message.destination() == self.ok { - engine - .user_interfaces - .first_mut() - .send_message(WindowMessage::close( - self.window, - MessageDirection::ToWidget, - )); - } else if message.destination() == self.default { - **settings = Default::default(); - - self.sync_to_model(engine.user_interfaces.first_mut(), settings, sender); - } - } else if let Some(InspectorMessage::PropertyChanged(property_changed)) = message.data() { - if message.destination() == self.inspector { - settings.handle_property_changed(property_changed); - } - } - - let graphics_context = engine.graphics_context.as_initialized_mut(); - - if settings.graphics.quality != graphics_context.renderer.get_quality_settings() { - if let Err(e) = graphics_context - .renderer - .set_quality_settings(&settings.graphics.quality) - { - Log::err(format!( - "An error occurred at attempt to set new graphics settings: {e:?}" - )); - } else { - Log::info("New graphics quality settings were successfully set!"); - } - } - } } diff --git a/editor/src/ui_scene/menu.rs b/editor/src/ui_scene/menu.rs index 7f94d6ff8..a50f8c410 100644 --- a/editor/src/ui_scene/menu.rs +++ b/editor/src/ui_scene/menu.rs @@ -42,6 +42,7 @@ use crate::{ world::WorldViewerItemContextMenu, Engine, Message, MessageDirection, }; +use fyrox::gui::constructor::WidgetConstructorContainer; use fyrox::gui::menu::ContextMenuBuilder; use std::path::PathBuf; @@ -77,14 +78,17 @@ fn resource_path_of_first_selected_node( } impl WidgetContextMenu { - pub fn new(ctx: &mut BuildContext) -> Self { + pub fn new( + widget_constructors_container: &WidgetConstructorContainer, + ctx: &mut BuildContext, + ) -> Self { let delete_selection; let copy_selection; let paste; let make_root; let open_asset; - let widgets_menu = UiMenu::new(UiMenu::default_entries(), "Create Child Widget", ctx); + let widgets_menu = UiMenu::new(widget_constructors_container, "Create Child Widget", ctx); let menu = ContextMenuBuilder::new( PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content( diff --git a/editor/src/ui_scene/mod.rs b/editor/src/ui_scene/mod.rs index 9027f618b..704faad63 100644 --- a/editor/src/ui_scene/mod.rs +++ b/editor/src/ui_scene/mod.rs @@ -53,12 +53,12 @@ use crate::fyrox::{ resource::texture::{TextureKind, TextureResource, TextureResourceExtension}, scene::SceneContainer, }; +use crate::plugins::absm::{command::fetch_machine, selection::SelectedEntity}; +use crate::plugins::animation::{self, command::fetch_animations_container}; +use crate::plugins::inspector::editors::handle::HandlePropertyEditorMessage; use crate::{ - absm::{command::fetch_machine, selection::SelectedEntity}, - animation::{self, command::fetch_animations_container}, asset::item::AssetItem, command::{make_command, Command, CommandGroup, CommandStack}, - inspector::editors::handle::HandlePropertyEditorMessage, message::MessageSender, scene::{ commands::ChangeSelectionCommand, controller::SceneController, selector::HierarchyNode, diff --git a/editor/src/ui_scene/utils.rs b/editor/src/ui_scene/utils.rs index 087f66a95..01437a1ef 100644 --- a/editor/src/ui_scene/utils.rs +++ b/editor/src/ui_scene/utils.rs @@ -123,41 +123,41 @@ impl<'a> WorldViewerDataProvider for UiSceneWorldViewerDataProvider<'a> { // Containers if node.cast::().is_some() { - load_image(include_bytes!("../../resources/canvas-icon.png")) + load_image!("../../resources/canvas-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/screen-icon.png")) + load_image!("../../resources/screen-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/grid-icon.png")) + load_image!("../../resources/grid-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/stackPanel-icon.png")) + load_image!("../../resources/stackPanel-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/window-icon.png")) + load_image!("../../resources/window-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/messageBox-icon.png")) + load_image!("../../resources/messageBox-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/menu-icon.png")) + load_image!("../../resources/menu-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/popup-icon.png")) + load_image!("../../resources/popup-icon.png") } // Visual else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/text-icon.png")) + load_image!("../../resources/text-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/image-icon.png")) + load_image!("../../resources/image-icon.png") } else if node.cast::().is_some() { - load_image(include_bytes!("../../resources/border-icon.png")) + load_image!("../../resources/border-icon.png") } // Controls else if node.cast::