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);