Merge branch 'FyroxEngine:master' into tiles

This commit is contained in:
b-guild
2024-11-28 03:33:28 -08:00
committed by GitHub
208 changed files with 6910 additions and 3965 deletions
-39
View File
@@ -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/[email protected]
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
-1
View File
@@ -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/)
+2 -3
View File
@@ -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;
+21 -4
View File
@@ -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)
});
}
}
+7 -7
View File
@@ -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(),
+9 -9
View File
@@ -285,7 +285,7 @@ impl AssetPreviewGenerator for SoundPreview {
_resource: &UntypedResource,
_resource_manager: &ResourceManager,
) -> Option<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
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<UntypedResource> {
load_image(include_bytes!("../../../resources/ui.png"))
load_image!("../../../resources/ui.png")
}
}
+21 -8
View File
@@ -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<AudioBus>, String)], ctx: &mut BuildContext) -> V
fn make_effect_names(names: &[String], ctx: &mut BuildContext) -> Vec<Handle<UiNode>> {
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));
}
}
+1 -1
View File
@@ -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::{
+3 -2
View File
@@ -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())
+44 -39
View File
@@ -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<UiNode>, 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,
+3 -3
View File
@@ -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),
);
+5 -3
View File
@@ -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(
+11 -8
View File
@@ -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,
+15 -12
View File
@@ -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)
+87 -275
View File
@@ -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> {
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<FxHashMap<usize, TextureResource>> = Default::default();
}
pub fn load_image(data: &[u8]) -> Option<UntypedResource> {
Some(load_texture(data)?.into())
pub fn load_texture_internal(data: &[u8]) -> Option<TextureResource> {
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<UntypedResource> {
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<UiNode>,
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<RefCell<SceneNodeContextMenu>>,
pub widget_context_menu: Rc<RefCell<WidgetContextMenu>>,
pub collider_control_panel: ColliderControlPanel,
pub overlay_pass: Option<Rc<RefCell<OverlayRenderPass>>>,
pub highlighter: Option<Rc<RefCell<HighlightRenderPass>>>,
pub export_window: Option<ExportWindow>,
pub statistics_window: Option<StatisticsWindow>,
pub surface_data_viewer: Option<SurfaceDataViewer>,
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::<GameScene>() {
let graph = &mut engine.scenes[game_scene.scene].graph;
self.animation_editor.handle_ui_message(
message,
&current_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,
&current_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,
&current_scene_entry.selection,
@@ -1294,13 +1269,6 @@ impl Editor {
engine,
&self.message_sender,
);
self.collider_control_panel.handle_ui_message(
message,
engine,
game_scene,
&current_scene_entry.selection,
&self.message_sender,
);
self.audio_preview_panel.handle_ui_message(
message,
&current_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::<UiScene>()
{
let ui_root = ui_scene.ui.root();
self.animation_editor.handle_ui_message(
message,
&current_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,
&current_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::<GameScene>() {
self.animation_editor.sync_to_model(
&current_scene_entry.selection,
engine.user_interfaces.first_mut(),
&engine.scenes[game_scene.scene].graph,
);
self.absm_editor.sync_to_model(
&current_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(&current_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::<UiScene>()
{
self.animation_editor.sync_to_model(
&current_scene_entry.selection,
engine.user_interfaces.first_mut(),
&ui_scene.ui,
);
self.absm_editor.sync_to_model(
&current_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::<UiScene>() {
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::<UiScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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(&current_mode))
+2 -4
View File
@@ -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},
+1 -1
View File
@@ -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),
);
-93
View File
@@ -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<UiNode>,
create_animation_player: Handle<UiNode>,
create_absm: Handle<UiNode>,
}
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<Node> {
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
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
}
}
}
+91 -407
View File
@@ -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<UiNode>,
@@ -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::<GameScene>() {
let scene = &engine.scenes[game_scene.scene];
@@ -134,203 +117,59 @@ impl CreateEntityRootMenu {
}
pub struct CreateEntityMenu {
create_pivot: Handle<UiNode>,
create_cube: Handle<UiNode>,
create_cone: Handle<UiNode>,
create_sphere: Handle<UiNode>,
create_cylinder: Handle<UiNode>,
create_quad: Handle<UiNode>,
create_decal: Handle<UiNode>,
create_point_light: Handle<UiNode>,
create_spot_light: Handle<UiNode>,
create_directional_light: Handle<UiNode>,
create_navmesh: Handle<UiNode>,
create_terrain: Handle<UiNode>,
create_camera: Handle<UiNode>,
create_sprite: Handle<UiNode>,
create_particle_system: Handle<UiNode>,
create_listener: Handle<UiNode>,
create_sound_source: Handle<UiNode>,
physics_menu: PhysicsMenu,
physics2d_menu: Physics2dMenu,
dim2_menu: Dim2Menu,
animation_menu: AnimationMenu,
ui_menu: UiMenu,
mesh_menu: Handle<UiNode>,
sound_menu: Handle<UiNode>,
light_menu: Handle<UiNode>,
}
fn placeholder_material() -> MaterialResource {
let mut material = Material::standard();
material.bind("diffuseTexture", PLACEHOLDER.resource());
MaterialResource::new_ok(ResourceKind::Embedded, material)
pub root_items: Vec<Handle<UiNode>>,
constructor_views: FxHashMap<Handle<UiNode>, VariantConstructor<Node, Graph>>,
}
impl CreateEntityMenu {
pub fn new(ctx: &mut BuildContext) -> (Self, Vec<Handle<UiNode>>) {
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<Node> {
if let Some(ui_scene) = controller.downcast_mut::<UiScene>() {
self.ui_menu
.handle_ui_message(sender, message, ui_scene, selection);
} else if let Some(game_scene) = controller.downcast_mut::<GameScene>() {
let graph = &mut engine.scenes[game_scene.scene].graph;
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
if let Some(constructor) = self.constructor_views.get(&message.destination()) {
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::<MenuItemMessage>() {
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
}
}
-83
View File
@@ -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<UiNode>,
create_sprite: Handle<UiNode>,
create_tile_map: Handle<UiNode>,
}
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<Node> {
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
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
}
}
}
+4 -4
View File
@@ -37,10 +37,10 @@ use crate::{
pub struct EditMenu {
pub menu: Handle<UiNode>,
undo: Handle<UiNode>,
redo: Handle<UiNode>,
copy: Handle<UiNode>,
paste: Handle<UiNode>,
pub undo: Handle<UiNode>,
pub redo: Handle<UiNode>,
pub copy: Handle<UiNode>,
pub paste: Handle<UiNode>,
}
impl EditMenu {
+7 -29
View File
@@ -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<UiNode>,
new_scene: Handle<UiNode>,
new_ui_scene: Handle<UiNode>,
pub new_scene: Handle<UiNode>,
pub new_ui_scene: Handle<UiNode>,
pub save: Handle<UiNode>,
pub save_as: Handle<UiNode>,
pub save_all: Handle<UiNode>,
load: Handle<UiNode>,
pub load: Handle<UiNode>,
pub close_scene: Handle<UiNode>,
exit: Handle<UiNode>,
pub open_settings: Handle<UiNode>,
configure: Handle<UiNode>,
pub exit: Handle<UiNode>,
pub configure: Handle<UiNode>,
pub save_file_selector: Handle<UiNode>,
pub load_file_selector: Handle<UiNode>,
configure_message: Handle<UiNode>,
pub settings: SettingsWindow,
pub configure_message: Handle<UiNode>,
pub recent_files_container: Handle<UiNode>,
pub recent_files: Vec<Handle<UiNode>>,
pub open_scene_settings: Handle<UiNode>,
@@ -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::<FileSelectorMessage>() {
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
+2 -2
View File
@@ -26,8 +26,8 @@ use crate::menu::{create_menu_item, create_root_menu_item};
pub struct HelpMenu {
pub menu: Handle<UiNode>,
open_book: Handle<UiNode>,
open_api_reference: Handle<UiNode>,
pub open_book: Handle<UiNode>,
pub open_api_reference: Handle<UiNode>,
}
impl HelpMenu {
+12 -19
View File
@@ -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<UiNode>,
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<UiNode>,
pub asset_window: Handle<UiNode>,
pub configurator_window: Handle<UiNode>,
pub path_fixer: Handle<UiNode>,
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<ExportWindow>,
pub statistics_window: &'b mut Option<StatisticsWindow>,
}
@@ -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);
-141
View File
@@ -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<UiNode>,
create_rigid_body: Handle<UiNode>,
create_revolute_joint: Handle<UiNode>,
create_ball_joint: Handle<UiNode>,
create_prismatic_joint: Handle<UiNode>,
create_fixed_joint: Handle<UiNode>,
create_collider: Handle<UiNode>,
create_ragdoll: Handle<UiNode>,
}
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<Node> {
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
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
}
}
}
-123
View File
@@ -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<UiNode>,
create_rigid_body: Handle<UiNode>,
create_ball_joint: Handle<UiNode>,
create_prismatic_joint: Handle<UiNode>,
create_fixed_joint: Handle<UiNode>,
create_collider: Handle<UiNode>,
}
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<Node> {
if let Some(MenuItemMessage::Click) = message.data::<MenuItemMessage>() {
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
}
}
}
+63 -218
View File
@@ -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<UiNode>,
constructors: FxHashMap<Handle<UiNode>, UiMenuEntry>,
}
#[allow(clippy::type_complexity)]
pub struct UiMenuEntry {
pub name: String,
pub constructor: Box<dyn FnMut(&str, &mut BuildContext) -> Handle<UiNode>>,
}
impl UiMenuEntry {
pub fn new<P, F>(name: P, constructor: F) -> Self
where
P: AsRef<str>,
F: FnMut(&str, &mut BuildContext) -> Handle<UiNode> + 'static,
{
Self {
name: name.as_ref().to_owned(),
constructor: Box::new(constructor),
}
}
constructor_views: FxHashMap<Handle<UiNode>, VariantConstructor<UiNode, UserInterface>>,
}
impl UiMenu {
pub fn default_entries() -> Vec<UiMenuEntry> {
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::<f32>::new(WidgetBuilder::new().with_name(name)).build(ctx)
}),
UiMenuEntry::new("NumericUpDownI32", |name, ctx| {
NumericUpDownBuilder::<i32>::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<UiMenuEntry>, name: &str, ctx: &mut BuildContext) -> Self {
let items = entries
.iter()
.map(|e| create_menu_item(&e.name, Default::default(), ctx))
.collect::<Vec<_>>();
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::<FxHashMap<_, _>>();
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::<MenuItemMessage>() {
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");
}
}
}
}
+12 -67
View File
@@ -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<UiNode>,
open_path_fixer: Handle<UiNode>,
open_curve_editor: Handle<UiNode>,
absm_editor: Handle<UiNode>,
animation_editor: Handle<UiNode>,
ragdoll_wizard: Handle<UiNode>,
rendering_statistics: Handle<UiNode>,
pub rendering_statistics: Handle<UiNode>,
}
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::<MenuItemMessage>() {
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,
+10 -10
View File
@@ -36,16 +36,16 @@ use crate::{
pub struct ViewMenu {
pub menu: Handle<UiNode>,
inspector: Handle<UiNode>,
world_viewer: Handle<UiNode>,
asset_browser: Handle<UiNode>,
light_panel: Handle<UiNode>,
log_panel: Handle<UiNode>,
nav_mesh: Handle<UiNode>,
audio: Handle<UiNode>,
command_stack: Handle<UiNode>,
save_layout: Handle<UiNode>,
load_layout: Handle<UiNode>,
pub inspector: Handle<UiNode>,
pub world_viewer: Handle<UiNode>,
pub asset_browser: Handle<UiNode>,
pub light_panel: Handle<UiNode>,
pub log_panel: Handle<UiNode>,
pub nav_mesh: Handle<UiNode>,
pub audio: Handle<UiNode>,
pub command_stack: Handle<UiNode>,
pub save_layout: Handle<UiNode>,
pub load_layout: Handle<UiNode>,
}
fn switch_window_state(window: Handle<UiNode>, ui: &UserInterface, center: bool) {
+8 -1
View File
@@ -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<Message>);
impl Default for MessageSender {
fn default() -> Self {
let (rx, _) = channel();
Self(rx)
}
}
unsafe impl Send for MessageSender {}
unsafe impl Sync for MessageSender {}
+11
View File
@@ -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.
@@ -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<P: Iterator<Item = Vector2<f32>>>(
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));
}
}
@@ -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<UiNode>,
pub initial_position: Vector2<f32>,
}
#[derive(Debug, Clone, PartialEq, Visit, Reflect, Default)]
pub(super) struct DragContext {
pub(crate) struct DragContext {
initial_cursor_position: Vector2<f32>,
entries: Vec<Entry>,
}
#[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<UiNode>,
@@ -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));
}
}
@@ -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;
@@ -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<UiNode>, ctx: &mut BuildContext) -> Handle<UiNode> {
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)
});
}
}
@@ -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<UiNode>) {
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<P, G, N>(
&mut self,
editor_selection: &Selection,
@@ -686,3 +708,208 @@ impl AbsmEditor {
}
}
}
#[derive(Default)]
pub struct AbsmEditorPlugin {
absm_editor: Option<AbsmEditor>,
open_absm_editor: Handle<UiNode>,
}
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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);
}
}
@@ -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::<Node>::new(WidgetBuilder::new()).build(ctx));
}
}
@@ -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;
@@ -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));
}
}
@@ -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<UiNode>,
connect_all_nodes: Handle<UiNode>,
@@ -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},
@@ -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,
@@ -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,
};
@@ -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<UiNode>,
@@ -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);
@@ -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)
});
}
}
@@ -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,
@@ -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<UiNode>) {
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<AnimationEditor>,
open_animation_editor: Handle<UiNode>,
}
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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::<GameScene>() {
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::<UiScene>() {
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);
}
}
@@ -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));
}
}
@@ -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));
}
}
@@ -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<G, N>(
&self,
animation: &Animation<Handle<N>>,
@@ -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<G, N>(
&mut self,
message: &UiMessage,
@@ -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<G, N>(
graph: &G,
node: Handle<N>,
existing_binding: Option<&ValueBinding>,
ui: &mut UserInterface,
) -> Handle<UiNode>
where
@@ -1121,6 +1117,7 @@ impl TrackList {
&& type_id != TypeId::of::<TriangleBuffer>()
// Makes no sense to animate drawing parameters.
&& type_id != TypeId::of::<DrawParameters>()
&& type_id != TypeId::of::<Samples>()
});
});
}
@@ -1147,6 +1144,21 @@ impl TrackList {
UnitQuaternion<f32>
})))
.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<G, N>(
@@ -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)
});
}
}
+76 -45
View File
@@ -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<Node>, scene: &Scene) -> Option<ColliderShape> {
scene
@@ -272,7 +282,7 @@ fn make_handle(scene: &mut Scene, root: Handle<Node>, visible: bool) -> Handle<N
material.bind(
"diffuseTexture",
load_texture(include_bytes!("../../../resources/circle.png")),
load_texture!("../../../resources/circle.png"),
);
let handle = SpriteBuilder::new(BaseBuilder::new().with_visibility(visible))
@@ -662,21 +672,28 @@ impl InteractionMode for ColliderShapeInteractionMode {
}
#[derive(Default)]
pub struct ColliderShapePlugin {}
pub struct ColliderPlugin {
panel: Option<ColliderControlPanel>,
}
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::<GameScene>());
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::<GameScene>() 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::<GameScene>());
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::<Collider>().is_none()
&& collider
.component_ref::<dim2::collider::Collider>()
.is_none()
{
continue;
}
let first_selected_collider = selection.nodes().iter().find(|h| {
scene.graph.has_component::<Collider>(**h)
|| scene.graph.has_component::<dim2::collider::Collider>(**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);
}
}
}
@@ -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::<Collider>(*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(
@@ -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<Self> {
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<CurveEditorWindow>,
open_curve_editor: Handle<UiNode>,
}
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);
}
}
@@ -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},
@@ -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));
}
}
@@ -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<UiNode
.with_margin(Thickness::uniform(1.0))
.with_background(Brush::Solid(color)),
)
.with_opt_texture(load_image(data))
.with_opt_texture(load_image_internal(data))
.build(ctx)
}
@@ -338,7 +340,7 @@ impl HandlePropertyEditorBuilder {
.on_column(1),
)
.with_content(make_icon(
include_bytes!("../../../resources/pick.png"),
include_bytes!("../../../../resources/pick.png"),
Color::opaque(0, 180, 0),
ctx,
))
@@ -354,7 +356,7 @@ impl HandlePropertyEditorBuilder {
.on_column(2),
)
.with_content(make_icon(
include_bytes!("../../../resources/locate.png"),
include_bytes!("../../../../resources/locate.png"),
Color::opaque(180, 180, 180),
ctx,
))
@@ -370,7 +372,7 @@ impl HandlePropertyEditorBuilder {
.on_column(3),
)
.with_content(make_icon(
include_bytes!("../../../resources/select_in_wv.png"),
include_bytes!("../../../../resources/select_in_wv.png"),
Color::opaque(180, 180, 180),
ctx,
))
@@ -386,7 +388,7 @@ impl HandlePropertyEditorBuilder {
.on_column(4),
)
.with_content(make_icon(
include_bytes!("../../../resources/cross.png"),
include_bytes!("../../../../resources/cross.png"),
Color::opaque(180, 0, 0),
ctx,
))
@@ -412,7 +414,7 @@ impl HandlePropertyEditorBuilder {
.with_preview_messages(true)
.with_allow_drop(true)
.with_child(grid)
.build(),
.build(ctx),
text,
value: self.value,
sender: self.sender,
@@ -521,3 +523,16 @@ fn request_name_sync(sender: &MessageSender, editor: Handle<UiNode>, 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)
});
}
}
@@ -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::<Option<TextureResource>>();
container.register_inheritable_vec_collection::<Option<UntypedResource>>();
container.insert(MaterialPropertyEditorDefinition {
sender: Mutex::new(sender.clone()),
});
container.insert(InheritablePropertyEditorDefinition::<MaterialResource>::new());
container.insert(InheritablePropertyEditorDefinition::<Handle<Node>>::new());
container.register_inheritable_vec_collection::<Handle<Node>>();
container.insert(NodeHandlePropertyEditorDefinition::new(
@@ -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::<Model>::new(WidgetBuilder::new(), Default::default())
.build(ctx, ResourceManager::new(Default::default()))
});
}
}
@@ -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,
@@ -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,
}))
@@ -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},
@@ -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,
@@ -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,
@@ -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),
);
@@ -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(),
)
});
}
}
@@ -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<UiNode>,
@@ -147,11 +154,7 @@ pub struct MaterialEditor {
texture_context_menu: TextureContextMenu,
}
fn create_item_container(
ctx: &mut BuildContext,
name: &str,
item: Handle<UiNode>,
) -> Handle<UiNode> {
fn make_item_container(ctx: &mut BuildContext, name: &str, item: Handle<UiNode>) -> Handle<UiNode> {
ctx[item].set_column(1);
GridBuilder::new(
@@ -171,79 +174,7 @@ fn create_item_container(
.build(ctx)
}
fn create_array_view<T, B>(
ctx: &mut BuildContext,
value: &[T],
mut item_builder: B,
) -> Handle<UiNode>
where
T: Clone,
B: FnMut(&mut BuildContext, T) -> Handle<UiNode>,
{
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<UiNode> {
NumericUpDownBuilder::<f32>::new(WidgetBuilder::new().with_height(24.0))
.with_value(value)
.build(ctx)
}
fn create_int_view(ctx: &mut BuildContext, value: i32) -> Handle<UiNode> {
NumericUpDownBuilder::<i32>::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<UiNode> {
NumericUpDownBuilder::<u32>::new(WidgetBuilder::new().with_height(24.0))
.with_value(value)
.with_precision(0)
.build(ctx)
}
fn create_vec2_view(ctx: &mut BuildContext, value: Vector2<f32>) -> Handle<UiNode> {
Vec2EditorBuilder::new(WidgetBuilder::new().with_height(24.0))
.with_value(value)
.build(ctx)
}
fn create_vec3_view(ctx: &mut BuildContext, value: Vector3<f32>) -> Handle<UiNode> {
Vec3EditorBuilder::new(WidgetBuilder::new().with_height(24.0))
.with_value(value)
.build(ctx)
}
fn create_vec4_view(ctx: &mut BuildContext, value: Vector4<f32>) -> Handle<UiNode> {
Vec4EditorBuilder::new(WidgetBuilder::new().with_height(24.0))
.with_value(value)
.build(ctx)
}
fn create_mat_view<const R: usize, const C: usize>(
ctx: &mut BuildContext,
value: SMatrix<f32, R, C>,
) -> Handle<UiNode> {
MatrixEditorBuilder::<R, C, f32>::new(WidgetBuilder::new())
.with_value(value)
.build(ctx)
}
fn sync_array<T, B>(ui: &UserInterface, handle: Handle<UiNode>, array: &[T], mut message_builder: B)
where
T: Clone,
B: FnMut(&T, Handle<UiNode>) -> UiMessage,
{
let views = &**ui.try_get_of_type::<ListView>(handle).unwrap().items;
for (item, view) in array.iter().zip(views) {
send_sync_message(ui, message_builder(item, *view))
}
}
fn pad_vec<T: Default + Clone>(v: &[T], max_len: usize) -> Vec<T> {
fn pad_vec<T: UiView>(v: &[T], max_len: usize) -> Vec<T> {
let mut vec = v.to_vec();
for _ in v.len()..max_len {
vec.push(T::default());
@@ -251,6 +182,102 @@ fn pad_vec<T: Default + Clone>(v: &[T], max_len: usize) -> Vec<T> {
vec
}
fn make_array_view(
ctx: &mut BuildContext,
value: &[impl UiView],
max_len: usize,
) -> Handle<UiNode> {
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<UiNode>, array: &[impl UiView]) {
let views = &**ui.try_get_of_type::<ListView>(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<UiNode>) -> UiMessage;
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode>;
fn send(self, ui: &UserInterface, item: Handle<UiNode>) {
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<UiNode>) -> UiMessage {
NumericUpDownMessage::value(item, MessageDirection::ToWidget, self)
}
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode> {
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<UiNode>) -> UiMessage {
VecEditorMessage::value(item, MessageDirection::ToWidget, self)
}
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode> {
VecEditorBuilder::new(WidgetBuilder::new().with_height(24.0))
.with_value(self)
.build(ctx)
}
})*
};
}
vec_ui_view!(Vector2<f32>, Vector3<f32>, Vector4<f32>);
macro_rules! mat_ui_view {
($($ty:ty),*) => {
$(impl UiView for $ty {
fn into_message(self, item: Handle<UiNode>) -> UiMessage {
MatrixEditorMessage::value(item, MessageDirection::ToWidget, self)
}
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode> {
MatrixEditorBuilder::new(WidgetBuilder::new())
.with_value(self)
.build(ctx)
}
})*
};
}
mat_ui_view!(Matrix2<f32>, Matrix3<f32>, Matrix4<f32>);
impl UiView for bool {
fn into_message(self, item: Handle<UiNode>) -> UiMessage {
CheckBoxMessage::checked(item, MessageDirection::ToWidget, Some(self))
}
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode> {
CheckBoxBuilder::new(WidgetBuilder::new())
.checked(Some(self))
.build(ctx)
}
}
impl UiView for Color {
fn into_message(self, item: Handle<UiNode>) -> UiMessage {
ColorFieldMessage::color(item, MessageDirection::ToWidget, self)
}
fn make_view(self, ctx: &mut BuildContext) -> Handle<UiNode> {
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<UiNode>, 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<MaterialResource>, 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::<Vec<_>>();
@@ -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::<PopupMessage>()
{
} 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::<MenuItemMessage>() {
} 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::<WidgetMessage>() {
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::<f32>::Value(value)) =
message.data()
{
Some(MaterialProperty::Float(*value))
} else if let Some(NumericUpDownMessage::<i32>::Value(value)) =
message.data()
{
Some(MaterialProperty::Int(*value))
} else if let Some(NumericUpDownMessage::<u32>::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<MaterialProperty> {
if let Some(NumericUpDownMessage::<f32>::Value(value)) = message.data() {
Some(MaterialProperty::Float(*value))
} else if let Some(NumericUpDownMessage::<i32>::Value(value)) = message.data() {
Some(MaterialProperty::Int(*value))
} else if let Some(NumericUpDownMessage::<u32>::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<MaterialEditor>,
}
impl EditorPlugin for MaterialPlugin {
fn on_start(&mut self, editor: &mut Editor) {
let container = &editor.plugins.get_mut::<InspectorPlugin>().property_editors;
container.insert(MaterialPropertyEditorDefinition {
sender: Mutex::new(editor.message_sender.clone()),
});
container.insert(InheritablePropertyEditorDefinition::<MaterialResource>::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,
));
}
}
+9
View File
@@ -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;
@@ -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<PathBuf> {
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<SerializationContext>,
resource_manager: ResourceManager,
) {
) -> Option<Self> {
if let Some(FileSelectorMessage::Commit(path)) = message.data::<FileSelectorMessage>() {
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<PathFixer>,
open_path_fixer: Handle<UiNode>,
}
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);
}
}
@@ -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<RagdollWizard>,
open_ragdoll_wizard: Handle<UiNode>,
}
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::<GameScene>());
let graph = &mut editor.engine.scenes[game_scene.scene].graph;
wizard.handle_ui_message(message, ui, graph, game_scene, &editor.message_sender);
}
}
+324
View File
@@ -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<PropertyEditorDefinitionContainer> {
let container = crate::plugins::inspector::editors::make_property_editors_container(sender);
container.insert(InspectablePropertyEditorDefinition::<GeneralSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<GraphicsSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<SelectionSettings>::new());
container.insert(EnumPropertyEditorDefinition::<ShadowMapPrecision>::new());
container.insert(EnumPropertyEditorDefinition::<ScriptEditor>::new());
container.insert(InspectablePropertyEditorDefinition::<DebuggingSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<CsmSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<QualitySettings>::new());
container.insert(InspectablePropertyEditorDefinition::<CameraSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<
MoveInteractionModeSettings,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
RotateInteractionModeSettings,
>::new());
container.insert(InspectablePropertyEditorDefinition::<ModelSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<NavmeshSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<KeyBindings>::new());
container.insert(InspectablePropertyEditorDefinition::<TerrainKeyBindings>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildSettings>::new());
container.insert(VecCollectionPropertyEditorDefinition::<EnvironmentVariable>::new());
container.insert(InspectablePropertyEditorDefinition::<EnvironmentVariable>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BuildProfile>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildProfile>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BuildCommand>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildCommand>::new());
container.insert(HotKeyPropertyEditorDefinition);
Arc::new(container)
}
pub struct SettingsWindow {
pub window: Handle<UiNode>,
ok: Handle<UiNode>,
default: Handle<UiNode>,
inspector: Handle<UiNode>,
}
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<UiNode>,
) -> Option<Self> {
let ui = engine.user_interfaces.first_mut();
if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
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<SettingsWindow>,
open_settings: Handle<UiNode>,
}
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,
);
}
}
+129
View File
@@ -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<UiNode>,
text: Handle<UiNode>,
open_ui_stats: Handle<UiNode>,
}
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
),
));
}
}
+1 -1
View File
@@ -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::{
+26 -28
View File
@@ -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<UiNode> {
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,
+20 -17
View File
@@ -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<UiNode>,
@@ -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),
);
+1 -1
View File
@@ -93,6 +93,6 @@ impl AssetPreviewGenerator for TileSetPreview {
_resource: &UntypedResource,
_resource_manager: &ResourceManager,
) -> Option<UntypedResource> {
load_image(include_bytes!("../../../resources/tile_set.png"))
load_image!("../../../resources/tile_set.png")
}
}
+2 -2
View File
@@ -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,
}))
+2 -2
View File
@@ -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);
+10 -9
View File
@@ -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::{
+162 -45
View File
@@ -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<PropertyDescriptorData>),
ChooseFocus,
}
impl PropertySelectorMessage {
define_constructor!(PropertySelectorMessage:Selection => fn selection(Vec<PropertyDescriptorData>), 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<PropertyDescriptorData>,
selected_property_paths: Vec<PropertyDescriptorData>,
tree_root: Handle<UiNode>,
search_bar: Handle<UiNode>,
scroll_viewer: Handle<UiNode>,
}
define_widget_deref!(PropertySelector);
uuid_provider!(PropertySelector = "8e58e123-48a1-4e18-9e90-fd35a1669bdc");
impl PropertySelector {
fn find_selected_tree_items(&self, ui: &UserInterface) -> Vec<Handle<UiNode>> {
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::<Tree>() {
if self.selected_property_paths.iter().any(|path| {
path.path
== tree
.user_data_cloned::<PropertyDescriptorData>()
.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::<PropertySelectorMessage>() {
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<PropertyDescriptor>,
allowed_types: Option<FxHashSet<TypeId>>,
selected_property_paths: Vec<PropertyDescriptorData>,
}
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<PropertyDescriptorData>) -> Self {
self.selected_property_paths = paths;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
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::<PropertySelector>()
.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::<PropertySelector>()
.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<PropertyDescriptor>,
allowed_types: Option<FxHashSet<TypeId>>,
selected_property_paths: Vec<PropertyDescriptorData>,
}
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<PropertyDescriptorData>) -> Self {
self.selected_property_paths = paths;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
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)
});
}
}
+99 -44
View File
@@ -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<ErasedHandle>),
ChooseFocus,
}
impl NodeSelectorMessage {
define_constructor!(NodeSelectorMessage:Hierarchy => fn hierarchy(HierarchyNode), layout: false);
define_constructor!(NodeSelectorMessage:Selection => fn selection(Vec<ErasedHandle>), 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::<NodeSelector>()
.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::<NodeSelector>()
.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)
});
}
}
+42 -34
View File
@@ -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::<Window>(self.window)
.unwrap()
.is_globally_visible()
{
return;
}
let scene = &engine.scenes[game_scene.scene];
let context = InspectorContext::from_object(
+8 -20
View File
@@ -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::<WidgetMessage>()
{
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 {
+6 -237
View File
@@ -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<UiNode>,
ok: Handle<UiNode>,
default: Handle<UiNode>,
inspector: Handle<UiNode>,
}
#[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<PropertyEditorDefinitionContainer> {
let container = make_property_editors_container(sender);
container.insert(InspectablePropertyEditorDefinition::<GeneralSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<GraphicsSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<SelectionSettings>::new());
container.insert(EnumPropertyEditorDefinition::<ShadowMapPrecision>::new());
container.insert(EnumPropertyEditorDefinition::<ScriptEditor>::new());
container.insert(InspectablePropertyEditorDefinition::<DebuggingSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<CsmSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<QualitySettings>::new());
container.insert(InspectablePropertyEditorDefinition::<CameraSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<
MoveInteractionModeSettings,
>::new());
container.insert(InspectablePropertyEditorDefinition::<
RotateInteractionModeSettings,
>::new());
container.insert(InspectablePropertyEditorDefinition::<ModelSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<NavmeshSettings>::new());
container.insert(InspectablePropertyEditorDefinition::<KeyBindings>::new());
container.insert(InspectablePropertyEditorDefinition::<TerrainKeyBindings>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildSettings>::new());
container.insert(VecCollectionPropertyEditorDefinition::<EnvironmentVariable>::new());
container.insert(InspectablePropertyEditorDefinition::<EnvironmentVariable>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BuildProfile>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildProfile>::new());
container.insert(VecCollectionPropertyEditorDefinition::<BuildCommand>::new());
container.insert(InspectablePropertyEditorDefinition::<BuildCommand>::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::<ButtonMessage>() {
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!");
}
}
}
}
+6 -2
View File
@@ -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(
+3 -3
View File
@@ -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,
+16 -16
View File
@@ -123,41 +123,41 @@ impl<'a> WorldViewerDataProvider for UiSceneWorldViewerDataProvider<'a> {
// Containers
if node.cast::<Canvas>().is_some() {
load_image(include_bytes!("../../resources/canvas-icon.png"))
load_image!("../../resources/canvas-icon.png")
} else if node.cast::<Screen>().is_some() {
load_image(include_bytes!("../../resources/screen-icon.png"))
load_image!("../../resources/screen-icon.png")
} else if node.cast::<Grid>().is_some() {
load_image(include_bytes!("../../resources/grid-icon.png"))
load_image!("../../resources/grid-icon.png")
} else if node.cast::<StackPanel>().is_some() {
load_image(include_bytes!("../../resources/stackPanel-icon.png"))
load_image!("../../resources/stackPanel-icon.png")
} else if node.cast::<Window>().is_some() {
load_image(include_bytes!("../../resources/window-icon.png"))
load_image!("../../resources/window-icon.png")
} else if node.cast::<MessageBox>().is_some() {
load_image(include_bytes!("../../resources/messageBox-icon.png"))
load_image!("../../resources/messageBox-icon.png")
} else if node.cast::<Menu>().is_some() {
load_image(include_bytes!("../../resources/menu-icon.png"))
load_image!("../../resources/menu-icon.png")
} else if node.cast::<Popup>().is_some() {
load_image(include_bytes!("../../resources/popup-icon.png"))
load_image!("../../resources/popup-icon.png")
}
// Visual
else if node.cast::<Text>().is_some() {
load_image(include_bytes!("../../resources/text-icon.png"))
load_image!("../../resources/text-icon.png")
} else if node.cast::<Image>().is_some() {
load_image(include_bytes!("../../resources/image-icon.png"))
load_image!("../../resources/image-icon.png")
} else if node.cast::<Border>().is_some() {
load_image(include_bytes!("../../resources/border-icon.png"))
load_image!("../../resources/border-icon.png")
}
// Controls
else if node.cast::<Button>().is_some() {
load_image(include_bytes!("../../resources/button-icon.png"))
load_image!("../../resources/button-icon.png")
} else if node.cast::<CheckBox>().is_some() {
load_image(include_bytes!("../../resources/checkbox-icon.png"))
load_image!("../../resources/checkbox-icon.png")
} else if node.cast::<ListView>().is_some() {
load_image(include_bytes!("../../resources/list-icon.png"))
load_image!("../../resources/list-icon.png")
} else if node.cast::<FileBrowser>().is_some() {
load_image(include_bytes!("../../resources/fileBrowser-icon.png"))
load_image!("../../resources/fileBrowser-icon.png")
} else if node.cast::<Inspector>().is_some() {
load_image(include_bytes!("../../resources/inspector-icon.png"))
load_image!("../../resources/inspector-icon.png")
} else {
None
}
-2
View File
@@ -32,8 +32,6 @@ use crate::fyrox::{
use std::{fs::File, io::Read, path::Path};
pub mod doc;
pub mod path_fixer;
pub mod ragdoll;
/// True if `a` and `b` have the same length, and every element of `a` is equal to some element of `b`
/// and every element of `b` is equal to some element of `a`.
+19 -4
View File
@@ -42,6 +42,8 @@ use crate::fyrox::{
},
};
use crate::{load_image, message::MessageSender, utils::make_node_name, Message};
use fyrox::gui::style::resource::StyleResourceExt;
use fyrox::gui::style::Style;
use std::{
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
@@ -196,9 +198,7 @@ impl Control for SceneItem {
.on_row(0)
.on_column(2),
)
.with_opt_texture(load_image(include_bytes!(
"../../../resources/warning.png"
)))
.with_opt_texture(load_image!("../../../resources/warning.png"))
.build(&mut ui.build_ctx());
ui.send_message(WidgetMessage::link(
@@ -325,7 +325,7 @@ impl SceneItemBuilder {
WidgetBuilder::new()
.with_foreground(
self.text_brush
.unwrap_or(Brush::Solid(fyrox::gui::COLOR_FOREGROUND)),
.unwrap_or(ctx.style.get_or_default(Style::BRUSH_FOREGROUND)),
)
.with_margin(Thickness::left(1.0))
.on_column(1)
@@ -363,3 +363,18 @@ impl SceneItemBuilder {
ctx.add_node(UiNode::new(item))
}
}
#[cfg(test)]
mod test {
use crate::world::graph::item::SceneItemBuilder;
use fyrox::gui::tree::TreeBuilder;
use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| {
SceneItemBuilder::new(TreeBuilder::new(WidgetBuilder::new()))
.build(ctx, Default::default())
});
}
}
+68 -45
View File
@@ -54,6 +54,9 @@ use crate::{
world::WorldViewerItemContextMenu,
Engine, Message, MessageDirection, PasteCommand,
};
use fyrox::engine::SerializationContext;
use fyrox::gui::constructor::WidgetConstructorContainer;
use fyrox::gui::menu::SortingPredicate;
use std::{any::TypeId, path::PathBuf};
pub struct SceneNodeContextMenu {
@@ -95,7 +98,11 @@ fn resource_path_of_first_selected_node(
}
impl SceneNodeContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
pub fn new(
serialization_context: &SerializationContext,
widget_constructors_container: &WidgetConstructorContainer,
ctx: &mut BuildContext,
) -> Self {
let delete_selection;
let copy_selection;
let save_as_prefab;
@@ -103,12 +110,16 @@ impl SceneNodeContextMenu {
let make_root;
let open_asset;
let reset_inheritable_properties;
let create_parent;
let create_child;
let replace_with;
let (create_child_entity_menu, create_child_entity_menu_root_items) =
CreateEntityMenu::new(ctx);
let (create_parent_entity_menu, create_parent_entity_menu_root_items) =
CreateEntityMenu::new(ctx);
let (replace_with_menu, replace_with_menu_root_items) = CreateEntityMenu::new(ctx);
let create_child_entity_menu =
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
let create_parent_entity_menu =
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
let replace_with_menu =
CreateEntityMenu::new(serialization_context, widget_constructors_container, ctx);
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
@@ -132,34 +143,37 @@ impl SceneNodeContextMenu {
save_as_prefab = create_menu_item("Save As Prefab...", vec![], ctx);
save_as_prefab
})
.with_child(
MenuItemBuilder::new(
.with_child({
create_parent = MenuItemBuilder::new(
WidgetBuilder::new().with_min_size(Vector2::new(120.0, 22.0)),
)
.with_content(MenuItemContent::text("Create Parent"))
.with_items(create_parent_entity_menu_root_items)
.build(ctx),
)
.with_child(
MenuItemBuilder::new(
.with_items(create_parent_entity_menu.root_items.clone())
.build(ctx);
create_parent
})
.with_child({
create_child = MenuItemBuilder::new(
WidgetBuilder::new().with_min_size(Vector2::new(120.0, 22.0)),
)
.with_content(MenuItemContent::text("Create Child"))
.with_items(create_child_entity_menu_root_items)
.build(ctx),
)
.with_items(create_child_entity_menu.root_items.clone())
.build(ctx);
create_child
})
.with_child({
make_root = create_menu_item("Make Root", vec![], ctx);
make_root
})
.with_child(
MenuItemBuilder::new(
.with_child({
replace_with = MenuItemBuilder::new(
WidgetBuilder::new().with_min_size(Vector2::new(120.0, 22.0)),
)
.with_content(MenuItemContent::text("Replace With"))
.with_items(replace_with_menu_root_items)
.build(ctx),
)
.with_items(replace_with_menu.root_items.clone())
.build(ctx);
replace_with
})
.with_child({
open_asset = create_menu_item("Open Asset", vec![], ctx);
open_asset
@@ -176,8 +190,13 @@ impl SceneNodeContextMenu {
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
// TODO: Not sure if this is the right place for this dialog.
let save_as_prefab_dialog = make_save_file_selector(ctx, PathBuf::from("unnamed.rgs"));
for item in [create_child, create_parent, replace_with] {
ctx.inner().send_message(MenuItemMessage::sort(
item,
MessageDirection::ToWidget,
SortingPredicate::sort_by_text(),
))
}
Self {
create_child_entity_menu,
@@ -186,7 +205,7 @@ impl SceneNodeContextMenu {
copy_selection,
placement_target: Default::default(),
save_as_prefab,
save_as_prefab_dialog,
save_as_prefab_dialog: Default::default(),
replace_with_menu,
paste,
make_root,
@@ -210,6 +229,7 @@ impl SceneNodeContextMenu {
sender,
controller,
editor_selection,
engine,
) {
if let Some(graph_selection) = editor_selection.as_graph() {
if let Some(parent) = graph_selection.nodes().first() {
@@ -221,6 +241,7 @@ impl SceneNodeContextMenu {
sender,
controller,
editor_selection,
engine,
) {
if let Some(graph_selection) = editor_selection.as_graph() {
if let Some(first) = graph_selection.nodes().first() {
@@ -262,10 +283,13 @@ impl SceneNodeContextMenu {
}
}
}
} else if let Some(replacement) =
self.replace_with_menu
.handle_ui_message(message, sender, controller, editor_selection)
{
} else if let Some(replacement) = self.replace_with_menu.handle_ui_message(
message,
sender,
controller,
editor_selection,
engine,
) {
if let Some(graph_selection) = editor_selection.as_graph() {
if let Some(first) = graph_selection.nodes().first() {
sender.do_command(ReplaceNodeCommand {
@@ -310,23 +334,22 @@ impl SceneNodeContextMenu {
}
}
} else if message.destination() == self.save_as_prefab {
engine
.user_interfaces
.first_mut()
.send_message(WindowMessage::open_modal(
self.save_as_prefab_dialog,
MessageDirection::ToWidget,
true,
true,
));
engine
.user_interfaces
.first_mut()
.send_message(FileSelectorMessage::root(
self.save_as_prefab_dialog,
MessageDirection::ToWidget,
Some(std::env::current_dir().unwrap()),
));
let ui = engine.user_interfaces.first_mut();
self.save_as_prefab_dialog =
make_save_file_selector(&mut ui.build_ctx(), PathBuf::from("unnamed.rgs"));
ui.send_message(WindowMessage::open_modal(
self.save_as_prefab_dialog,
MessageDirection::ToWidget,
true,
true,
));
ui.send_message(FileSelectorMessage::root(
self.save_as_prefab_dialog,
MessageDirection::ToWidget,
Some(std::env::current_dir().unwrap()),
));
} else if message.destination() == self.make_root {
if let Some(graph_selection) = editor_selection.as_graph() {
if let Some(first) = graph_selection.nodes.first() {
+6 -6
View File
@@ -133,17 +133,17 @@ impl<'a> WorldViewerDataProvider for EditorSceneWrapper<'a> {
fn icon_of(&self, node: ErasedHandle) -> Option<UntypedResource> {
let node = self.scene.graph.try_get(node.into()).unwrap();
if node.is_point_light() || node.is_directional_light() || node.is_spot_light() {
load_image(include_bytes!("../../../resources/light.png"))
load_image!("../../../resources/light.png")
} else if node.is_joint() || node.is_joint2d() {
load_image(include_bytes!("../../../resources/joint.png"))
load_image!("../../../resources/joint.png")
} else if node.is_rigid_body() || node.is_rigid_body2d() {
load_image(include_bytes!("../../../resources/rigid_body.png"))
load_image!("../../../resources/rigid_body.png")
} else if node.is_collider() || node.is_collider2d() {
load_image(include_bytes!("../../../resources/collider.png"))
load_image!("../../../resources/collider.png")
} else if node.is_sound() {
load_image(include_bytes!("../../../resources/sound_source.png"))
load_image!("../../../resources/sound_source.png")
} else {
load_image(include_bytes!("../../../resources/cube.png"))
load_image!("../../../resources/cube.png")
}
}
+11 -14
View File
@@ -47,7 +47,7 @@ use crate::{
window::{WindowBuilder, WindowTitle},
wrap_panel::WrapPanelBuilder,
BuildContext, Orientation, RcUiNodeHandle, Thickness, UiNode, UserInterface,
VerticalAlignment, BRUSH_BRIGHT_BLUE, BRUSH_PRIMARY,
VerticalAlignment,
},
},
gui::make_image_button_with_tooltip,
@@ -58,6 +58,8 @@ use crate::{
world::graph::item::{DropAnchor, SceneItem, SceneItemBuilder, SceneItemMessage},
Mode, Settings,
};
use fyrox::gui::style::resource::StyleResourceExt;
use fyrox::gui::style::Style;
use rust_fuzzy_search::fuzzy_compare;
use std::{
borrow::Cow,
@@ -156,7 +158,7 @@ fn make_graph_node_item(
.with_text_brush(if is_instance {
Brush::Solid(Color::opaque(160, 160, 200))
} else {
Brush::Solid(fyrox::gui::COLOR_FOREGROUND)
ctx.style.get_or_default(Style::BRUSH_FOREGROUND)
})
.with_name(name.deref().to_owned())
.with_entity_handle(handle)
@@ -245,9 +247,7 @@ impl WorldViewer {
ctx,
size,
size,
load_image(include_bytes!(
"../../resources/collapse.png"
)),
load_image!("../../resources/collapse.png"),
"Collapse Everything",
Some(0),
);
@@ -258,9 +258,7 @@ impl WorldViewer {
ctx,
size,
size,
load_image(include_bytes!(
"../../resources/expand.png"
)),
load_image!("../../resources/expand.png"),
"Expand Everything",
Some(1),
);
@@ -271,9 +269,7 @@ impl WorldViewer {
ctx,
size,
size,
load_image(include_bytes!(
"../../resources/locate.png"
)),
load_image!("../../resources/locate.png"),
"Locate Selection",
Some(2),
);
@@ -376,10 +372,11 @@ impl WorldViewer {
let element = ButtonBuilder::new(WidgetBuilder::new().with_height(16.0))
.with_back(
DecoratorBuilder::new(BorderBuilder::new(
WidgetBuilder::new().with_foreground(BRUSH_PRIMARY),
WidgetBuilder::new()
.with_foreground(ctx.style.get_or_default(Style::BRUSH_PRIMARY)),
))
.with_normal_brush(BRUSH_PRIMARY)
.with_hover_brush(BRUSH_BRIGHT_BLUE)
.with_normal_brush(ctx.style.get_or_default(Style::BRUSH_PRIMARY))
.with_hover_brush(ctx.style.get_or_default(Style::BRUSH_BRIGHT_BLUE))
.build(ctx),
)
.with_content(
+55
View File
@@ -30,6 +30,7 @@ use crate::core::{
visitor::prelude::*,
ImmutableString,
};
use std::any::TypeId;
use std::{
any,
any::Any,
@@ -137,6 +138,60 @@ pub enum ValueType {
UnitQuaternionF64,
}
impl ValueType {
/// Converts the value type into its respective type id.
pub fn into_type_id(self) -> TypeId {
match self {
ValueType::Bool => TypeId::of::<bool>(),
ValueType::F32 => TypeId::of::<f32>(),
ValueType::F64 => TypeId::of::<f64>(),
ValueType::U64 => TypeId::of::<u64>(),
ValueType::I64 => TypeId::of::<i64>(),
ValueType::U32 => TypeId::of::<u32>(),
ValueType::I32 => TypeId::of::<i32>(),
ValueType::U16 => TypeId::of::<u16>(),
ValueType::I16 => TypeId::of::<i16>(),
ValueType::U8 => TypeId::of::<u8>(),
ValueType::I8 => TypeId::of::<i8>(),
ValueType::Vector2Bool => TypeId::of::<Vector2<bool>>(),
ValueType::Vector2F32 => TypeId::of::<Vector2<f32>>(),
ValueType::Vector2F64 => TypeId::of::<Vector2<f64>>(),
ValueType::Vector2U64 => TypeId::of::<Vector2<u64>>(),
ValueType::Vector2I64 => TypeId::of::<Vector2<i64>>(),
ValueType::Vector2U32 => TypeId::of::<Vector2<u32>>(),
ValueType::Vector2I32 => TypeId::of::<Vector2<i32>>(),
ValueType::Vector2U16 => TypeId::of::<Vector2<u16>>(),
ValueType::Vector2I16 => TypeId::of::<Vector2<i16>>(),
ValueType::Vector2U8 => TypeId::of::<Vector2<u8>>(),
ValueType::Vector2I8 => TypeId::of::<Vector2<i8>>(),
ValueType::Vector3Bool => TypeId::of::<Vector3<bool>>(),
ValueType::Vector3F32 => TypeId::of::<Vector3<f32>>(),
ValueType::Vector3F64 => TypeId::of::<Vector3<f64>>(),
ValueType::Vector3U64 => TypeId::of::<Vector3<u64>>(),
ValueType::Vector3I64 => TypeId::of::<Vector3<i64>>(),
ValueType::Vector3U32 => TypeId::of::<Vector3<u32>>(),
ValueType::Vector3I32 => TypeId::of::<Vector3<i32>>(),
ValueType::Vector3U16 => TypeId::of::<Vector3<u16>>(),
ValueType::Vector3I16 => TypeId::of::<Vector3<i16>>(),
ValueType::Vector3U8 => TypeId::of::<Vector3<u8>>(),
ValueType::Vector3I8 => TypeId::of::<Vector3<i8>>(),
ValueType::Vector4Bool => TypeId::of::<Vector4<bool>>(),
ValueType::Vector4F32 => TypeId::of::<Vector4<f32>>(),
ValueType::Vector4F64 => TypeId::of::<Vector4<f64>>(),
ValueType::Vector4U64 => TypeId::of::<Vector4<u64>>(),
ValueType::Vector4I64 => TypeId::of::<Vector4<i64>>(),
ValueType::Vector4U32 => TypeId::of::<Vector4<u32>>(),
ValueType::Vector4I32 => TypeId::of::<Vector4<i32>>(),
ValueType::Vector4U16 => TypeId::of::<Vector4<u16>>(),
ValueType::Vector4I16 => TypeId::of::<Vector4<i16>>(),
ValueType::Vector4U8 => TypeId::of::<Vector4<u8>>(),
ValueType::Vector4I8 => TypeId::of::<Vector4<i8>>(),
ValueType::UnitQuaternionF32 => TypeId::of::<UnitQuaternion<f32>>(),
ValueType::UnitQuaternionF64 => TypeId::of::<UnitQuaternion<f64>>(),
}
}
}
impl Default for ValueType {
fn default() -> Self {
Self::F32

Some files were not shown because too many files have changed in this diff Show More