Quellcode durchsuchen

add asset browser example

Jeffery Myers vor 1 Jahr
Ursprung
Commit
3c6986358c

+ 269 - 0
examples/asset_browser/asset_browser.cpp

@@ -0,0 +1,269 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#define  _CRT_NONSTDC_NO_WARNINGS
+
+#include "asset_browser.h"
+#include "imgui_utils.h"
+#include "imgui.h"
+#include "imgui_internal.h"
+#include "raylib.h"
+#include "extras/IconsFontAwesome6.h"
+
+
+AssetBrowserPanel::AssetBrowserPanel()
+{
+    AssetRoot = GetWorkingDirectory();
+    RebuildFolderTree();
+    SetCurrentFolder(&FolderRoot);
+
+    CurrentView = &ListView;
+}
+
+void AssetBrowserPanel::Show()
+{
+    ShowHeader();
+
+    ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders;
+  
+    if (ImGui::BeginTable("AssetBrowserTab", 2, flags, ImGui::GetContentRegionAvail()))
+    {
+        ImGui::TableSetupColumn("FolderView", ImGuiTableColumnFlags_None, 0.25f);
+        ImGui::TableSetupColumn("AssetView", ImGuiTableColumnFlags_None, 0.75f);
+        ImGui::TableNextRow();
+        ImGui::TableNextColumn();
+
+        if (ImGui::BeginChild("FolderList", ImGui::GetContentRegionAvail(),ImGuiChildFlags_None, ImGuiWindowFlags_None))
+        {
+            ShowFolderTree();
+            ImGui::EndChild();
+        }
+
+        ImGui::TableNextColumn();
+        ShowFilePane();
+        ImGui::EndTable();
+    }
+}
+
+void AssetBrowserPanel::RebuildFolderTree()
+{
+    FolderRoot.Children.clear();
+
+    FolderRoot.FullPath = AssetRoot;
+    FolderRoot.Name = GetFileNameWithoutExt(AssetRoot.c_str());
+    FolderRoot.Parent = nullptr;
+    FolderRoot.Icon = ICON_FA_SERVER;
+    FolderRoot.ForceOpenNextFrame = true;
+    FolderRoot.PopulateChildren();
+}
+
+void AssetBrowserPanel::SetCurrentFolder(FolderInfo* folder)
+{
+    if (CurrentFolderContents.Folder == folder)
+        return;
+
+    CurrentFolderContents.Folder = folder;
+    CurrentFolderContents.Files.clear();
+
+    if (folder == nullptr)
+        return;
+
+    FolderInfo* openFolder = folder;
+    while (openFolder != nullptr)
+    {
+        openFolder->ForceOpenNextFrame = true;
+        openFolder = openFolder->Parent;
+    }
+
+    auto files = LoadDirectoryFiles(CurrentFolderContents.Folder->FullPath.c_str());
+
+    for (unsigned int i = 0; i < files.count; i++)
+    {
+        if (DirectoryExists(files.paths[i]))
+            continue;
+
+        const char* name = GetFileName(files.paths[i]);
+        if (!name || *name == '.')
+            continue;
+
+        FileInfo& file = CurrentFolderContents.Files.emplace_back();
+        file.FullPath = files.paths[i];
+        file.Name = name;
+        file.Icon = GetFileIcon(name);
+    }
+
+    UnloadDirectoryFiles(files);
+}
+
+void AssetBrowserPanel::FolderInfo::PopulateChildren()
+{
+    constexpr Color folderColor = { 255,255,145,255 };
+
+    auto folders = LoadDirectoryFiles(FullPath.c_str());
+
+    for (unsigned int i = 0; i < folders.count; i++)
+    {
+        if (DirectoryExists(folders.paths[i]))
+        {
+            const char* name = GetFileNameWithoutExt(folders.paths[i]);
+            if (!name || *name == '.')
+                continue;
+  
+            FolderInfo& child = Children.emplace_back();
+            child.FullPath = folders.paths[i];
+            child.Name = name;
+            child.Parent = this;
+            child.Tint = folderColor;
+            child.Icon = ICON_FA_FOLDER;
+            child.PopulateChildren();
+        }
+    }
+    UnloadDirectoryFiles(folders);
+}
+
+bool AssetBrowserPanel::ShowFolderTreeNode(FolderInfo& info)
+{
+    ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
+    if (info.Children.empty())
+        flags |= ImGuiTreeNodeFlags_Leaf;
+
+    if (CurrentFolderContents.Folder == &info)
+        flags |= ImGuiTreeNodeFlags_Selected;
+
+    if (info.ForceOpenNextFrame)
+    {
+        ImGui::SetNextItemOpen(true);
+    }
+
+    bool open = ImGui::TreeNodeEx(info.Name.c_str(), flags, "%s %s", info.Icon.c_str(), info.Name.c_str());
+
+    if (info.ForceOpenNextFrame && CurrentFolderContents.Folder == &info)
+        ImGui::ScrollToItem(ImGuiScrollFlags_KeepVisibleCenterY);
+
+    info.ForceOpenNextFrame = false;
+    if (ImGui::IsItemClicked())
+        SetCurrentFolder(&info);
+    
+    if (open)
+    {
+        for (auto& node : info.Children)
+            ShowFolderTreeNode(node);
+
+        ImGui::TreePop();
+    }
+
+    return CurrentFolderContents.Folder == &info;
+}  
+
+void AssetBrowserPanel::ShowHeader()
+{
+    ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1);
+    if (ImGui::BeginChild("Header", ImVec2{ ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight() }))
+    {
+        ImGui::Text("%s Root", ICON_FA_FOLDER_OPEN);
+        ImGui::SameLine();
+        ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.33f);
+        ImGui::InputText("###Path", (char*)(CurrentFolderContents.Folder->FullPath.c_str() + AssetRoot.size()), CurrentFolderContents.Folder->FullPath.size(), ImGuiInputTextFlags_ReadOnly);
+
+        ImGui::EndChild();
+    }
+    ImGui::PopStyleVar();
+}
+
+void AssetBrowserPanel::ShowFolderTree()
+{
+    ShowFolderTreeNode(FolderRoot);
+}
+
+void AssetBrowserPanel::ShowFilePane()
+{
+    if (ImGui::BeginChild("FileList", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_None))
+    {
+        if (CurrentView)
+        {
+            auto *item = CurrentView->Show(CurrentFolderContents);
+            if (item)
+            {
+                AssetItemInfo* assetItem = static_cast<AssetItemInfo*>(item);
+                if (!assetItem->IsFile())
+                    SetCurrentFolder(static_cast<FolderInfo*>(item));
+            }
+        }
+        ImGui::EndChild();
+    }
+}
+
+ViewableItem* AssetBrowserPanel::AssetContainer::Reset()
+{
+    FileItr = Files.begin();
+    FolderItr = Folder->Children.begin();
+    if (FileItr != Files.end())
+        return &(*FileItr);
+    if (FolderItr != Folder->Children.end())
+        return &(*FolderItr);
+
+    return nullptr;
+}
+
+size_t AssetBrowserPanel::AssetContainer::Count()
+{
+    return Files.size() + Folder->Children.size();
+}
+
+ViewableItem* AssetBrowserPanel::AssetContainer::Next()
+{
+    if (FileItr != Files.end())
+    {
+        FileItr++;
+        if (FileItr != Files.end())
+            return &(*FileItr);
+        else
+        {
+            if (FolderItr != Folder->Children.end())
+                return &(*FolderItr);
+            else
+                return nullptr;
+        }
+    }
+
+    if (FolderItr != Folder->Children.end())
+    {
+        FolderItr++;
+        if (FolderItr != Folder->Children.end())
+            return &(*FolderItr);
+    }
+
+    return nullptr;
+}
+
+const char* AssetBrowserPanel::GetFileIcon(const char* filename)
+{
+    const char* ext = GetFileExtension(filename);
+
+    if (ext != nullptr)
+    {
+        if (stricmp(ext, ".png") == 0)
+            return ICON_FA_FILE_IMAGE;
+
+        if (stricmp(ext, ".wav") == 0 || stricmp(ext, ".mp3") == 0 || stricmp(ext, ".oog") == 0)
+            return ICON_FA_FILE_AUDIO;
+
+        if (stricmp(ext, ".ttf") == 0 || stricmp(ext, ".otf") == 0 || stricmp(ext, ".fnt") == 0)
+            return ICON_FA_FONT;
+
+        if (stricmp(ext, ".txt") == 0 || stricmp(ext, ".md") == 0)
+            return ICON_FA_FILE_LINES;
+
+        if (stricmp(ext, ".lua") == 0 || stricmp(ext, ".c") == 0 || stricmp(ext, ".h") == 0 || stricmp(ext, ".cpp") == 0)
+            return ICON_FA_FILE_CODE;
+    }
+    return ICON_FA_FILE;
+}

