mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-11 22:50:08 +00:00
added settings window in project manager + open in ide ability
- `open in ide` uses configurable shell command
This commit is contained in:
+2
-2
@@ -170,7 +170,7 @@ use crate::{
|
||||
world::{graph::menu::SceneNodeContextMenu, graph::EditorSceneWrapper, WorldViewer},
|
||||
};
|
||||
use fyrox::gui::log::LogPanel;
|
||||
use fyrox_build_tools::BuildCommand;
|
||||
use fyrox_build_tools::CommandDescriptor;
|
||||
pub use message::Message;
|
||||
use plugins::inspector::InspectorPlugin;
|
||||
use std::process::Stdio;
|
||||
@@ -303,7 +303,7 @@ pub fn make_save_file_selector(
|
||||
pub enum Mode {
|
||||
Edit,
|
||||
Build {
|
||||
queue: VecDeque<BuildCommand>,
|
||||
queue: VecDeque<CommandDescriptor>,
|
||||
process: Option<std::process::Child>,
|
||||
},
|
||||
Play {
|
||||
|
||||
@@ -67,7 +67,7 @@ use crate::{
|
||||
},
|
||||
Editor, MSG_SYNC_FLAG,
|
||||
};
|
||||
use fyrox_build_tools::{BuildCommand, BuildProfile, EnvironmentVariable};
|
||||
use fyrox_build_tools::{BuildProfile, CommandDescriptor, EnvironmentVariable};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn make_property_editors_container(
|
||||
@@ -100,8 +100,8 @@ fn make_property_editors_container(
|
||||
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(VecCollectionPropertyEditorDefinition::<CommandDescriptor>::new());
|
||||
container.insert(InspectablePropertyEditorDefinition::<CommandDescriptor>::new());
|
||||
container.insert(HotKeyPropertyEditorDefinition);
|
||||
Arc::new(container)
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ pub struct EnvironmentVariable {
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Clone, Debug, Default, Reflect, TypeUuidProvider)]
|
||||
#[type_uuid(id = "67b93136-17fe-4776-b5f0-f4a9ef3d8972")]
|
||||
pub struct BuildCommand {
|
||||
pub struct CommandDescriptor {
|
||||
pub command: String,
|
||||
pub args: Vec<String>,
|
||||
pub environment_variables: Vec<EnvironmentVariable>,
|
||||
}
|
||||
|
||||
impl BuildCommand {
|
||||
impl CommandDescriptor {
|
||||
pub fn make_command(&self) -> std::process::Command {
|
||||
let mut command = std::process::Command::new(&self.command);
|
||||
|
||||
@@ -52,7 +52,7 @@ impl BuildCommand {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BuildCommand {
|
||||
impl Display for CommandDescriptor {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for var in self.environment_variables.iter() {
|
||||
write!(f, "{}=\"{}\" ", var.name, var.value)?;
|
||||
@@ -73,11 +73,11 @@ impl Display for BuildCommand {
|
||||
pub struct BuildProfile {
|
||||
pub name: String,
|
||||
#[reflect(description = "A set of commands that will be used to build your game.")]
|
||||
pub build_commands: Vec<BuildCommand>,
|
||||
pub build_commands: Vec<CommandDescriptor>,
|
||||
#[reflect(description = "A set of commands that will be used to run your game. \
|
||||
This set of commands will be executed right after build commands (if the \
|
||||
build was successful)")]
|
||||
pub run_command: BuildCommand,
|
||||
pub run_command: CommandDescriptor,
|
||||
}
|
||||
|
||||
impl BuildProfile {
|
||||
@@ -91,7 +91,7 @@ impl BuildProfile {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_and_run_queue(&self) -> VecDeque<BuildCommand> {
|
||||
pub fn build_and_run_queue(&self) -> VecDeque<CommandDescriptor> {
|
||||
let mut queue = self.build_commands.iter().cloned().collect::<VecDeque<_>>();
|
||||
queue.push_back(self.run_command.clone());
|
||||
queue
|
||||
@@ -100,7 +100,7 @@ impl BuildProfile {
|
||||
pub fn debug() -> Self {
|
||||
BuildProfile {
|
||||
name: "Debug".to_string(),
|
||||
build_commands: vec![BuildCommand {
|
||||
build_commands: vec![CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"build".to_string(),
|
||||
@@ -109,7 +109,7 @@ impl BuildProfile {
|
||||
],
|
||||
environment_variables: vec![],
|
||||
}],
|
||||
run_command: BuildCommand {
|
||||
run_command: CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"run".to_string(),
|
||||
@@ -133,7 +133,7 @@ impl BuildProfile {
|
||||
name: "Debug (HR)".to_string(),
|
||||
build_commands: vec![
|
||||
Self::build_game(), // Build the executor.
|
||||
BuildCommand {
|
||||
CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"build".to_string(),
|
||||
@@ -163,9 +163,9 @@ impl BuildProfile {
|
||||
release_hot_reloading
|
||||
}
|
||||
|
||||
fn build_game() -> BuildCommand {
|
||||
fn build_game() -> CommandDescriptor {
|
||||
// Build the game plugin DLL first.
|
||||
BuildCommand {
|
||||
CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"build".to_string(),
|
||||
@@ -184,8 +184,8 @@ impl BuildProfile {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_hot_reload(package_name: &str) -> BuildCommand {
|
||||
BuildCommand {
|
||||
fn run_hot_reload(package_name: &str) -> CommandDescriptor {
|
||||
CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"run".to_string(),
|
||||
@@ -222,7 +222,7 @@ impl BuildProfile {
|
||||
pub fn debug_editor() -> Self {
|
||||
BuildProfile {
|
||||
name: "Debug Editor".to_string(),
|
||||
build_commands: vec![BuildCommand {
|
||||
build_commands: vec![CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"build".to_string(),
|
||||
@@ -231,7 +231,7 @@ impl BuildProfile {
|
||||
],
|
||||
environment_variables: vec![],
|
||||
}],
|
||||
run_command: BuildCommand {
|
||||
run_command: CommandDescriptor {
|
||||
command: "cargo".to_string(),
|
||||
args: vec![
|
||||
"run".to_string(),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -18,10 +18,11 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::settings::MANIFEST_PATH_VAR;
|
||||
use crate::{
|
||||
build::BuildWindow,
|
||||
project::ProjectWizard,
|
||||
settings::{Project, Settings},
|
||||
settings::{Project, Settings, SettingsWindow},
|
||||
upgrade::UpgradeTool,
|
||||
utils::{self, is_production_ready, load_image, make_button},
|
||||
};
|
||||
@@ -47,14 +48,14 @@ use fyrox::{
|
||||
stack_panel::StackPanelBuilder,
|
||||
style::{resource::StyleResourceExt, Style},
|
||||
text::{TextBuilder, TextMessage},
|
||||
utils::make_simple_tooltip,
|
||||
utils::{make_image_button_with_tooltip, make_simple_tooltip},
|
||||
widget::{WidgetBuilder, WidgetMessage},
|
||||
window::{WindowBuilder, WindowMessage, WindowTitle},
|
||||
BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface,
|
||||
VerticalAlignment,
|
||||
},
|
||||
};
|
||||
use fyrox_build_tools::{BuildCommand, BuildProfile};
|
||||
use fyrox_build_tools::{BuildProfile, CommandDescriptor};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
path::{Path, PathBuf},
|
||||
@@ -64,7 +65,7 @@ use std::{
|
||||
enum Mode {
|
||||
Normal,
|
||||
Build {
|
||||
queue: VecDeque<BuildCommand>,
|
||||
queue: VecDeque<CommandDescriptor>,
|
||||
process: Option<std::process::Child>,
|
||||
current_dir: PathBuf,
|
||||
},
|
||||
@@ -94,7 +95,10 @@ pub struct ProjectManager {
|
||||
deletion_confirmation_dialog: Handle<UiNode>,
|
||||
upgrade: Handle<UiNode>,
|
||||
locate: Handle<UiNode>,
|
||||
open_settings: Handle<UiNode>,
|
||||
open_ide: Handle<UiNode>,
|
||||
upgrade_tool: Option<UpgradeTool>,
|
||||
settings_window: Option<SettingsWindow>,
|
||||
}
|
||||
|
||||
fn make_project_item(
|
||||
@@ -294,9 +298,18 @@ impl ProjectManager {
|
||||
.with_height(25.0),
|
||||
)
|
||||
.build(ctx);
|
||||
let open_settings = make_image_button_with_tooltip(
|
||||
ctx,
|
||||
18.0,
|
||||
18.0,
|
||||
load_image(include_bytes!("../resources/gear.png")),
|
||||
"Settings\nHotkey: Ctrl+S",
|
||||
Some(7),
|
||||
);
|
||||
ctx[open_settings].set_column(3);
|
||||
|
||||
let message_count;
|
||||
let open_log = ButtonBuilder::new(WidgetBuilder::new().on_column(3).with_visibility(false))
|
||||
let open_log = ButtonBuilder::new(WidgetBuilder::new().on_column(4).with_visibility(false))
|
||||
.with_content(
|
||||
GridBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
@@ -341,12 +354,14 @@ impl ProjectManager {
|
||||
.with_child(create)
|
||||
.with_child(import)
|
||||
.with_child(search_bar)
|
||||
.with_child(open_settings)
|
||||
.with_child(open_log),
|
||||
)
|
||||
.add_column(Column::auto())
|
||||
.add_column(Column::auto())
|
||||
.add_column(Column::stretch())
|
||||
.add_column(Column::auto())
|
||||
.add_column(Column::auto())
|
||||
.add_row(Row::auto())
|
||||
.build(ctx);
|
||||
|
||||
@@ -362,13 +377,25 @@ impl ProjectManager {
|
||||
experimental and unsafe nature of code hot reloading.\
|
||||
\nHotkey: Ctrl+H";
|
||||
let locate_tooltip = "Opens project folder in the default OS file manager.\
|
||||
\nHotkey: Ctrl+O";
|
||||
\nHotkey: Ctrl+L";
|
||||
let open_ide_tooltip = "Opens project folder in the currently selected IDE \
|
||||
(can be changed in settings).\nHotkey: Ctrl+O";
|
||||
|
||||
let edit = make_button("Edit", 130.0, 25.0, 5, 0, 0, Some(edit_tooltip), ctx);
|
||||
let run = make_button("Run", 130.0, 25.0, 6, 0, 0, Some(run_tooltip), ctx);
|
||||
let delete = make_button("Delete", 130.0, 25.0, 7, 0, 0, Some(delete_tooltip), ctx);
|
||||
let upgrade = make_button("Upgrade", 130.0, 25.0, 8, 0, 0, Some(upgrade_tooltip), ctx);
|
||||
let locate = make_button("Locate", 130.0, 25.0, 9, 0, 0, Some(locate_tooltip), ctx);
|
||||
let open_ide = make_button(
|
||||
"Open IDE",
|
||||
130.0,
|
||||
25.0,
|
||||
9,
|
||||
0,
|
||||
0,
|
||||
Some(open_ide_tooltip),
|
||||
ctx,
|
||||
);
|
||||
let hot_reload = CheckBoxBuilder::new(
|
||||
WidgetBuilder::new()
|
||||
.with_tab_index(Some(4))
|
||||
@@ -392,7 +419,8 @@ impl ProjectManager {
|
||||
.with_child(run)
|
||||
.with_child(delete)
|
||||
.with_child(upgrade)
|
||||
.with_child(locate),
|
||||
.with_child(locate)
|
||||
.with_child(open_ide),
|
||||
)
|
||||
.build(ctx);
|
||||
|
||||
@@ -468,7 +496,10 @@ impl ProjectManager {
|
||||
deletion_confirmation_dialog: Default::default(),
|
||||
upgrade,
|
||||
locate,
|
||||
open_settings,
|
||||
open_ide,
|
||||
upgrade_tool: None,
|
||||
settings_window: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +590,8 @@ impl ProjectManager {
|
||||
if let Some(build_window) = self.build_window.as_mut() {
|
||||
build_window.update(ui);
|
||||
}
|
||||
|
||||
self.settings.try_save();
|
||||
}
|
||||
|
||||
fn try_import(&mut self, path: &Path, ui: &mut UserInterface) {
|
||||
@@ -689,6 +722,10 @@ impl ProjectManager {
|
||||
self.on_delete_clicked(ui);
|
||||
} else if button == self.locate {
|
||||
self.on_locate_click();
|
||||
} else if button == self.open_ide {
|
||||
self.on_open_ide_click();
|
||||
} else if button == self.open_settings {
|
||||
self.on_open_settings_click(ui);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,6 +735,32 @@ impl ProjectManager {
|
||||
Log::verify(open::that_detached(folder));
|
||||
}
|
||||
|
||||
fn on_open_ide_click(&mut self) {
|
||||
let project = some_or_return!(self.selection.and_then(|i| self.settings.projects.get(i)));
|
||||
let mut open_ide_command = self.settings.open_ide_command.clone();
|
||||
if let Some(manifest_path_arg) = open_ide_command
|
||||
.args
|
||||
.iter_mut()
|
||||
.find(|cmd| cmd.as_str() == MANIFEST_PATH_VAR)
|
||||
{
|
||||
*manifest_path_arg = project.manifest_path.to_string_lossy().to_string();
|
||||
} else {
|
||||
Log::warn(format!("{} variable is not specified!", MANIFEST_PATH_VAR));
|
||||
}
|
||||
let mut command = open_ide_command.make_command();
|
||||
if let Err(err) = command.spawn() {
|
||||
Log::err(format!(
|
||||
"Unable to open the IDE using {} command. Reason: {:?}",
|
||||
open_ide_command, err
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_open_settings_click(&mut self, ui: &mut UserInterface) {
|
||||
let ctx = &mut ui.build_ctx();
|
||||
self.settings_window = Some(SettingsWindow::new(&self.settings, ctx));
|
||||
}
|
||||
|
||||
fn run_build_profile(
|
||||
&mut self,
|
||||
name: &str,
|
||||
@@ -740,7 +803,9 @@ impl ProjectManager {
|
||||
KeyCode::KeyI if modifiers.control => self.on_import_clicked(ui),
|
||||
KeyCode::KeyC if modifiers.control => self.on_create_clicked(ui),
|
||||
KeyCode::KeyH if modifiers.control => self.on_hot_reload_changed(true, ui),
|
||||
KeyCode::KeyO if modifiers.control => self.on_locate_click(),
|
||||
KeyCode::KeyL if modifiers.control => self.on_locate_click(),
|
||||
KeyCode::KeyO if modifiers.control => self.on_open_ide_click(),
|
||||
KeyCode::KeyS if modifiers.control => self.on_open_settings_click(ui),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -758,6 +823,10 @@ impl ProjectManager {
|
||||
build_window.handle_ui_message(message, ui);
|
||||
}
|
||||
|
||||
if let Some(settings_window) = self.settings_window.take() {
|
||||
self.settings_window = settings_window.handle_ui_message(&mut self.settings, message);
|
||||
}
|
||||
|
||||
if let Some(upgrade_tool) = self.upgrade_tool.take() {
|
||||
if let Some(index) = self.selection {
|
||||
if let Some(project) = self.settings.projects.get(index) {
|
||||
|
||||
@@ -18,16 +18,55 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use fyrox::{
|
||||
core::{log::Log, pool::Handle, reflect::prelude::*},
|
||||
gui::{
|
||||
inspector::{
|
||||
editors::{
|
||||
collection::VecCollectionPropertyEditorDefinition,
|
||||
inspectable::InspectablePropertyEditorDefinition,
|
||||
PropertyEditorDefinitionContainer,
|
||||
},
|
||||
InspectorBuilder, InspectorContext,
|
||||
},
|
||||
inspector::{InspectorMessage, PropertyAction},
|
||||
message::{MessageDirection, UiMessage},
|
||||
scroll_viewer::ScrollViewerBuilder,
|
||||
widget::WidgetBuilder,
|
||||
window::{WindowBuilder, WindowMessage, WindowTitle},
|
||||
BuildContext, UiNode,
|
||||
},
|
||||
};
|
||||
use fyrox_build_tools::{CommandDescriptor, EnvironmentVariable};
|
||||
use ron::ser::PrettyConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs::File, io::Write, path::PathBuf};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::{fs::File, io::Write, path::PathBuf, sync::Arc};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub const MANIFEST_PATH_VAR: &str = "%MANIFEST_PATH%";
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Reflect, Debug)]
|
||||
pub struct SettingsData {
|
||||
#[serde(default = "default_open_ide_command")]
|
||||
#[reflect(
|
||||
description = "Defines a command to run an IDE in a project folder. This command \
|
||||
should use %MANIFEST_PATH% built-in variable to provide the selected project path to the \
|
||||
chosen IDE."
|
||||
)]
|
||||
pub open_ide_command: CommandDescriptor,
|
||||
#[reflect(hidden)]
|
||||
pub projects: Vec<Project>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn default_open_ide_command() -> CommandDescriptor {
|
||||
CommandDescriptor {
|
||||
command: "rustrover64".to_string(),
|
||||
args: vec![MANIFEST_PATH_VAR.to_string()],
|
||||
environment_variables: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsData {
|
||||
pub const PATH: &'static str = "pm_settings.ron";
|
||||
|
||||
pub fn load() -> Self {
|
||||
@@ -47,9 +86,119 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
data: SettingsData,
|
||||
need_save: bool,
|
||||
}
|
||||
|
||||
impl Deref for Settings {
|
||||
type Target = SettingsData;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Settings {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.need_save = true;
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn load() -> Self {
|
||||
Self {
|
||||
data: SettingsData::load(),
|
||||
need_save: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_save(&mut self) {
|
||||
if self.need_save {
|
||||
self.need_save = false;
|
||||
self.data.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Project {
|
||||
pub manifest_path: PathBuf,
|
||||
pub name: String,
|
||||
pub hot_reload: bool,
|
||||
}
|
||||
|
||||
pub struct SettingsWindow {
|
||||
window: Handle<UiNode>,
|
||||
inspector: Handle<UiNode>,
|
||||
}
|
||||
|
||||
impl SettingsWindow {
|
||||
pub fn new(settings: &Settings, ctx: &mut BuildContext) -> Self {
|
||||
let property_editors = PropertyEditorDefinitionContainer::with_default_editors();
|
||||
property_editors.insert(InspectablePropertyEditorDefinition::<CommandDescriptor>::new());
|
||||
property_editors
|
||||
.insert(VecCollectionPropertyEditorDefinition::<EnvironmentVariable>::new());
|
||||
property_editors.insert(InspectablePropertyEditorDefinition::<EnvironmentVariable>::new());
|
||||
let property_editors = Arc::new(property_editors);
|
||||
let context = InspectorContext::from_object(
|
||||
settings.deref(),
|
||||
ctx,
|
||||
property_editors,
|
||||
None,
|
||||
1,
|
||||
0,
|
||||
true,
|
||||
Default::default(),
|
||||
200.0,
|
||||
);
|
||||
let inspector = InspectorBuilder::new(WidgetBuilder::new())
|
||||
.with_context(context)
|
||||
.build(ctx);
|
||||
|
||||
let window = WindowBuilder::new(WidgetBuilder::new().with_width(300.0).with_height(400.0))
|
||||
.with_content(
|
||||
ScrollViewerBuilder::new(WidgetBuilder::new())
|
||||
.with_content(inspector)
|
||||
.build(ctx),
|
||||
)
|
||||
.open(false)
|
||||
.with_title(WindowTitle::text("Settings"))
|
||||
.with_remove_on_close(true)
|
||||
.build(ctx);
|
||||
|
||||
ctx.sender()
|
||||
.send(WindowMessage::open_modal(
|
||||
window,
|
||||
MessageDirection::ToWidget,
|
||||
true,
|
||||
true,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
Self { window, inspector }
|
||||
}
|
||||
|
||||
pub fn handle_ui_message(self, settings: &mut Settings, message: &UiMessage) -> Option<Self> {
|
||||
if let Some(WindowMessage::Close) = message.data() {
|
||||
if message.destination() == self.window {
|
||||
return None;
|
||||
}
|
||||
} else if let Some(InspectorMessage::PropertyChanged(args)) = message.data() {
|
||||
if message.destination() == self.inspector
|
||||
&& message.direction() == MessageDirection::FromWidget
|
||||
{
|
||||
PropertyAction::from_field_kind(&args.value).apply(
|
||||
&args.path(),
|
||||
settings.deref_mut(),
|
||||
&mut |result| {
|
||||
Log::verify(result);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user