Files
Fyrox/editor/src/configurator.rs
T
2024-08-11 11:45:33 +03:00

380 lines
15 KiB
Rust

// 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::gui::text::TextMessage;
use crate::fyrox::{
core::{
algebra::Vector2,
pool::Handle,
scope_profile,
visitor::{Visit, VisitResult, Visitor},
},
gui::{
border::BorderBuilder,
button::{ButtonBuilder, ButtonMessage},
decorator::DecoratorBuilder,
file_browser::{FileSelectorBuilder, FileSelectorMessage, Filter},
formatted_text::WrapMode,
grid::{Column, GridBuilder, Row},
list_view::{ListViewBuilder, ListViewMessage},
message::{MessageDirection, UiMessage},
stack_panel::StackPanelBuilder,
text::TextBuilder,
text_box::TextBoxBuilder,
widget::{WidgetBuilder, WidgetMessage},
window::{WindowBuilder, WindowMessage, WindowTitle},
BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, VerticalAlignment,
},
};
use crate::message::MessageSender;
use crate::{Engine, Message};
use std::{
env,
path::{Path, PathBuf},
};
#[derive(Default, Eq, PartialEq, Visit)]
struct HistoryEntry {
work_dir: PathBuf,
}
pub const HISTORY_PATH: &str = "history.bin";
pub struct Configurator {
pub window: Handle<UiNode>,
work_dir_browser: Handle<UiNode>,
select_work_dir: Handle<UiNode>,
ok: Handle<UiNode>,
sender: MessageSender,
work_dir: PathBuf,
tb_work_dir: Handle<UiNode>,
lv_history: Handle<UiNode>,
history: Vec<HistoryEntry>,
}
fn make_history_entry_widget(ctx: &mut BuildContext, entry: &HistoryEntry) -> Handle<UiNode> {
DecoratorBuilder::new(BorderBuilder::new(
WidgetBuilder::new()
.with_height(18.0)
.with_margin(Thickness {
left: 1.0,
top: 0.0,
right: 1.0,
bottom: 1.0,
})
.with_child(
TextBuilder::new(WidgetBuilder::new().with_margin(Thickness::left(5.0)))
.with_text(format!("{}", entry.work_dir.display(),))
.with_vertical_text_alignment(VerticalAlignment::Center)
.build(ctx),
),
))
.build(ctx)
}
impl Configurator {
pub fn new(sender: MessageSender, ctx: &mut BuildContext) -> Self {
let select_work_dir;
let ok;
let tb_work_dir;
let current_path = env::current_dir().unwrap();
let filter = Filter::new(|p: &Path| p.is_dir());
let folder_browser = FileSelectorBuilder::new(
WindowBuilder::new(WidgetBuilder::new().with_width(300.0).with_height(400.0))
.open(false)
.with_title(WindowTitle::text("Select Working Directory")),
)
.with_filter(filter)
.build(ctx);
// Load history.
let mut history: Vec<HistoryEntry> = Vec::new();
if let Ok(mut visitor) =
fyrox::core::futures::executor::block_on(Visitor::load_binary(HISTORY_PATH))
{
history.visit("History", &mut visitor).unwrap();
}
// Remove entries with invalid paths.
history = history
.into_iter()
.filter(|e| e.work_dir.exists())
.collect::<Vec<_>>();
let message = "Please select the working directory of \
your current project. In most cases it will be the root folder \
of your project";
let lv_history;
let window = WindowBuilder::new(
WidgetBuilder::new()
.with_width(370.0)
.with_height(250.0)
.with_min_size(Vector2::new(370.0, 250.0)),
)
.with_title(WindowTitle::text("Configure Editor"))
.open(false)
.can_close(false)
.with_content(
GridBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_child(
TextBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(1.0)))
.with_text(message)
.with_wrap(WrapMode::Word)
.build(ctx),
)
.with_child(
GridBuilder::new(
WidgetBuilder::new()
.on_row(1)
.with_child(
TextBuilder::new(
WidgetBuilder::new()
.on_row(0)
.on_column(0)
.with_margin(Thickness::uniform(1.0))
.with_vertical_alignment(VerticalAlignment::Center),
)
.with_text("Working Directory")
.build(ctx),
)
.with_child({
tb_work_dir = TextBoxBuilder::new(
WidgetBuilder::new()
.on_row(0)
.on_column(1)
.with_margin(Thickness::uniform(1.0))
.with_enabled(false),
)
.with_text(
(current_path.clone())
.into_os_string()
.into_string()
.unwrap(),
)
.with_vertical_text_alignment(VerticalAlignment::Center)
.build(ctx);
tb_work_dir
})
.with_child({
select_work_dir = ButtonBuilder::new(
WidgetBuilder::new()
.with_tab_index(Some(0))
.on_row(0)
.on_column(2)
.with_margin(Thickness::uniform(1.0)),
)
.with_text("...")
.build(ctx);
select_work_dir
}),
)
.add_row(Row::strict(25.0))
.add_column(Column::strict(120.0))
.add_column(Column::stretch())
.add_column(Column::strict(25.0))
.build(ctx),
)
.with_child(
TextBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(5.0))
.on_row(2),
)
.with_text("Previous Configurations")
.with_horizontal_text_alignment(HorizontalAlignment::Center)
.build(ctx),
)
.with_child({
lv_history = ListViewBuilder::new(
WidgetBuilder::new().with_tab_index(Some(1)).on_row(3),
)
.with_items(
history
.iter()
.map(|entry| make_history_entry_widget(ctx, entry))
.collect(),
)
.build(ctx);
lv_history
})
.with_child(
StackPanelBuilder::new(
WidgetBuilder::new()
.on_row(4)
.with_horizontal_alignment(HorizontalAlignment::Right)
.with_vertical_alignment(VerticalAlignment::Bottom)
.with_child({
ok = ButtonBuilder::new(
WidgetBuilder::new()
.with_tab_index(Some(2))
.with_enabled(true) // Enabled by default.
.with_width(80.0)
.with_height(25.0)
.with_margin(Thickness::uniform(1.0)),
)
.with_text("OK")
.build(ctx);
ok
}),
)
.with_orientation(Orientation::Horizontal)
.build(ctx),
),
)
.add_row(Row::auto())
.add_row(Row::auto())
.add_row(Row::auto())
.add_row(Row::strict(80.0))
.add_row(Row::stretch())
.add_column(Column::stretch())
.build(ctx),
)
.build(ctx);
Self {
window,
work_dir_browser: folder_browser,
select_work_dir,
ok,
sender,
tb_work_dir,
work_dir: current_path,
lv_history,
history,
}
}
fn validate(&mut self, engine: &mut Engine) {
let is_valid_scene_path = self.work_dir.exists();
engine
.user_interfaces
.first_mut()
.send_message(WidgetMessage::enabled(
self.ok,
MessageDirection::ToWidget,
is_valid_scene_path,
));
}
pub fn handle_ui_message(&mut self, message: &UiMessage, engine: &mut Engine) {
scope_profile!();
if let Some(WindowMessage::Close) = message.data::<WindowMessage>() {
if message.destination() == self.window {
// Save history for next editor runs.
let mut visitor = Visitor::new();
self.history.visit("History", &mut visitor).unwrap();
visitor.save_binary(HISTORY_PATH).unwrap();
}
} else if let Some(ListViewMessage::SelectionChanged(selected_indices)) =
message.data::<ListViewMessage>()
{
if let Some(index) = selected_indices.first().cloned() {
if message.destination() == self.lv_history
&& message.direction() == MessageDirection::FromWidget
{
let entry = &self.history[index];
self.work_dir.clone_from(&entry.work_dir);
engine
.user_interfaces
.first_mut()
.send_message(TextMessage::text(
self.tb_work_dir,
MessageDirection::ToWidget,
self.work_dir.to_string_lossy().to_string(),
));
self.validate(engine);
}
}
} else if let Some(FileSelectorMessage::Commit(path)) =
message.data::<FileSelectorMessage>()
{
if message.destination() == self.work_dir_browser {
if let Ok(work_dir) = path.clone().canonicalize() {
self.work_dir = work_dir;
engine
.user_interfaces
.first_mut()
.send_message(TextMessage::text(
self.tb_work_dir,
MessageDirection::ToWidget,
self.work_dir.to_string_lossy().to_string(),
));
self.validate(engine);
}
}
} else if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
if message.destination() == self.ok {
self.sender.send(Message::Configure {
working_directory: self.work_dir.clone(),
});
let new_entry = HistoryEntry {
work_dir: self.work_dir.clone(),
};
if !self.history.iter().any(|e| e == &new_entry) {
self.history.push(new_entry);
let widget = make_history_entry_widget(
&mut engine.user_interfaces.first_mut().build_ctx(),
self.history.last().unwrap(),
);
engine
.user_interfaces
.first_mut()
.send_message(ListViewMessage::add_item(
self.lv_history,
MessageDirection::ToWidget,
widget,
));
}
engine
.user_interfaces
.first_mut()
.send_message(WindowMessage::close(
self.window,
MessageDirection::ToWidget,
));
} else if message.destination() == self.select_work_dir {
engine
.user_interfaces
.first_mut()
.send_message(WindowMessage::open_modal(
self.work_dir_browser,
MessageDirection::ToWidget,
true,
true,
));
}
}
}
}