+ 95 - 0
examples/asset_browser/asset_browser.h

@@ -0,0 +1,95 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#pragma once
+
+#include "item_view.h"
+#include "raylib.h"
+
+#include <string>
+#include <vector>
+#include <list>
+
+class AssetBrowserPanel
+{
+public:
+    AssetBrowserPanel();
+
+    void Show();
+
+private:
+    std::string AssetRoot;
+
+    class AssetItemInfo : public ViewableItem
+    {
+    protected:
+        bool File = false;
+
+    public:
+        AssetItemInfo(bool file) : File(file) {}
+        bool IsFile() const { return File; }
+    };
+
+    class FileInfo : public AssetItemInfo
+    {
+    public:
+        FileInfo() : AssetItemInfo(true) {}
+
+        std::string FullPath;
+    };
+
+    class FolderInfo : public AssetItemInfo
+    {
+    public:
+        FolderInfo() : AssetItemInfo(false) {}
+
+        std::string FullPath;
+        FolderInfo* Parent = nullptr;
+        std::list<FolderInfo> Children;
+
+        bool ForceOpenNextFrame = false;
+
+        void PopulateChildren();
+    };
+
+    FolderInfo FolderRoot;
+
+    class AssetContainer : public ViewableItemContainer
+    {
+    public:
+        ViewableItem* Reset() override;
+        size_t Count() override;
+        ViewableItem* Next() override;
+
+        FolderInfo* Folder = nullptr;
+        std::vector<FileInfo> Files;
+
+        std::vector<FileInfo>::iterator FileItr;
+        std::list<FolderInfo>::iterator FolderItr;
+    };
+
+    AssetContainer CurrentFolderContents;
+
+    ListItemView ListView;
+
+    ItemView* CurrentView = nullptr;
+
+    void RebuildFolderTree();
+
+    void SetCurrentFolder(FolderInfo* folder);
+
+    bool ShowFolderTreeNode(FolderInfo& folder);
+    void ShowFolderTree();
+    void ShowFilePane();
+    void ShowHeader();
+
+    const char* GetFileIcon(const char* filename);
+};

