diff --git a/project-manager/resources/flame.png b/project-manager/resources/flame.png new file mode 100644 index 000000000..1f768456f Binary files /dev/null and b/project-manager/resources/flame.png differ diff --git a/project-manager/src/build.rs b/project-manager/src/build.rs index 9fc18d466..108b36596 100644 --- a/project-manager/src/build.rs +++ b/project-manager/src/build.rs @@ -18,8 +18,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use fyrox::gui::style::resource::StyleResourceExt; -use fyrox::gui::style::Style; use fyrox::{ core::{parking_lot::Mutex, pool::Handle}, gui::{ @@ -29,6 +27,7 @@ use fyrox::{ message::{MessageDirection, UiMessage}, scroll_viewer::{ScrollViewerBuilder, ScrollViewerMessage}, stack_panel::StackPanelBuilder, + style::{resource::StyleResourceExt, Style}, text::{TextBuilder, TextMessage}, widget::WidgetBuilder, window::{WindowBuilder, WindowMessage, WindowTitle}, @@ -55,7 +54,7 @@ pub struct BuildWindow { } impl BuildWindow { - pub fn new(ctx: &mut BuildContext) -> Self { + pub fn new(project: &str, ctx: &mut BuildContext) -> Self { let log_text; let stop; let scroll_viewer; @@ -70,7 +69,7 @@ impl BuildWindow { TextBuilder::new( WidgetBuilder::new().with_margin(Thickness::uniform(1.0)), ) - .with_text("Please wait while your game is building...\nLog:") + .with_text(format!("Please wait while the {project} is building...")) .build(ctx), ) .with_child( @@ -120,7 +119,7 @@ impl BuildWindow { .add_column(Column::stretch()) .build(ctx), ) - .with_title(WindowTitle::text("Building the Game...")) + .with_title(WindowTitle::text(format!("Building the {project}..."))) .build(ctx); Self { diff --git a/project-manager/src/manager.rs b/project-manager/src/manager.rs index 184c63322..fb14939ce 100644 --- a/project-manager/src/manager.rs +++ b/project-manager/src/manager.rs @@ -22,8 +22,7 @@ use crate::{ build::BuildWindow, project::ProjectWizard, settings::{Project, Settings}, - utils, - utils::{is_production_ready, load_image, make_button}, + utils::{self, is_production_ready, load_image, make_button}, }; use fyrox::{ core::{color::Color, log::Log, pool::Handle}, @@ -43,11 +42,13 @@ use fyrox::{ screen::ScreenBuilder, searchbar::{SearchBarBuilder, SearchBarMessage}, stack_panel::StackPanelBuilder, + style::{resource::StyleResourceExt, Style}, text::TextBuilder, utils::make_simple_tooltip, widget::{WidgetBuilder, WidgetMessage}, window::{WindowBuilder, WindowMessage}, - BuildContext, Orientation, Thickness, UiNode, UserInterface, VerticalAlignment, + BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface, + VerticalAlignment, }, }; use std::{path::Path, process::Stdio}; @@ -70,7 +71,12 @@ pub struct ProjectManager { import_project_dialog: Handle, } -fn make_project_item(name: &str, path: &Path, ctx: &mut BuildContext) -> Handle { +fn make_project_item( + name: &str, + path: &Path, + hot_reload: bool, + ctx: &mut BuildContext, +) -> Handle { let icon = ImageBuilder::new( WidgetBuilder::new() .with_margin(Thickness::uniform(4.0)) @@ -112,20 +118,41 @@ fn make_project_item(name: &str, path: &Path, ctx: &mut BuildContext) -> Handle< .add_row(Row::auto()) .build(ctx); + let hot_reload = ImageBuilder::new( + WidgetBuilder::new() + .on_row(0) + .on_column(2) + .with_width(18.0) + .with_height(18.0) + .with_margin(Thickness::uniform(2.0)) + .with_visibility(hot_reload) + .with_horizontal_alignment(HorizontalAlignment::Right) + .with_vertical_alignment(VerticalAlignment::Top), + ) + .with_opt_texture(load_image(include_bytes!("../resources/flame.png"))) + .build(ctx); + DecoratorBuilder::new( BorderBuilder::new( WidgetBuilder::new() .with_margin(Thickness::uniform(1.0)) .with_child( - GridBuilder::new(WidgetBuilder::new().with_child(icon).with_child(item)) - .add_column(Column::auto()) - .add_column(Column::stretch()) - .add_row(Row::auto()) - .build(ctx), + GridBuilder::new( + WidgetBuilder::new() + .with_child(icon) + .with_child(item) + .with_child(hot_reload), + ) + .add_column(Column::auto()) + .add_column(Column::stretch()) + .add_column(Column::auto()) + .add_row(Row::auto()) + .build(ctx), ), ) .with_corner_radius(4.0f32.into()), ) + .with_selected_brush(ctx.style.property(Style::BRUSH_BRIGHT_BLUE)) .build(ctx) } @@ -133,7 +160,14 @@ fn make_project_items(settings: &Settings, ctx: &mut BuildContext) -> Vec>() } @@ -178,8 +212,12 @@ impl ProjectManager { .add_row(Row::auto()) .build(ctx); - let create = make_button("+ Create", 100.0, 25.0, 0, ctx); - let import = make_button("Import", 100.0, 25.0, 1, ctx); + let create_tooltip = "Opens a new dialog window that creates a new project with specified \ + settings."; + let import_tooltip = "Allows you to import an existing project in the project manager."; + + let create = make_button("+ Create", 100.0, 25.0, 0, Some(create_tooltip), ctx); + let import = make_button("Import", 100.0, 25.0, 1, Some(import_tooltip), ctx); let search_bar = SearchBarBuilder::new( WidgetBuilder::new() .with_tab_index(Some(2)) @@ -200,20 +238,37 @@ impl ProjectManager { .with_orientation(Orientation::Horizontal) .build(ctx); - let edit = make_button("Edit", 100.0, 25.0, 3, ctx); - let run = make_button("Run", 100.0, 25.0, 4, ctx); - let delete = make_button("Delete", 100.0, 25.0, 5, ctx); - let hot_reload = CheckBoxBuilder::new(WidgetBuilder::new().with_tooltip( - make_simple_tooltip(ctx, "Run the project with code hot reloading support. \ - Significantly reduces iteration times, but might result in subtle bugs due to experimental \ - and unsafe nature of code hot reloading."), - )) - .build(ctx); + let edit_tooltip = "Build the editor and run it."; + let run_tooltip = "Build the game and run it."; + let delete_tooltip = "Delete the entire project with all its assets. \ + WARNING: This is irreversible operation and permanently deletes your project!"; + + let edit = make_button("Edit", 130.0, 25.0, 3, Some(edit_tooltip), ctx); + let run = make_button("Run", 130.0, 25.0, 4, Some(run_tooltip), ctx); + let delete = make_button("Delete", 130.0, 25.0, 5, Some(delete_tooltip), ctx); + let hot_reload = CheckBoxBuilder::new( + WidgetBuilder::new() + .with_margin(Thickness::uniform(1.0)) + .with_tooltip(make_simple_tooltip( + ctx, + "Run the project with code hot reloading support. \ + Significantly reduces iteration times, but might result in subtle bugs due to \ + experimental and unsafe nature of code hot reloading.", + )), + ) + .with_content( + TextBuilder::new(WidgetBuilder::new().with_margin(Thickness::left(2.0))) + .with_font_size(16.0f32.into()) + .with_text("Hot Reloading") + .build(ctx), + ) + .build(ctx); let project_controls = StackPanelBuilder::new( WidgetBuilder::new() .with_enabled(false) .on_column(1) + .with_child(hot_reload) .with_child(edit) .with_child(run) .with_child(delete), @@ -296,7 +351,7 @@ impl ProjectManager { } fn try_import(&mut self, path: &Path, ui: &mut UserInterface) { - let manifest_path = utils::folder_to_manifest_path(path.clone()); + let manifest_path = utils::folder_to_manifest_path(path); let metadata = match utils::read_crate_metadata(&manifest_path) { Ok(metadata) => metadata, @@ -330,7 +385,6 @@ impl ProjectManager { Log::err(format!( "{manifest_path:?} does not contain a game package!" )); - return; } } @@ -368,7 +422,7 @@ impl ProjectManager { match new_process.spawn() { Ok(mut new_process) => { - let mut build_window = BuildWindow::new(&mut ui.build_ctx()); + let mut build_window = BuildWindow::new("editor", &mut ui.build_ctx()); build_window.listen(new_process.stderr.take().unwrap(), ui); @@ -385,7 +439,7 @@ impl ProjectManager { match new_process.spawn() { Ok(mut new_process) => { - let mut build_window = BuildWindow::new(&mut ui.build_ctx()); + let mut build_window = BuildWindow::new("game", &mut ui.build_ctx()); build_window.listen(new_process.stderr.take().unwrap(), ui); @@ -436,11 +490,16 @@ impl ProjectManager { // TODO: Filter projects. self.refresh(ui); } - } else if let Some(CheckBoxMessage::Check(Some(_value))) = message.data() { + } else if let Some(CheckBoxMessage::Check(Some(value))) = message.data() { if message.destination() == self.hot_reload && message.direction() == MessageDirection::FromWidget { - // TODO: Switch to respective mode. + if let Some(selection) = self.selection { + if let Some(project) = self.settings.projects.get_mut(selection) { + project.hot_reload = *value; + self.refresh(ui); + } + } } } else if let Some(FileSelectorMessage::Commit(path)) = message.data() { if message.destination() == self.import_project_dialog { diff --git a/project-manager/src/project.rs b/project-manager/src/project.rs index b23bf596e..3f48be24a 100644 --- a/project-manager/src/project.rs +++ b/project-manager/src/project.rs @@ -178,8 +178,8 @@ impl ProjectWizard { .with_selected(1) .build(ctx); - let create = make_button("Create", 100.0, 22.0, 0, ctx); - let cancel = make_button("Cancel", 100.0, 22.0, 0, ctx); + let create = make_button("Create", 100.0, 22.0, 0, None, ctx); + let cancel = make_button("Cancel", 100.0, 22.0, 0, None, ctx); let buttons = StackPanelBuilder::new( WidgetBuilder::new() .on_row(1) diff --git a/project-manager/src/utils.rs b/project-manager/src/utils.rs index cc368b785..80496d276 100644 --- a/project-manager/src/utils.rs +++ b/project-manager/src/utils.rs @@ -19,6 +19,7 @@ // SOFTWARE. use cargo_metadata::Metadata; +use fyrox::gui::utils::make_simple_tooltip; use fyrox::{ asset::untyped::UntypedResource, core::pool::Handle, @@ -72,24 +73,29 @@ pub fn make_button( width: f32, height: f32, tab_index: usize, + tooltip: Option<&str>, ctx: &mut BuildContext, ) -> Handle { - ButtonBuilder::new( - WidgetBuilder::new() - .with_width(width) - .with_height(height) - .with_tab_index(Some(tab_index)) - .with_margin(Thickness::uniform(1.0)), - ) - .with_content( - TextBuilder::new(WidgetBuilder::new()) - .with_text(text) - .with_font_size(16.0.into()) - .with_vertical_text_alignment(VerticalAlignment::Center) - .with_horizontal_text_alignment(HorizontalAlignment::Center) - .build(ctx), - ) - .build(ctx) + let mut widget_builder = WidgetBuilder::new() + .with_width(width) + .with_height(height) + .with_tab_index(Some(tab_index)) + .with_margin(Thickness::uniform(1.0)); + + if let Some(tooltip) = tooltip { + widget_builder = widget_builder.with_tooltip(make_simple_tooltip(ctx, tooltip)); + } + + ButtonBuilder::new(widget_builder) + .with_content( + TextBuilder::new(WidgetBuilder::new()) + .with_text(text) + .with_font_size(16.0.into()) + .with_vertical_text_alignment(VerticalAlignment::Center) + .with_horizontal_text_alignment(HorizontalAlignment::Center) + .build(ctx), + ) + .build(ctx) } pub fn load_image(data: &[u8]) -> Option {