diff --git a/Content/Content.vcxitems b/Content/Content.vcxitems index 93303f353..a39875f28 100644 --- a/Content/Content.vcxitems +++ b/Content/Content.vcxitems @@ -597,11 +597,14 @@ + + + @@ -610,6 +613,7 @@ + diff --git a/Content/Content.vcxitems.filters b/Content/Content.vcxitems.filters index d0595d53c..6c2a8f3d2 100644 --- a/Content/Content.vcxitems.filters +++ b/Content/Content.vcxitems.filters @@ -572,6 +572,18 @@ scripts\character_controller\assets + + models\Sponza + + + scripts\character_controller + + + scripts\dungeon_generator + + + scripts\fighting_game + diff --git a/Content/models/Sponza/thumbnail.png b/Content/models/Sponza/thumbnail.png new file mode 100644 index 000000000..68cc1b267 Binary files /dev/null and b/Content/models/Sponza/thumbnail.png differ diff --git a/Content/scripts/character_controller/thumbnail.png b/Content/scripts/character_controller/thumbnail.png new file mode 100644 index 000000000..d9e991360 Binary files /dev/null and b/Content/scripts/character_controller/thumbnail.png differ diff --git a/Content/scripts/dungeon_generator/thumbnail.png b/Content/scripts/dungeon_generator/thumbnail.png new file mode 100644 index 000000000..7d535cd88 Binary files /dev/null and b/Content/scripts/dungeon_generator/thumbnail.png differ diff --git a/Content/scripts/fighting_game/thumbnail.png b/Content/scripts/fighting_game/thumbnail.png new file mode 100644 index 000000000..b4aeb54cc Binary files /dev/null and b/Content/scripts/fighting_game/thumbnail.png differ diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 0f0eaf852..51f5df494 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -46,6 +46,7 @@ set (SOURCE_FILES SpriteWindow.cpp FontWindow.cpp VoxelGridWindow.cpp + ContentBrowserWindow.cpp xatlas.cpp EmbeddedResources.cpp ) diff --git a/Editor/ContentBrowserWindow.cpp b/Editor/ContentBrowserWindow.cpp new file mode 100644 index 000000000..6d4ddd128 --- /dev/null +++ b/Editor/ContentBrowserWindow.cpp @@ -0,0 +1,356 @@ +#include "stdafx.h" +#include "ContentBrowserWindow.h" + +using namespace wi::ecs; +using namespace wi::scene; + +void ContentBrowserWindow::Create(EditorComponent* _editor) +{ + editor = _editor; + control_size = 30; + + wi::gui::Window::WindowControls controls = wi::gui::Window::WindowControls::ALL; + controls &= ~wi::gui::Window::WindowControls::RESIZE_BOTTOMLEFT; + wi::gui::Window::Create("Content Browser", controls); + + RemoveWidget(&scrollbar_horizontal); + + SetVisible(false); +} + +static const float separator = 140; +void ContentBrowserWindow::Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const +{ + wi::gui::Window::Render(canvas, cmd); + + if (!IsCollapsed()) + { + ApplyScissor(canvas, scissorRect, cmd); + wi::image::Params params; + params.pos = XMFLOAT3(translation.x + separator, translation.y + control_size, 0); + params.siz = XMFLOAT2(2, scale.y - control_size); + params.color = shadow_color; + wi::image::Draw(nullptr, params, cmd); + } +} + +void ContentBrowserWindow::Update(const wi::Canvas& canvas, float dt) +{ + wi::gui::Window::Update(canvas, dt); + + SetShadowRadius(6); + + static const float radius = 15; + + for (int i = 0; i < arraysize(wi::gui::Widget::sprites); ++i) + { + sprites[i].params.enableCornerRounding(); + sprites[i].params.corners_rounding[0].radius = radius; + sprites[i].params.corners_rounding[1].radius = radius; + sprites[i].params.corners_rounding[2].radius = radius; + sprites[i].params.corners_rounding[3].radius = radius; + resizeDragger_UpperLeft.sprites[i].params.enableCornerRounding(); + resizeDragger_UpperLeft.sprites[i].params.corners_rounding[0].radius = radius; + resizeDragger_UpperRight.sprites[i].params.enableCornerRounding(); + resizeDragger_UpperRight.sprites[i].params.corners_rounding[1].radius = radius; + resizeDragger_BottomLeft.sprites[i].params.enableCornerRounding(); + resizeDragger_BottomLeft.sprites[i].params.corners_rounding[2].radius = radius; + resizeDragger_BottomRight.sprites[i].params.enableCornerRounding(); + resizeDragger_BottomRight.sprites[i].params.corners_rounding[3].radius = radius; + + if (IsCollapsed()) + { + resizeDragger_UpperLeft.sprites[i].params.corners_rounding[2].radius = radius; + resizeDragger_UpperRight.sprites[i].params.corners_rounding[3].radius = radius; + } + else + { + resizeDragger_UpperLeft.sprites[i].params.corners_rounding[2].radius = 0; + resizeDragger_UpperRight.sprites[i].params.corners_rounding[3].radius = 0; + } + + openFolderButton.sprites[i].params.enableCornerRounding(); + openFolderButton.sprites[i].params.corners_rounding[0].radius = radius; + openFolderButton.sprites[i].params.corners_rounding[1].radius = radius; + openFolderButton.sprites[i].params.corners_rounding[2].radius = radius; + openFolderButton.sprites[i].params.corners_rounding[3].radius = radius; + } + + for (auto& x : folderButtons) + { + x.font.params.h_align = wi::font::WIFALIGN_LEFT; + for (auto& y : x.sprites) + { + y.params.enableCornerRounding(); + y.params.corners_rounding[3].radius = radius; + } + x.SetShadowRadius(0); + } + for (auto& x : itemButtons) + { + if (x.sprites[wi::gui::IDLE].textureResource.IsValid()) + { + x.sprites[wi::gui::IDLE].params.color = wi::Color::White(); + } + for (auto& y : x.sprites) + { + y.params.enableCornerRounding(); + y.params.corners_rounding[0].radius = radius; + y.params.corners_rounding[1].radius = radius; + y.params.corners_rounding[2].radius = radius; + y.params.corners_rounding[3].radius = radius; + } + x.SetShadowRadius(4); + } + + openFolderButton.SetShadowRadius(0); + + XMFLOAT4 color_on = sprites[wi::gui::FOCUS].params.color; + XMFLOAT4 color_off = sprites[wi::gui::IDLE].params.color; + + for (auto& x : folderButtons) + { + x.sprites[wi::gui::IDLE].params.color = color_off; + } + if (current_selection < SELECTION_COUNT) + { + folderButtons[current_selection].sprites[wi::gui::IDLE].params.color = color_on; + } +} + +void ContentBrowserWindow::ResizeLayout() +{ + wi::gui::Window::ResizeLayout(); + const float padding = 4; + const float width = GetWidgetAreaSize().x; + float y = padding; + + openFolderButton.Detach(); + openFolderButton.SetPos(XMFLOAT2(translation.x + padding, translation.y + scale.y - openFolderButton.GetSize().y - padding)); + openFolderButton.SetSize(XMFLOAT2(separator - padding * 2, openFolderButton.GetSize().y)); + openFolderButton.AttachTo(this); + + for (auto& x : folderButtons) + { + x.Detach(); + x.SetPos(XMFLOAT2(translation.x + padding, translation.y + control_size + y)); + x.SetSize(XMFLOAT2(separator - padding * 2, x.GetSize().y)); + y += x.GetSize().y + padding; + x.AttachTo(this); + } + + y = padding + 10; + + float sep = separator + 10; + float hoffset = sep; + for (auto& x : itemButtons) + { + x.SetPos(XMFLOAT2(hoffset, y)); + hoffset += x.GetSize().x + 15; + if (hoffset + x.GetSize().x >= width) + { + hoffset = sep; + y += x.GetSize().y + 40; + } + } +} + +void ContentBrowserWindow::RefreshContent() +{ +#ifdef PLATFORM_UWP + content_folder = wi::helper::GetCurrentPath() + "/"; +#else + content_folder = wi::helper::GetCurrentPath() + "/Content/"; + wi::helper::MakePathAbsolute(content_folder); + if (!wi::helper::FileExists(content_folder)) + { + content_folder = wi::helper::GetCurrentPath() + "/../Content/"; + wi::helper::MakePathAbsolute(content_folder); + } +#endif // PLATFORM_UWP + + float hei = 25; + float wid = 120; + + for (auto& x : folderButtons) + { + RemoveWidget(&x); + } + + if (wi::helper::DirectoryExists(content_folder + "scripts")) + { + wi::gui::Button& button = folderButtons[SELECTION_SCRIPTS]; + button.Create("Scripts"); + button.SetLocalizationEnabled(false); + button.SetSize(XMFLOAT2(wid, hei)); + button.OnClick([this](wi::gui::EventArgs args) { + SetSelection(SELECTION_SCRIPTS); + }); + AddWidget(&button, wi::gui::Window::AttachmentOptions::NONE); + if (current_selection == SELECTION_COUNT) + { + current_selection = SELECTION_SCRIPTS; + } + } + if (wi::helper::DirectoryExists(content_folder + "models")) + { + wi::gui::Button& button = folderButtons[SELECTION_MODELS]; + button.Create("Models"); + button.SetLocalizationEnabled(false); + button.SetSize(XMFLOAT2(wid, hei)); + button.OnClick([this](wi::gui::EventArgs args) { + SetSelection(SELECTION_MODELS); + }); + AddWidget(&button, wi::gui::Window::AttachmentOptions::NONE); + if (current_selection == SELECTION_COUNT) + { + current_selection = SELECTION_MODELS; + } + } + if (!editor->recentFilenames.empty()) + { + wi::gui::Button& button = folderButtons[SELECTION_RECENT]; + button.Create("Recently Opened"); + button.SetLocalizationEnabled(false); + button.SetSize(XMFLOAT2(wid, hei)); + button.OnClick([this](wi::gui::EventArgs args) { + SetSelection(SELECTION_RECENT); + }); + AddWidget(&button, wi::gui::Window::AttachmentOptions::NONE); + if (current_selection == SELECTION_COUNT) + { + current_selection = SELECTION_RECENT; + } + } + + if (current_selection != SELECTION_COUNT) + { + SetSelection(current_selection); + } +} +void ContentBrowserWindow::SetSelection(SELECTION selection) +{ + wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { + current_selection = selection; + for (auto& x : itemButtons) + { + RemoveWidget(&x); + } + itemButtons.clear(); + + RemoveWidget(&openFolderButton); + openFolderButton.Create(ICON_OPEN " Go to location"); + openFolderButton.SetSize(XMFLOAT2(60, 60)); + + switch (selection) + { + default: + case SELECTION_SCRIPTS: + AddItems(content_folder + "scripts/", "lua", ICON_SCRIPT); + openFolderButton.OnClick([this](wi::gui::EventArgs args) { + wi::helper::OpenUrl(content_folder + "scripts/"); + }); + AddWidget(&openFolderButton, wi::gui::Window::AttachmentOptions::NONE); + break; + case SELECTION_MODELS: + AddItems(content_folder + "models/", "wiscene", ICON_OBJECT); + AddItems(content_folder + "models/", "gltf", ICON_OBJECT); + AddItems(content_folder + "models/", "glb", ICON_OBJECT); + AddItems(content_folder + "models/", "obj", ICON_OBJECT); + openFolderButton.OnClick([this](wi::gui::EventArgs args) { + wi::helper::OpenUrl(content_folder + "models/"); + }); + AddWidget(&openFolderButton, wi::gui::Window::AttachmentOptions::NONE); + break; + case SELECTION_RECENT: + for (size_t i = 0; i < editor->recentFilenames.size(); ++i) + { + const std::string& filename = editor->recentFilenames[editor->recentFilenames.size() - 1 - i]; + std::string ext = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename)); + if (!ext.compare("LUA")) + { + AddItem(filename, ICON_SCRIPT); + } + if (!ext.compare("WISCENE")) + { + AddItem(filename, ICON_OBJECT); + } + if (!ext.compare("GLTF")) + { + AddItem(filename, ICON_OBJECT); + } + if (!ext.compare("GLB")) + { + AddItem(filename, ICON_OBJECT); + } + if (!ext.compare("OBJ")) + { + AddItem(filename, ICON_OBJECT); + } + } + break; + } + + for (auto& x : itemButtons) + { + AddWidget(&x); + } + + // Refresh theme: + editor->optionsWnd.generalWnd.themeCombo.SetSelected(editor->optionsWnd.generalWnd.themeCombo.GetSelected()); + + }); +} +void ContentBrowserWindow::AddItems(const std::string& folder, const std::string& extension, const std::string& icon) +{ + // Folders parse: + wi::helper::GetFolderNamesInDirectory(folder, [&](std::string foldername) { + std::string itemname = foldername.substr(folder.size()) + "." + extension; + std::string filename = foldername + "/" + itemname; + if (wi::helper::FileExists(filename)) + { + AddItem(filename, icon); + } + }); + + // Individual items: + wi::helper::GetFileNamesInDirectory(folder, [&](std::string filename) { + AddItem(filename, icon); + }, extension); +} +void ContentBrowserWindow::AddItem(const std::string& filename, const std::string& icon) +{ + static const XMFLOAT2 siz = XMFLOAT2(240, 120); + + std::string itemname = wi::helper::GetFileNameFromPath(filename); + std::string foldername = wi::helper::GetDirectoryFromPath(filename); + + wi::gui::Button& button = itemButtons.emplace_back(); + button.Create(icon); + button.SetSize(siz); + button.SetLocalizationEnabled(false); + button.SetDescription(itemname); + button.SetTooltip(filename); + button.OnClick([this, filename](wi::gui::EventArgs args) { + wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { + editor->Open(filename); + }); + this->SetVisible(false); + }); + button.font_description.params.h_align = wi::font::WIFALIGN_CENTER; + button.font_description.params.v_align = wi::font::WIFALIGN_TOP; + button.font.params.size = 42; + std::string thumbnailName = foldername + "/thumbnail.png"; + if (wi::helper::FileExists(thumbnailName)) + { + wi::Resource thumbnail = wi::resourcemanager::Load(thumbnailName); + if (thumbnail.IsValid()) + { + for (int i = 0; i < arraysize(sprites); ++i) + { + button.sprites[i].textureResource = thumbnail; + } + button.SetText(""); + } + } +} diff --git a/Editor/ContentBrowserWindow.h b/Editor/ContentBrowserWindow.h new file mode 100644 index 000000000..44edf9947 --- /dev/null +++ b/Editor/ContentBrowserWindow.h @@ -0,0 +1,36 @@ +#pragma once +class EditorComponent; + +class ContentBrowserWindow : public wi::gui::Window +{ +public: + void Create(EditorComponent* editor); + + EditorComponent* editor = nullptr; + + std::string content_folder; + enum SELECTION + { + SELECTION_SCRIPTS, + SELECTION_MODELS, + SELECTION_RECENT, + + SELECTION_COUNT + }; + SELECTION current_selection = SELECTION_COUNT; + wi::gui::Button folderButtons[SELECTION_COUNT]; + wi::vector itemButtons; + + wi::gui::Button openFolderButton; + + void RefreshContent(); + + void SetSelection(SELECTION selection); + void AddItems(const std::string& folder, const std::string& extension, const std::string& icon); + void AddItem(const std::string& filename, const std::string& icon); + + void Update(const wi::Canvas& canvas, float dt); + void Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const override; + void ResizeLayout() override; +}; + diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 1350bd436..dd8132006 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -213,6 +213,9 @@ void EditorComponent::ResizeLayout() aboutWindow.SetSize(XMFLOAT2(screenW / 2.0f, screenH / 1.5f)); aboutWindow.SetPos(XMFLOAT2(screenW / 2.0f - aboutWindow.scale.x / 2.0f, screenH / 2.0f - aboutWindow.scale.y / 2.0f)); + contentBrowserWnd.SetSize(XMFLOAT2(screenW / 1.6f, screenH / 1.2f)); + contentBrowserWnd.SetPos(XMFLOAT2(screenW / 2.0f - contentBrowserWnd.scale.x / 2.0f, screenH / 2.0f - contentBrowserWnd.scale.y / 2.0f)); + } void EditorComponent::Load() { @@ -415,6 +418,23 @@ void EditorComponent::Load() GetGUI().AddWidget(&openButton); + contentBrowserButton.Create("Content Browser"); + contentBrowserButton.SetLocalizationEnabled(wi::gui::LocalizationEnabled::Tooltip); + contentBrowserButton.SetShadowRadius(2); + contentBrowserButton.font.params.shadowColor = wi::Color::Transparent(); + contentBrowserButton.SetTooltip("Browse sample content."); + contentBrowserButton.SetColor(wi::Color(50, 100, 255, 180), wi::gui::WIDGETSTATE::IDLE); + contentBrowserButton.SetColor(wi::Color(120, 160, 255, 255), wi::gui::WIDGETSTATE::FOCUS); + contentBrowserButton.OnClick([&](wi::gui::EventArgs args) { + contentBrowserWnd.SetVisible(!contentBrowserWnd.IsVisible()); + if (contentBrowserWnd.IsVisible()) + { + contentBrowserWnd.RefreshContent(); + } + }); + GetGUI().AddWidget(&contentBrowserButton); + + logButton.Create("Backlog"); logButton.SetLocalizationEnabled(wi::gui::LocalizationEnabled::Tooltip); logButton.SetShadowRadius(2); @@ -659,6 +679,9 @@ void EditorComponent::Load() profilerWnd.Create(); GetGUI().AddWidget(&profilerWnd); + contentBrowserWnd.Create(this); + GetGUI().AddWidget(&contentBrowserWnd); + std::string theme = main->config.GetSection("options").GetText("theme"); if(theme.empty()) { @@ -694,6 +717,14 @@ void EditorComponent::Load() }; wi::helper::GetFileNamesInDirectory("fonts/", load_font, "TTF"); + size_t current_recent = 0; + auto& recent = main->config.GetSection("recent"); + while (recent.Has(std::to_string(current_recent).c_str())) + { + recentFilenames.push_back(recent.GetText(std::to_string(current_recent).c_str())); + current_recent++; + } + RenderPath2D::Load(); } void EditorComponent::Start() @@ -3609,6 +3640,32 @@ void EditorComponent::ConsumeHistoryOperation(bool undo) optionsWnd.RefreshEntityTree(); } +void EditorComponent::RegisterRecentlyUsed(const std::string& filename) +{ + for (size_t i = 0; i < recentFilenames.size();) + { + if (recentFilenames[i].compare(filename) == 0) + { + recentFilenames.erase(recentFilenames.begin() + i); + } + else + { + i++; + } + } + while (recentFilenames.size() >= maxRecentFilenames) + { + recentFilenames.erase(recentFilenames.begin()); + } + recentFilenames.push_back(filename); + auto& recent = main->config.GetSection("recent"); + for (size_t i = 0; i < recentFilenames.size(); ++i) + { + recent.Set(std::to_string(i).c_str(), recentFilenames[i]); + } + main->config.Commit(); +} + void EditorComponent::Open(const std::string& filename) { std::string extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename)); @@ -3630,6 +3687,7 @@ void EditorComponent::Open(const std::string& filename) playButton.SetScriptTip("dofile(\"" + last_script_path + "\")"); wi::lua::RunFile(filename); optionsWnd.RefreshEntityTree(); + RegisterRecentlyUsed(filename); return; } if (type == FileType::VIDEO) @@ -3667,6 +3725,8 @@ void EditorComponent::Open(const std::string& filename) return; } + RegisterRecentlyUsed(filename); + size_t camera_count_prev = GetCurrentScene().cameras.GetCount(); main->loader.addLoadingFunction([=](wi::jobsystem::JobArgs args) { @@ -3897,6 +3957,18 @@ void EditorComponent::UpdateTopMenuAnimation() openButton.SetText(openButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? ICON_OPEN " Open" : ICON_OPEN); } + if (contentBrowserButton.GetState() > wi::gui::WIDGETSTATE::IDLE && current_localization.Get((size_t)EditorLocalization::ContentBrowser) != nullptr) + { + tmp = ICON_CONTENT_BROWSER " "; + tmp += current_localization.Get((size_t)EditorLocalization::ContentBrowser); + contentBrowserButton.SetText(tmp); + } + else + { + contentBrowserButton.SetText(contentBrowserButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? ICON_CONTENT_BROWSER " Content" : ICON_CONTENT_BROWSER); + } + + if (logButton.GetState() > wi::gui::WIDGETSTATE::IDLE && current_localization.Get((size_t)EditorLocalization::Backlog) != nullptr) { tmp = ICON_BACKLOG " "; @@ -3997,6 +4069,7 @@ void EditorComponent::UpdateTopMenuAnimation() profilerButton.SetSize(XMFLOAT2(wi::math::Lerp(profilerButton.GetSize().x, profilerButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); cinemaButton.SetSize(XMFLOAT2(wi::math::Lerp(cinemaButton.GetSize().x, cinemaButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); logButton.SetSize(XMFLOAT2(wi::math::Lerp(logButton.GetSize().x, logButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); + contentBrowserButton.SetSize(XMFLOAT2(wi::math::Lerp(contentBrowserButton.GetSize().x, contentBrowserButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); openButton.SetSize(XMFLOAT2(wi::math::Lerp(openButton.GetSize().x, openButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); saveButton.SetSize(XMFLOAT2(wi::math::Lerp(saveButton.GetSize().x, saveButton.GetState() > wi::gui::WIDGETSTATE::IDLE ? wid_focus : wid_idle, lerp), hei)); @@ -4007,11 +4080,12 @@ void EditorComponent::UpdateTopMenuAnimation() cinemaButton.SetPos(XMFLOAT2(fullscreenButton.GetPos().x - cinemaButton.GetSize().x - padding, 0)); profilerButton.SetPos(XMFLOAT2(cinemaButton.GetPos().x - profilerButton.GetSize().x - padding, 0)); logButton.SetPos(XMFLOAT2(profilerButton.GetPos().x - logButton.GetSize().x - padding, 0)); - openButton.SetPos(XMFLOAT2(logButton.GetPos().x - openButton.GetSize().x - padding, 0)); + contentBrowserButton.SetPos(XMFLOAT2(logButton.GetPos().x - contentBrowserButton.GetSize().x - padding, 0)); + openButton.SetPos(XMFLOAT2(contentBrowserButton.GetPos().x - openButton.GetSize().x - padding, 0)); saveButton.SetPos(XMFLOAT2(openButton.GetPos().x - saveButton.GetSize().x - padding, 0)); - float static_pos = screenW - wid_idle * 11; + float static_pos = screenW - wid_idle * 12; dummyButton.SetSize(XMFLOAT2(wid_idle * 0.75f, hei)); dummyButton.SetPos(XMFLOAT2(static_pos - dummyButton.GetSize().x - 20, 0)); diff --git a/Editor/Editor.h b/Editor/Editor.h index 6e4574928..461dc299e 100644 --- a/Editor/Editor.h +++ b/Editor/Editor.h @@ -4,6 +4,7 @@ #include "OptionsWindow.h" #include "ComponentsWindow.h" #include "ProfilerWindow.h" +#include "ContentBrowserWindow.h" #include "IconDefinitions.h" class EditorLoadingScreen : public wi::LoadingScreen @@ -46,6 +47,7 @@ public: wi::gui::Button saveButton; wi::gui::Button openButton; + wi::gui::Button contentBrowserButton; wi::gui::Button logButton; wi::gui::Button profilerButton; wi::gui::Button cinemaButton; @@ -59,6 +61,7 @@ public: OptionsWindow optionsWnd; ComponentsWindow componentsWnd; ProfilerWindow profilerWnd; + ContentBrowserWindow contentBrowserWnd; wi::primitive::Ray pickRay; wi::physics::PickDragOperation physicsDragOp; @@ -136,6 +139,10 @@ public: wi::Archive& AdvanceHistory(); void ConsumeHistoryOperation(bool undo); + wi::vector recentFilenames; + size_t maxRecentFilenames = 10; + void RegisterRecentlyUsed(const std::string& filename); + void Open(const std::string& filename); void Save(const std::string& filename); void SaveAs(); @@ -209,6 +216,7 @@ enum class EditorLocalization // Top menu: Save, Open, + ContentBrowser, Backlog, Profiler, Cinema, @@ -227,6 +235,7 @@ static const char* EditorLocalizationStrings[] = { // Top menu: "Save", "Open", + "Content", "Backlog", "Profiler", "Cinema", diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems index 23693de96..215de9b8b 100644 --- a/Editor/Editor_SOURCE.vcxitems +++ b/Editor/Editor_SOURCE.vcxitems @@ -20,6 +20,7 @@ + @@ -146,6 +147,7 @@ + diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters index 7cfa90717..7dfd93e12 100644 --- a/Editor/Editor_SOURCE.vcxitems.filters +++ b/Editor/Editor_SOURCE.vcxitems.filters @@ -92,6 +92,7 @@ + @@ -148,6 +149,7 @@ + diff --git a/Editor/IconDefinitions.h b/Editor/IconDefinitions.h index 26360e962..47149ac91 100644 --- a/Editor/IconDefinitions.h +++ b/Editor/IconDefinitions.h @@ -59,6 +59,7 @@ #define ICON_CINEMA_MODE ICON_FA_CLAPPERBOARD #define ICON_DUMMY ICON_FA_PERSON_ARROW_UP_FROM_LINE #define ICON_NAVIGATION ICON_FA_ROUTE +#define ICON_CONTENT_BROWSER ICON_FA_ICONS #define ICON_LEFT_RIGHT ICON_FA_LEFT_RIGHT #define ICON_UP_DOWN ICON_FA_UP_DOWN diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index 5680fa990..d66b8d268 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -880,10 +880,10 @@ namespace wi::gui switch (font_description.params.h_align) { case wi::font::WIFALIGN_LEFT: - font_description.params.posX = translation.x + scale.x; + font_description.params.posX = translation.x + scale.x + shadow; break; case wi::font::WIFALIGN_RIGHT: - font_description.params.posX = translation.x; + font_description.params.posX = translation.x - shadow; break; case wi::font::WIFALIGN_CENTER: default: @@ -893,10 +893,10 @@ namespace wi::gui switch (font_description.params.v_align) { case wi::font::WIFALIGN_TOP: - font_description.params.posY = translation.y + scale.y; + font_description.params.posY = translation.y + scale.y + shadow; break; case wi::font::WIFALIGN_BOTTOM: - font_description.params.posY = translation.y; + font_description.params.posY = translation.y - shadow; break; case wi::font::WIFALIGN_CENTER: default: @@ -2959,7 +2959,14 @@ namespace wi::gui void Window::AddWidget(Widget* widget, AttachmentOptions options) { widget->SetEnabled(this->IsEnabled()); - widget->SetVisible(this->IsVisible()); + if (IsVisible() && !IsMinimized()) + { + widget->SetVisible(true); + } + else + { + widget->SetVisible(false); + } if (has_flag(options, AttachmentOptions::SCROLLABLE)) { widget->AttachTo(&scrollable_area); @@ -3249,13 +3256,24 @@ namespace wi::gui void Window::SetVisible(bool value) { Widget::SetVisible(value); - //SetMinimized(!value); - if (!IsMinimized()) + bool minimized = IsMinimized(); + for (auto& x : widgets) { - for (auto& x : widgets) + if ( + x == &resizeDragger_UpperLeft || + x == &resizeDragger_UpperRight || + x == &closeButton || + x == &collapseButton || + x == &moveDragger || + x == &label + ) { x->SetVisible(value); } + else + { + x->SetVisible(!minimized); + } } } void Window::SetEnabled(bool value) @@ -3296,18 +3314,23 @@ namespace wi::gui { minimized = value; - scrollable_area.SetVisible(!value); - if (resizeDragger_BottomLeft.parent != nullptr) + for (auto& x : widgets) { - resizeDragger_BottomLeft.SetVisible(!value); - } - if (resizeDragger_BottomRight.parent != nullptr) - { - resizeDragger_BottomRight.SetVisible(!value); + if ( + x == &resizeDragger_UpperLeft || + x == &resizeDragger_UpperRight || + x == &closeButton || + x == &collapseButton || + x == &moveDragger || + x == &label + ) + { + continue; + } + x->SetVisible(!value); } - scrollbar_horizontal.SetVisible(!value); - scrollbar_vertical.SetVisible(!value); + scrollable_area.SetVisible(!value); if (IsMinimized()) { diff --git a/WickedEngine/wiHelper.cpp b/WickedEngine/wiHelper.cpp index 0c0d75f47..015316c91 100644 --- a/WickedEngine/wiHelper.cpp +++ b/WickedEngine/wiHelper.cpp @@ -1218,6 +1218,54 @@ namespace wi::helper #endif // PLATFORM_UWP } + bool DirectoryExists(const std::string& fileName) + { +#ifndef PLATFORM_UWP + bool exists = std::filesystem::exists(ToNativeString(fileName)); + return exists; +#else + using namespace winrt::Windows::Storage; + using namespace winrt::Windows::Storage::Streams; + using namespace winrt::Windows::Foundation; + std::wstring wstr; + std::filesystem::path filepath = fileName; + filepath = std::filesystem::absolute(filepath); + StringConvert(filepath.string(), wstr); + bool success = false; + + auto async_helper = [&]() -> IAsyncAction { + try + { + auto file = co_await StorageFolder::GetFolderFromPathAsync(wstr); + success = true; + } + catch (winrt::hresult_error const& ex) + { + switch (ex.code()) + { + case E_ACCESSDENIED: + wi::backlog::post("Opening folder failed: " + fileName + " | Reason: Permission Denied!"); + break; + default: + break; + } + } + + }; + + if (winrt::impl::is_sta_thread()) + { + std::thread([&] { async_helper().get(); }).join(); // can't block coroutine from ui thread + } + else + { + async_helper().get(); + } + + return success; +#endif // PLATFORM_UWP + } + uint64_t FileTimestamp(const std::string& fileName) { auto tim = std::filesystem::last_write_time(ToNativeString(fileName)); @@ -1477,14 +1525,31 @@ namespace wi::helper for (const auto& entry : std::filesystem::directory_iterator(directory_path)) { + if (entry.is_directory()) + continue; std::string filename = entry.path().filename().generic_u8string(); - if (filter_extension.empty() || wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename)).compare(filter_extension) == 0) + if (filter_extension.empty() || wi::helper::toUpper(wi::helper::GetExtensionFromFileName(filename)).compare(wi::helper::toUpper(filter_extension)) == 0) { onSuccess(directory + filename); } } } + void GetFolderNamesInDirectory(const std::string& directory, std::function onSuccess) + { + std::filesystem::path directory_path = ToNativeString(directory); + if (!std::filesystem::exists(directory_path)) + return; + + for (const auto& entry : std::filesystem::directory_iterator(directory_path)) + { + if (!entry.is_directory()) + continue; + std::string filename = entry.path().filename().generic_u8string(); + onSuccess(directory + filename); + } + } + bool Bin2H(const uint8_t* data, size_t size, const std::string& dst_filename, const char* dataName) { std::string ss; @@ -1642,7 +1707,7 @@ namespace wi::helper #endif // PLATFORM_UWP #ifdef PLATFORM_WINDOWS_DESKTOP - std::string op = "start " + url; + std::string op = "start \"\" \"" + url + "\""; int status = system(op.c_str()); wi::backlog::post("wi::helper::OpenUrl(" + url + ") returned status: " + std::to_string(status)); return; diff --git a/WickedEngine/wiHelper.h b/WickedEngine/wiHelper.h index 685bb15e8..e918171ff 100644 --- a/WickedEngine/wiHelper.h +++ b/WickedEngine/wiHelper.h @@ -98,6 +98,8 @@ namespace wi::helper bool FileExists(const std::string& fileName); + bool DirectoryExists(const std::string& fileName); + uint64_t FileTimestamp(const std::string& fileName); std::string GetTempDirectoryPath(); @@ -118,6 +120,8 @@ namespace wi::helper void GetFileNamesInDirectory(const std::string& directory, std::function onSuccess, const std::string& filter_extension = ""); + void GetFolderNamesInDirectory(const std::string& directory, std::function onSuccess); + // Converts a file into a C++ header file that contains the file contents as byte array. // dataName : the byte array's name bool Bin2H(const uint8_t* data, size_t size, const std::string& dst_filename, const char* dataName);