+ 94 - 0
examples/asset_browser/imgui_utils.cpp

@@ -0,0 +1,94 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#include "imgui_utils.h"
+
+#include "imgui.h"
+#include "imgui_internal.h"
+
+namespace ImGuiUtils
+{
+
+    bool IsSpace(char aCharacter)
+    {
+        // all space characters are values 32 or less (space is 32)
+        // so we can convert them to a bitmask and use a single condition
+        const int mask = (1 << (' ' - 1)) | (1 << ('\f' - 1)) | (1 << ('\n' - 1)) | (1 << ('\r' - 1)) | (1 << ('\t' - 1)) | (1 << ('\v' - 1));
+        return (mask & (1 << ((aCharacter && aCharacter <= 32) * (aCharacter - 1)))) != 0;
+    }
+
+    //-------------------------------------------------------------------------------------------------
+    // Todo: Add support for soft-hyphens when using word boundaries?
+    //-------------------------------------------------------------------------------------------------
+    void TextWithEllipsis(const char* string, float aMaxWidth, bool useWordBoundaries, float aSpacing)
+    {
+        char const* partStart = string;
+        char const* partEnd = string;
+
+        ImWchar elipsisChar = ImGui::GetFont()->EllipsisChar;
+        char elipsisText[8];
+        ImTextStrToUtf8(elipsisText, sizeof(elipsisText), &elipsisChar, (&elipsisChar) + 1);
+
+        if (aSpacing < 0.0f) aSpacing = ImGui::GetStyle().ItemSpacing.x;
+
+        float const ellipsisWidth = ImGui::CalcTextSize(elipsisText).x + aSpacing;
+        float width = 0;
+        bool addElipsis = false;
+
+        while (*partStart != 0 )
+        {
+            // Add space to next segment
+            while (IsSpace(*partEnd))
+                partEnd++;
+
+            if (useWordBoundaries)
+            {
+                // get next 'word' by looking for space after non-space
+                while (*partEnd != 0 && !IsSpace(*partEnd))
+                    ++partEnd;
+            }
+            else
+            {
+                if (*partEnd != 0)
+                    ++partEnd;
+            }
+
+            ImVec2 const wordSize = ImGui::CalcTextSize(partStart, partEnd);
+
+            // Clearly we have space for this word so just add it
+            if (wordSize.x + width + ellipsisWidth < aMaxWidth)
+            {
+                width += wordSize.x;
+                partStart = partEnd;
+            }
+            // If we're just at the end of the word and we just fit then we can commit here
+            else if (*partEnd == 0 && wordSize.x + width < aMaxWidth)
+            {
+                width += wordSize.x;
+                partStart = partEnd;
+            }
+            // we're done so add elipsis where the current segment starts
+            else
+            {
+                addElipsis = true;
+                break;
+            }
+        }
+
+        ImGui::TextUnformatted(string, partStart);
+
+        if (addElipsis)
+        {
+            ImGui::SameLine(0.0f, aSpacing);
+            ImGui::TextUnformatted(elipsisText);
+        }
+    }
+}

