use crate::{
asset::{
item::{AssetItem, AssetItemMessage},
preview::cache::IconRequest,
selector::AssetSelectorMixin,
},
fyrox::{
asset::manager::ResourceManager,
core::{
futures::executor::block_on, log::Log, make_relative_path, pool::Handle,
reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*,
},
graph::SceneGraph,
gui::{
button::{ButtonBuilder, ButtonMessage},
define_widget_deref,
grid::{Column, GridBuilder, Row},
image::{ImageBuilder, ImageMessage},
inspector::{
editors::{
PropertyEditorBuildContext, PropertyEditorDefinition, PropertyEditorInstance,
PropertyEditorMessageContext, PropertyEditorTranslationContext,
},
FieldKind, InspectorError, PropertyChanged,
},
message::{MessageDirection, UiMessage},
text::{TextBuilder, TextMessage},
utils::make_asset_preview_tooltip,
widget::{Widget, WidgetBuilder, WidgetMessage},
BuildContext, Control, Thickness, UiNode, UserInterface,
},
scene::mesh::surface::{SurfaceData, SurfaceResource},
},
message::MessageSender,
plugins::inspector::EditorEnvironment,
utils::make_pick_button,
Message,
};
use fyrox::gui::brush::Brush;
use fyrox::gui::button::Button;
use fyrox::gui::image::Image;
use fyrox::gui::message::MessageData;
use fyrox::gui::text::Text;
use std::{any::TypeId, sync::mpsc::Sender};
#[derive(Debug, PartialEq, Clone)]
pub enum SurfaceDataPropertyEditorMessage {
Value(SurfaceResource),
}
impl MessageData for SurfaceDataPropertyEditorMessage {}
#[derive(Clone, Visit, Reflect, Debug, ComponentProvider, TypeUuidProvider)]
#[type_uuid(id = "8461a183-4fd4-4f74-a4f4-7fd8e84bf423")]
#[reflect(derived_type = "UiNode")]
#[allow(dead_code)]
pub struct SurfaceDataPropertyEditor {
widget: Widget,
view: Handle<Button>,
surface_resource: SurfaceResource,
text: Handle<Text>,
image: Handle<Image>,
image_preview: Handle<Image>,
#[visit(skip)]
#[reflect(hidden)]
sender: Option<MessageSender>,
asset_selector_mixin: AssetSelectorMixin<SurfaceData>,
}
define_widget_deref!(SurfaceDataPropertyEditor);
impl Control for SurfaceDataPropertyEditor {
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if let Some(ButtonMessage::Click) = message.data() {
if message.destination == self.view {
if let Some(sender) = self.sender.as_ref() {
sender.send(Message::ViewSurfaceData(self.surface_resource.clone()));
}
}
} else if let Some(WidgetMessage::Drop(dropped)) = message.data() {
if message.destination() == self.handle() {
if let Some(item) = ui.node(*dropped).cast::<AssetItem>() {
let path = if self
.asset_selector_mixin
.resource_manager
.state()
.built_in_resources
.contains_key(&item.path)
{
Ok(item.path.clone())
} else {
make_relative_path(&item.path)
};
if let Ok(path) = path {
if let Some(request) = self
.asset_selector_mixin
.resource_manager
.try_request::<SurfaceData>(path)
{
if let Ok(value) = block_on(request) {
ui.send(
self.handle(),
SurfaceDataPropertyEditorMessage::Value(value),
);
}
}
}
}
}
} else if let Some(SurfaceDataPropertyEditorMessage::Value(surface_resource)) =
message.data_for(self.handle)
{
if &self.surface_resource != surface_resource {
self.surface_resource = surface_resource.clone();
ui.send_message(message.reverse());
ui.send(
self.text,
TextMessage::Text(surface_data_info(
&self.asset_selector_mixin.resource_manager,
surface_resource,
)),
);
self.asset_selector_mixin
.request_preview(self.handle, surface_resource);
}
} else if let Some(AssetItemMessage::Icon {
texture,
flip_y,
color,
}) = message.data_for(self.handle)
{
for widget in [self.image, self.image_preview] {
ui.send(widget, ImageMessage::Texture(texture.clone()));
ui.send(widget, ImageMessage::Flip(*flip_y));
ui.send(
widget,
WidgetMessage::Background(Brush::Solid(*color).into()),
)
}
}
self.asset_selector_mixin
.handle_ui_message(Some(&self.surface_resource), ui, message);
}
fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
self.asset_selector_mixin
.preview_ui_message(ui, message, |resource| {
UiMessage::for_widget(
self.handle,
SurfaceDataPropertyEditorMessage::Value(
resource.try_cast::<SurfaceData>().unwrap(),
),
)
})
}
}
fn surface_data_info(resource_manager: &ResourceManager, data: &SurfaceResource) -> String {
let use_count = data.use_count();
let id = data.key();
let kind = resource_manager
.resource_path(data.as_ref())
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| "External".to_string());
if data.is_ok() {
let guard = data.data_ref();
format!(
"{}\nVertices: {}\nTriangles: {}\nUse Count: {}\nId: {}",
kind,
guard.vertex_buffer.vertex_count(),
guard.geometry_buffer.len(),
use_count,
id
)
} else {
format!("{}\nNot loaded\nUse Count: {}\nId: {}", kind, use_count, id)
}
}
impl SurfaceDataPropertyEditor {
pub fn build(
ctx: &mut BuildContext,
surface_resource: SurfaceResource,
sender: MessageSender,
icon_request_sender: Sender<IconRequest>,
resource_manager: ResourceManager,
) -> Handle<SurfaceDataPropertyEditor> {
let view = ButtonBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.on_row(0)
.on_column(1)
.with_width(55.0)
.with_height(22.0),
)
.with_text("View...")
.build(ctx);
let select = make_pick_button(2, ctx);
let text = TextBuilder::new(
WidgetBuilder::new()
.on_row(0)
.on_column(0)
.with_margin(Thickness::uniform(1.0)),
)
.with_text(surface_data_info(&resource_manager, &surface_resource))
.build(ctx);
let (image_preview_tooltip, image_preview) = make_asset_preview_tooltip(None, ctx);
let image = ImageBuilder::new(
WidgetBuilder::new()
.on_column(0)
.with_width(52.0)
.with_height(52.0)
.with_tooltip(image_preview_tooltip)
.with_margin(Thickness::uniform(1.0)),
)
.build(ctx);
let content = GridBuilder::new(
WidgetBuilder::new()
.on_column(1)
.with_child(text)
.with_child(view)
.with_child(select),
)
.add_column(Column::stretch())
.add_column(Column::auto())
.add_column(Column::auto())
.add_row(Row::auto())
.build(ctx);
let widget = WidgetBuilder::new()
.with_preview_messages(true)
.with_child(
GridBuilder::new(WidgetBuilder::new().with_child(image).with_child(content))
.add_column(Column::auto())
.add_column(Column::stretch())
.add_row(Row::auto())
.build(ctx),
)
.with_allow_drop(true)
.build(ctx);
let editor = Self {
widget,
surface_resource: surface_resource.clone(),
view,
sender: Some(sender),
asset_selector_mixin: AssetSelectorMixin::new(
select,
icon_request_sender.clone(),
resource_manager,
),
text,
image,
image_preview,
};
let handle = ctx.add(editor);
Log::verify(icon_request_sender.send(IconRequest {
widget_handle: handle.to_base(),
resource: surface_resource.into_untyped(),
force_update: false,
}));
handle
}
}
#[derive(Debug)]
pub struct SurfaceDataPropertyEditorDefinition {
pub sender: MessageSender,
}
impl PropertyEditorDefinition for SurfaceDataPropertyEditorDefinition {
fn value_type_id(&self) -> TypeId {
TypeId::of::<SurfaceResource>()
}
fn create_instance(
&self,
ctx: PropertyEditorBuildContext,
) -> Result<PropertyEditorInstance, InspectorError> {
let value = ctx.property_info.cast_value::<SurfaceResource>()?;
let environment = EditorEnvironment::try_get_from(&ctx.environment)?;
Ok(PropertyEditorInstance::simple(
SurfaceDataPropertyEditor::build(
ctx.build_context,
value.clone(),
self.sender.clone(),
environment.icon_request_sender.clone(),
environment.resource_manager.clone(),
),
))
}
fn create_message(
&self,
ctx: PropertyEditorMessageContext,
) -> Result<Option<UiMessage>, InspectorError> {
let value = ctx.property_info.cast_value::<SurfaceResource>()?;
Ok(Some(UiMessage::for_widget(
ctx.instance,
SurfaceDataPropertyEditorMessage::Value(value.clone()),
)))
}
fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
if ctx.message.direction() == MessageDirection::FromWidget {
if let Some(SurfaceDataPropertyEditorMessage::Value(value)) = ctx.message.data() {
return Some(PropertyChanged {
name: ctx.name.to_string(),
value: FieldKind::object(value.clone()),
});
}
}
None
}
}