#![warn(missing_docs)]
use crate::button::Button;
use crate::file_browser::{FileSelector, PathFilter};
use crate::text_box::TextBox;
use crate::{
button::{ButtonBuilder, ButtonMessage},
core::{
pool::Handle, reflect::prelude::*, type_traits::prelude::*, uuid_provider,
variable::InheritableVariable, visitor::prelude::*,
},
file_browser::{FileSelectorBuilder, FileSelectorMessage},
grid::{Column, GridBuilder, Row},
message::{MessageData, UiMessage},
text::TextMessage,
text_box::TextBoxBuilder,
widget::{Widget, WidgetBuilder, WidgetMessage},
window::{WindowAlignment, WindowBuilder, WindowMessage, WindowTitle},
BuildContext, Control, Thickness, UiNode, UserInterface,
};
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use std::{path::Path, path::PathBuf};
#[derive(Debug, Clone, PartialEq)]
pub enum PathEditorMessage {
Path(PathBuf),
}
impl MessageData for PathEditorMessage {}
#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
#[reflect(derived_type = "UiNode")]
pub struct PathEditor {
pub widget: Widget,
pub text_field: InheritableVariable<Handle<TextBox>>,
pub select: InheritableVariable<Handle<Button>>,
pub selector: InheritableVariable<Handle<FileSelector>>,
pub path: InheritableVariable<PathBuf>,
pub file_types: PathFilter,
}
impl ConstructorProvider<UiNode, UserInterface> for PathEditor {
fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
GraphNodeConstructor::new::<Self>()
.with_variant("Path Editor", |ui| {
PathEditorBuilder::new(WidgetBuilder::new().with_name("Path Editor"))
.build(&mut ui.build_ctx())
.to_base()
.into()
})
.with_group("Input")
}
}
crate::define_widget_deref!(PathEditor);
uuid_provider!(PathEditor = "51cfe7ec-ec31-4354-9578-047004b213a1");
impl Control for PathEditor {
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.select {
self.selector.set_value_and_mark_modified(
FileSelectorBuilder::new(
WindowBuilder::new(
WidgetBuilder::new().with_width(300.0).with_height(450.0),
)
.open(false)
.with_title(WindowTitle::text("Select a Path")),
)
.with_filter(self.file_types.clone())
.build(&mut ui.build_ctx()),
);
ui.send(
*self.selector,
FileSelectorMessage::Path((*self.path).clone()),
);
ui.send(
*self.selector,
WindowMessage::Open {
alignment: WindowAlignment::Center,
modal: true,
focus_content: true,
},
);
ui.send(*self.selector, FileSelectorMessage::FocusCurrentPath);
}
} else if let Some(PathEditorMessage::Path(path)) = message.data_for(self.handle) {
if &*self.path != path {
self.path.set_value_and_mark_modified(path.clone());
ui.send(
*self.text_field,
TextMessage::Text(path.to_string_lossy().to_string()),
);
ui.try_send_response(message);
}
}
}
fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
if let Some(FileSelectorMessage::Commit(path)) = message.data() {
if message.destination() == *self.selector && &*self.path != path {
ui.send(*self.selector, WidgetMessage::Remove);
ui.send(self.handle, PathEditorMessage::Path(path.clone()));
}
}
}
}
pub struct PathEditorBuilder {
widget_builder: WidgetBuilder,
path: PathBuf,
file_types: PathFilter,
}
impl PathEditorBuilder {
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
path: Default::default(),
file_types: Default::default(),
}
}
pub fn with_path<P: AsRef<Path>>(mut self, path: P) -> Self {
path.as_ref().clone_into(&mut self.path);
self
}
pub fn with_file_types(mut self, filter: PathFilter) -> Self {
self.file_types = filter;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<PathEditor> {
let text_field;
let select;
let grid = GridBuilder::new(
WidgetBuilder::new()
.with_child({
text_field = TextBoxBuilder::new(
WidgetBuilder::new()
.on_column(0)
.with_margin(Thickness::uniform(1.0)),
)
.with_text(self.path.to_string_lossy())
.with_editable(false)
.build(ctx);
text_field
})
.with_child({
select = ButtonBuilder::new(
WidgetBuilder::new()
.on_column(1)
.with_width(30.0)
.with_margin(Thickness::uniform(1.0)),
)
.with_text("...")
.build(ctx);
select
}),
)
.add_row(Row::stretch())
.add_column(Column::stretch())
.add_column(Column::auto())
.build(ctx);
let path_editor = PathEditor {
widget: self
.widget_builder
.with_child(grid)
.with_preview_messages(true)
.build(ctx),
text_field: text_field.into(),
select: select.into(),
selector: Default::default(),
path: self.path.into(),
file_types: self.file_types,
};
ctx.add(path_editor)
}
}
#[cfg(test)]
mod test {
use crate::path::PathEditorBuilder;
use crate::{test::test_widget_deletion, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| PathEditorBuilder::new(WidgetBuilder::new()).build(ctx));
}
}