+ 19 - 0
examples/asset_browser/imgui_utils.h

@@ -0,0 +1,19 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#pragma once
+
+#include <string>
+
+namespace ImGuiUtils
+{
+    void TextWithEllipsis(const char* string, float maxWidth, bool useWordBoundaries = false, float aSpacing = 0);
+}

+ 47 - 0
examples/asset_browser/item_view.h

@@ -0,0 +1,47 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#pragma once
+
+#include <string>
+#include "raylib.h"
+
+class ViewableItem
+{
+public:
+    virtual ~ViewableItem() = default;
+
+    std::string Name;
+    std::string Icon;
+    Color Tint = BLANK;
+};
+
+class ViewableItemContainer
+{
+public:
+    virtual ~ViewableItemContainer() = default;
+    virtual ViewableItem* Reset() = 0;
+    virtual size_t Count() = 0;
+    virtual ViewableItem* Next() = 0;
+};
+
+class ItemView
+{
+public:
+    virtual ~ItemView() = default;
+    virtual ViewableItem* Show(ViewableItemContainer& container) = 0;
+};
+
+class ListItemView : public ItemView
+{
+public:
+    ViewableItem* Show(ViewableItemContainer& container) override;
+};

+ 53 - 0
examples/asset_browser/item_views.cpp

@@ -0,0 +1,53 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+#include "item_view.h"
+
+#include "imgui.h"
+#include "imgui_utils.h"
+#include "rlImGuiColors.h"
+#include "raylib.h"
+
+extern ImFont* IconFont;
+
+ViewableItem* ListItemView::Show(ViewableItemContainer& container)
+{
+    ViewableItem* item = container.Reset();
+
+    ViewableItem* selected = nullptr;
+    while (item)
+    {
+        float x = ImGui::GetCursorPosX();
+
+        const char* name = TextFormat("###%s", item->Name.c_str());
+        if (item->Tint.a > 0)
+            ImGui::TextColored(rlImGuiColors::Convert(item->Tint), " %s", item->Icon.c_str());
+        else
+            ImGui::Text(" %s", item->Icon.c_str());
+
+        ImGui::SameLine(0, 0);
+        ImGui::Text(" %s", item->Name.c_str());
+        ImGui::SameLine(0, 0);
+
+        ImGui::SetCursorPosX(x);
+        //ImGui::SetItemAllowOverlap();
+
+        ImGui::Selectable(name);
+        if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
+        {
+            selected = item;
+        }
+
+        item = container.Next();
+    }
+
+    return selected;
+}

+ 96 - 0
examples/asset_browser/main.cpp

@@ -0,0 +1,96 @@
+/*******************************************************************************************
+*
+*   raylib-extras [ImGui] example - asset browser
+*
+*	This is a more complex ImGui Integration
+*	It shows how to build windows on top of 2d and 3d views using a render texture
+*
+*   Copyright (c) 2024 Jeffery Myers
+*
+********************************************************************************************/
+
+
+#include "raylib.h"
+#include "raymath.h"
+
+#include "imgui.h"
+#include "rlImGui.h"
+#include "rlImGuiColors.h"
+#include "extras/FA6FreeSolidFontData.h"
+
+#include "asset_browser.h"
+
+#include <limits>
+
+
+ImFont* IconFont = nullptr;
+
+int main(int argc, char* argv[])
+{
+	// Initialization
+	//--------------------------------------------------------------------------------------
+	int screenWidth = 1900;
+	int screenHeight = 900;
+
+	SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
+	InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - Asset browser");
+	SetTargetFPS(144);
+
+    rlImGuiBeginInitImGui();
+    ImGui::StyleColorsDark();
+
+
+    static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
+    ImFontConfig icons_config;
+    icons_config.MergeMode = true;
+    icons_config.PixelSnapH = true;
+    icons_config.FontDataOwnedByAtlas = false;
+
+    icons_config.GlyphMaxAdvanceX = std::numeric_limits<float>::max();
+    icons_config.RasterizerMultiply = 1.0f;
+    icons_config.OversampleH = 2;
+    icons_config.OversampleV = 1;
+
+    icons_config.GlyphRanges = icons_ranges;
+
+    ImGuiIO& io = ImGui::GetIO();
+    io.Fonts->AddFontFromMemoryCompressedTTF((void*)fa_solid_900_compressed_data, fa_solid_900_compressed_size, 12, &icons_config, icons_ranges);
+
+    icons_config.MergeMode = false;
+    IconFont = io.Fonts->AddFontFromMemoryCompressedTTF((void*)fa_solid_900_compressed_data, fa_solid_900_compressed_size, 72, &icons_config, icons_ranges);
+
+    rlImGuiEndInitImGui();
+
+	AssetBrowserPanel assetBrowser;
+	
+	// Main game loop
+	while (!WindowShouldClose())    // Detect window close button or ESC key
+	{
+
+		BeginDrawing();
+		ClearBackground(DARKGRAY);
+
+		rlImGuiBegin();
+
+		ImGui::SetNextWindowPos(ImVec2(0, 0));
+		ImGui::SetNextWindowSize(ImVec2(GetScreenWidth(), GetScreenHeight()));
+		if (ImGui::Begin("Frame", 0, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings))
+		{
+			assetBrowser.Show();
+		}
+		ImGui::End();
+
+		rlImGuiEnd();
+
+		EndDrawing();
+		//----------------------------------------------------------------------------------
+	}
+	rlImGuiShutdown();
+
+	// De-Initialization
+	//--------------------------------------------------------------------------------------   
+	CloseWindow();        // Close window and OpenGL context
+	//--------------------------------------------------------------------------------------
+
+	return 0;
+}

+ 34 - 4
premake5.lua

@@ -90,7 +90,7 @@ workspace "rlImGui"
 	
 
 	cdialect "C99"
-	cppdialect "C++11"
+	cppdialect "C++17"
 	check_raylib()
 	check_imgui()
 
@@ -101,7 +101,8 @@ project "rlImGui"
 	location "build"
 	targetdir "bin/%{cfg.buildcfg}"
 	language "C++"
-	
+	cdialect "C99"
+	cppdialect "C++17"
 	include_raylib()
 	includedirs { "rlImGui", "imgui", "imgui-master"}
 	vpaths 
@@ -118,6 +119,8 @@ group "Examples"
 project "simple"
 	kind "ConsoleApp"
 	language "C++"
+	cdialect "C99"
+	cppdialect "C++17"
 	location "build"
 	targetdir "bin/%{cfg.buildcfg}"
 	
@@ -138,6 +141,8 @@ project "simple"
 project "editor"
 	kind "ConsoleApp"
 	language "C++"
+	cdialect "C99"
+	cppdialect "C++17"
 	location "build"
 	targetdir "bin/%{cfg.buildcfg}"
 
@@ -159,6 +164,8 @@ project "editor"
 project "imgui_style_example"
 	kind "ConsoleApp"
 	language "C++"
+	cdialect "C99"
+	cppdialect "C++17"
 	location "build"
 	targetdir "bin/%{cfg.buildcfg}"
 	
@@ -178,6 +185,8 @@ project "imgui_style_example"
 project "docking_example"
 	kind "ConsoleApp"
 	language "C++"
+	cdialect "C99"
+	cppdialect "C++17"
 	location "build"
 	targetdir "bin/%{cfg.buildcfg}"
 	
@@ -192,5 +201,26 @@ project "docking_example"
 	includedirs {"./", "imgui", "imgui-master" }
 		
     filter "action:vs*"
-		debugdir "$(SolutionDir)"	
-	
+		debugdir "$(SolutionDir)"
+
+project "asset_browser"
+	kind "ConsoleApp"
+	language "C++"
+	cdialect "C99"
+	cppdialect "C++17"
+	location "build"
+	targetdir "bin/%{cfg.buildcfg}"
+	
+	vpaths 
+	{
+		["Header Files"] = { "examples/asset_browser/**.h"},
+		["Source Files"] = {"examples/asset_browser/**.cpp", "examples/asset_browser/**.c"},
+	}
+	files {"examples/asset_browser/**.cpp", "examples/asset_browser/**.h"}
+	link_raylib()
+	links {"rlImGui"}
+	includedirs {"./", "examples/asset_browser/", "imgui", "imgui-master" }
+		
+    filter "action:vs*"
+		debugdir "$(SolutionDir)"
+		

+ 2 - 0
raylib_premake5.lua

@@ -107,6 +107,8 @@ project "raylib"
 
     location "build"
     language "C"
+	cdialect "C99"
+	cppdialect "C++17"
     targetdir "bin/%{cfg.buildcfg}"
 
     filter "action:vs*"