diff --git a/.github/resource/JetBrains.png b/.github/resource/JetBrains.png deleted file mode 100644 index 6f6b3193f..000000000 Binary files a/.github/resource/JetBrains.png and /dev/null differ diff --git a/.github/resource/Logo.png b/.github/resource/Logo.png index 47990c8e7..abcd42766 100644 Binary files a/.github/resource/Logo.png and b/.github/resource/Logo.png differ diff --git a/Editor/Include/Editor/Panel/AssetsPanel.h b/Editor/Include/Editor/Panel/AssetsPanel.h index 70e34a068..12cf2e85f 100644 --- a/Editor/Include/Editor/Panel/AssetsPanel.h +++ b/Editor/Include/Editor/Panel/AssetsPanel.h @@ -29,6 +29,7 @@ namespace Editor { void RenderDirectoryTree(const std::filesystem::path& inDirectory, const char* inLabel); void RenderDirectoryContents(); void RenderEntry(const AssetFileEntry& inEntry); + void RenderEntryContextMenu(const AssetFileEntry& inEntry, const char* inPopupId); void RenderBackgroundMenu(); void RenderModals(); void HandleKeyboardShortcuts(); diff --git a/Editor/Include/Editor/Theme/Theme.h b/Editor/Include/Editor/Theme/Theme.h new file mode 100644 index 000000000..38a8b0829 --- /dev/null +++ b/Editor/Include/Editor/Theme/Theme.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +namespace Editor { + class Theme final { + public: + Theme(); + Theme(const Theme& inOther); + Theme(Theme&& inOther) noexcept; + ~Theme(); + + Theme& operator=(const Theme& inOther); + Theme& operator=(Theme&& inOther) noexcept; + + void Apply() const; + + Theme& SetTextColor(const ImVec4& inColor); + Theme& SetTextDisabledColor(const ImVec4& inColor); + Theme& SetWindowBackgroundColor(const ImVec4& inColor); + Theme& SetPopupBackgroundColor(const ImVec4& inColor); + Theme& SetSurfaceColor(const ImVec4& inColor); + Theme& SetSurfaceHoveredColor(const ImVec4& inColor); + Theme& SetSurfaceActiveColor(const ImVec4& inColor); + Theme& SetAccentColor(const ImVec4& inColor); + Theme& SetAccentHoveredColor(const ImVec4& inColor); + Theme& SetAccentActiveColor(const ImVec4& inColor); + Theme& SetOnAccentColor(const ImVec4& inColor); + Theme& SetWarningColor(const ImVec4& inColor); + Theme& SetErrorColor(const ImVec4& inColor); + + const ImVec4& GetTextColor() const; + const ImVec4& GetTextDisabledColor() const; + const ImVec4& GetWindowBackgroundColor() const; + const ImVec4& GetPopupBackgroundColor() const; + const ImVec4& GetSurfaceColor() const; + const ImVec4& GetSurfaceHoveredColor() const; + const ImVec4& GetSurfaceActiveColor() const; + const ImVec4& GetAccentColor() const; + const ImVec4& GetAccentHoveredColor() const; + const ImVec4& GetAccentActiveColor() const; + const ImVec4& GetOnAccentColor() const; + const ImVec4& GetWarningColor() const; + const ImVec4& GetErrorColor() const; + + private: + ImVec4 textColor; + ImVec4 textDisabledColor; + ImVec4 windowBackgroundColor; + ImVec4 popupBackgroundColor; + ImVec4 surfaceColor; + ImVec4 surfaceHoveredColor; + ImVec4 surfaceActiveColor; + ImVec4 accentColor; + ImVec4 accentHoveredColor; + ImVec4 accentActiveColor; + ImVec4 onAccentColor; + ImVec4 warningColor; + ImVec4 errorColor; + }; + + class ThemeSwitcher final { + public: + ThemeSwitcher() = delete; + ~ThemeSwitcher() = delete; + + static const Theme& GetCurrentTheme(); + static void SwitchTheme(Theme inTheme); + static void ApplyCurrentTheme(); + + private: + static Theme currentTheme; + }; +} diff --git a/Editor/Include/Editor/Widget/IconWidgets.h b/Editor/Include/Editor/Widget/IconWidgets.h index 843223a32..8193b4bdf 100644 --- a/Editor/Include/Editor/Widget/IconWidgets.h +++ b/Editor/Include/Editor/Widget/IconWidgets.h @@ -3,8 +3,13 @@ #include #include +struct ImVec2; + namespace Editor::Widgets { std::string Label(const char* inIcon, std::string_view inText); std::string Label(const char* inIcon, std::string_view inText, std::string_view inId); bool IconButton(const char* inId, const char* inIcon, const char* inTooltip); + bool PrimaryButton(const char* inLabel, const ImVec2& inSize); + void PushWarningTextColor(); + void PushErrorTextColor(); } diff --git a/Editor/Resource/Image/Logo.png b/Editor/Resource/Image/Logo.png new file mode 100644 index 000000000..f79d55739 Binary files /dev/null and b/Editor/Resource/Image/Logo.png differ diff --git a/Editor/Src/EditorApplication.cpp b/Editor/Src/EditorApplication.cpp index 19bda7bd9..1c75f1367 100644 --- a/Editor/Src/EditorApplication.cpp +++ b/Editor/Src/EditorApplication.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -71,19 +72,6 @@ namespace Editor::Internal { return inDesc.mode == EditorApplicationMode::projectHub ? projectHubWindowHeight : defaultWindowHeight; } - static void SetDarkStyle() - { - ImGui::StyleColorsDark(); - ImGuiStyle& style = ImGui::GetStyle(); - style.WindowRounding = 0.0f; - style.ChildRounding = 2.0f; - style.FrameRounding = 2.0f; - style.PopupRounding = 2.0f; - style.ScrollbarRounding = 2.0f; - style.GrabRounding = 2.0f; - style.TabRounding = 2.0f; - } - static void LoadIconFonts(ImFontAtlas& inFontAtlas, float inSizePixels) { static ImVector glyphRanges; @@ -244,7 +232,7 @@ namespace Editor { defaultFontConfig.SizePixels = Internal::defaultFontSize; io.FontDefault = io.Fonts->AddFontDefaultVector(&defaultFontConfig); Internal::LoadIconFonts(*io.Fonts, Internal::defaultFontSize); - Internal::SetDarkStyle(); + ThemeSwitcher::ApplyCurrentTheme(); } void EditorApplication::ShutdownImGui() diff --git a/Editor/Src/Frame/EditorFrame.cpp b/Editor/Src/Frame/EditorFrame.cpp index 9c0844448..9134b2149 100644 --- a/Editor/Src/Frame/EditorFrame.cpp +++ b/Editor/Src/Frame/EditorFrame.cpp @@ -316,16 +316,16 @@ namespace Editor { ImGui::Separator(); ImGui::BeginChild("LogEntries", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_HorizontalScrollbar); for (const auto& entry : entries) { - ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text); const char* levelIcon = Icons::Tabler::infoCircle; if (entry.level == Core::LogLevel::warning) { - color = ImVec4(1.0f, 0.78f, 0.25f, 1.0f); + Widgets::PushWarningTextColor(); levelIcon = Icons::Tabler::alertTriangle; } else if (entry.level == Core::LogLevel::error) { - color = ImVec4(1.0f, 0.35f, 0.32f, 1.0f); + Widgets::PushErrorTextColor(); levelIcon = Icons::Tabler::circleX; + } else { + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); } - ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::TextUnformatted(std::format("{} [{}][{}][{}] {}", levelIcon, entry.time, Internal::LevelString(entry.level), entry.tag, entry.content).c_str()); ImGui::PopStyleColor(); } diff --git a/Editor/Src/Frame/ProjectHubFrame.cpp b/Editor/Src/Frame/ProjectHubFrame.cpp index ff3d76890..afd4b845b 100644 --- a/Editor/Src/Frame/ProjectHubFrame.cpp +++ b/Editor/Src/Frame/ProjectHubFrame.cpp @@ -28,25 +28,6 @@ namespace Editor::ProjectHub::Internal { constexpr float actionButtonHeight = 58.0f; constexpr float recentProjectHeight = 68.0f; constexpr float contentSpacing = 12.0f; - const ImVec4 windowBackground(0.055f, 0.063f, 0.078f, 1.0f); - const ImVec4 cardBackground(0.09f, 0.102f, 0.125f, 1.0f); - const ImVec4 cardHovered(0.125f, 0.145f, 0.18f, 1.0f); - const ImVec4 accent(0.25f, 0.49f, 0.96f, 1.0f); - const ImVec4 accentHovered(0.31f, 0.55f, 1.0f, 1.0f); - const ImVec4 secondaryButton(0.12f, 0.137f, 0.17f, 1.0f); - const ImVec4 secondaryButtonHovered(0.16f, 0.18f, 0.22f, 1.0f); - - static bool RenderActionButton(const char* inLabel, const ImVec2& inSize, const ImVec4& inColor, const ImVec4& inHoveredColor) - { - ImGui::PushStyleColor(ImGuiCol_Button, inColor); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, inHoveredColor); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, inHoveredColor); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 9.0f); - const bool clicked = ImGui::Button(inLabel, inSize); - ImGui::PopStyleVar(); - ImGui::PopStyleColor(3); - return clicked; - } static bool RenderRecentProject(const RecentProjectInfo& inProject) { @@ -57,8 +38,8 @@ namespace Editor::ProjectHub::Internal { const bool hovered = ImGui::IsItemHovered(); ImDrawList* drawList = ImGui::GetWindowDrawList(); - drawList->AddRectFilled(cardMin, cardMax, ImGui::GetColorU32(hovered ? cardHovered : cardBackground), 8.0f); - drawList->AddText(ImVec2(cardMin.x + 16.0f, cardMin.y + 26.0f), ImGui::GetColorU32(accent), Icons::Tabler::folder); + drawList->AddRectFilled(cardMin, cardMax, ImGui::GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 8.0f); + drawList->AddText(ImVec2(cardMin.x + 16.0f, cardMin.y + 26.0f), ImGui::GetColorU32(ImGuiCol_CheckMark), Icons::Tabler::folder); drawList->PushClipRect(ImVec2(cardMin.x + 46.0f, cardMin.y), ImVec2(cardMax.x - 38.0f, cardMax.y), true); drawList->AddText(ImVec2(cardMin.x + 46.0f, cardMin.y + 13.0f), ImGui::GetColorU32(ImGuiCol_Text), inProject.name.c_str()); drawList->AddText(ImVec2(cardMin.x + 46.0f, cardMin.y + 38.0f), ImGui::GetColorU32(ImGuiCol_TextDisabled), inProject.path.c_str()); @@ -150,7 +131,6 @@ namespace Editor { ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ProjectHub::Internal::windowBackground); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(26.0f, 24.0f)); ImGui::Begin( @@ -161,7 +141,6 @@ namespace Editor { | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoBringToFrontOnFocus); ImGui::PopStyleVar(2); - ImGui::PopStyleColor(); RenderActionBar(inWindow, inRhiType); ImGui::Dummy(ImVec2(0.0f, 22.0f)); @@ -175,7 +154,7 @@ namespace Editor { const float spacing = ImGui::GetStyle().ItemSpacing.x; const float buttonWidth = (ImGui::GetContentRegionAvail().x - spacing) * 0.5f; const std::string openLabel = Widgets::Label(Icons::Tabler::folderOpen, "Open"); - if (ProjectHub::Internal::RenderActionButton(openLabel.c_str(), ImVec2(buttonWidth, ProjectHub::Internal::actionButtonHeight), ProjectHub::Internal::accent, ProjectHub::Internal::accentHovered)) { + if (Widgets::PrimaryButton(openLabel.c_str(), ImVec2(buttonWidth, ProjectHub::Internal::actionButtonHeight))) { if (const auto selectedDirectory = PlatformUtils::SelectDirectory("Open Explosion Project")) { OpenProject(inWindow, *selectedDirectory, inRhiType); } @@ -183,7 +162,7 @@ namespace Editor { ImGui::SameLine(); const std::string createLabel = Widgets::Label(Icons::Tabler::plus, "Create"); - if (ProjectHub::Internal::RenderActionButton(createLabel.c_str(), ImVec2(buttonWidth, ProjectHub::Internal::actionButtonHeight), ProjectHub::Internal::secondaryButton, ProjectHub::Internal::secondaryButtonHovered)) { + if (ImGui::Button(createLabel.c_str(), ImVec2(buttonWidth, ProjectHub::Internal::actionButtonHeight))) { statusMessage.clear(); ImGui::OpenPopup("##CreateProjectPopup"); } @@ -200,7 +179,7 @@ namespace Editor { if (!statusMessage.empty()) { const std::string statusLabel = Widgets::Label(Icons::Tabler::circleX, statusMessage); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.42f, 0.42f, 1.0f)); + Widgets::PushErrorTextColor(); ImGui::TextWrapped("%s", statusLabel.c_str()); ImGui::PopStyleColor(); ImGui::Dummy(ImVec2(0.0f, 7.0f)); @@ -218,10 +197,10 @@ namespace Editor { if (recentProjects.empty()) { const ImVec2 emptyMin = ImGui::GetCursorScreenPos(); const ImVec2 emptyMax(emptyMin.x + ImGui::GetContentRegionAvail().x, emptyMin.y + 118.0f); - ImGui::GetWindowDrawList()->AddRectFilled(emptyMin, emptyMax, ImGui::GetColorU32(ProjectHub::Internal::cardBackground), 8.0f); + ImGui::GetWindowDrawList()->AddRectFilled(emptyMin, emptyMax, ImGui::GetColorU32(ImGuiCol_FrameBg), 8.0f); const char* title = "No recent projects"; const char* description = "Open an existing project or create a new one."; - ImGui::GetWindowDrawList()->AddText(ImVec2(emptyMin.x + 16.0f, emptyMin.y + 51.0f), ImGui::GetColorU32(ProjectHub::Internal::accent), Icons::Tabler::folderOff); + ImGui::GetWindowDrawList()->AddText(ImVec2(emptyMin.x + 16.0f, emptyMin.y + 51.0f), ImGui::GetColorU32(ImGuiCol_CheckMark), Icons::Tabler::folderOff); ImGui::GetWindowDrawList()->AddText(ImVec2(emptyMin.x + 46.0f, emptyMin.y + 31.0f), ImGui::GetColorU32(ImGuiCol_Text), title); ImGui::GetWindowDrawList()->AddText(ImVec2(emptyMin.x + 46.0f, emptyMin.y + 59.0f), ImGui::GetColorU32(ImGuiCol_TextDisabled), description); ImGui::Dummy(ImVec2(0.0f, 118.0f)); @@ -238,7 +217,6 @@ namespace Editor { const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(456.0f, 0.0f), ImGuiCond_Appearing); - ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.075f, 0.086f, 0.105f, 1.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(24.0f, 22.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 11.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); @@ -289,7 +267,7 @@ namespace Editor { if (!statusMessage.empty()) { ImGui::Dummy(ImVec2(0.0f, 9.0f)); const std::string statusLabel = Widgets::Label(Icons::Tabler::circleX, statusMessage); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.42f, 0.42f, 1.0f)); + Widgets::PushErrorTextColor(); ImGui::TextWrapped("%s", statusLabel.c_str()); ImGui::PopStyleColor(); } @@ -304,7 +282,7 @@ namespace Editor { } ImGui::SameLine(); const std::string confirmLabel = Widgets::Label(Icons::Tabler::check, "Create##Confirm"); - if (ProjectHub::Internal::RenderActionButton(confirmLabel.c_str(), ImVec2(88.0f, 36.0f), ProjectHub::Internal::accent, ProjectHub::Internal::accentHovered)) { + if (Widgets::PrimaryButton(confirmLabel.c_str(), ImVec2(88.0f, 36.0f))) { const CreateProjectResult result = CreateProject(); if (result.success) { statusMessage.clear(); @@ -317,7 +295,6 @@ namespace Editor { ImGui::EndPopup(); } ImGui::PopStyleVar(4); - ImGui::PopStyleColor(); } CreateProjectResult ProjectHubFrame::CreateProject() diff --git a/Editor/Src/Panel/AssetsPanel.cpp b/Editor/Src/Panel/AssetsPanel.cpp index 1578442a9..f468aecad 100644 --- a/Editor/Src/Panel/AssetsPanel.cpp +++ b/Editor/Src/Panel/AssetsPanel.cpp @@ -2,7 +2,6 @@ // Created by johnk on 2026/7/18. // -#include #include #include @@ -89,11 +88,12 @@ namespace Editor { RenderToolbar(); RenderBreadcrumbs(); if (!statusMessage.empty()) { - const ImVec4 color = statusIsError - ? ImVec4(1.0f, 0.35f, 0.32f, 1.0f) - : ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled); const std::string statusLabel = Widgets::Label(statusIsError ? Icons::Tabler::circleX : Icons::Tabler::infoCircle, statusMessage); - ImGui::PushStyleColor(ImGuiCol_Text, color); + if (statusIsError) { + Widgets::PushErrorTextColor(); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + } ImGui::TextWrapped("%s", statusLabel.c_str()); ImGui::PopStyleColor(); } @@ -260,31 +260,7 @@ namespace Editor { if (inEntry.directory) { HandleDropTarget(inEntry.path); } - if (ImGui::BeginPopupContextItem("AssetEntryMenu")) { - selectedPath = inEntry.path; - const std::string openLabel = Widgets::Label(Icons::Tabler::externalLink, "Open"); - if (ImGui::MenuItem(openLabel.c_str(), nullptr, false, inEntry.directory)) { - NavigateTo(inEntry.path); - } - ImGui::Separator(); - const std::string cutLabel = Widgets::Label(Icons::Tabler::cut, "Cut"); - if (ImGui::MenuItem(cutLabel.c_str(), "Ctrl+X")) { - SetClipboard(inEntry.path, ClipboardMode::move); - } - const std::string copyLabel = Widgets::Label(Icons::Tabler::copy, "Copy"); - if (ImGui::MenuItem(copyLabel.c_str(), "Ctrl+C")) { - SetClipboard(inEntry.path, ClipboardMode::copy); - } - const std::string renameLabel = Widgets::Label(Icons::Tabler::edit, "Rename"); - if (ImGui::MenuItem(renameLabel.c_str(), "F2")) { - OpenRenamePopup(inEntry.path); - } - const std::string deleteLabel = Widgets::Label(Icons::Tabler::trash, "Delete"); - if (ImGui::MenuItem(deleteLabel.c_str(), "Delete")) { - OpenDeletePopup(inEntry.path); - } - ImGui::EndPopup(); - } + RenderEntryContextMenu(inEntry, "AssetEntryMenu"); ImGui::TableSetColumnIndex(1); ImGui::TextUnformatted(Internal::EntryType(inEntry).c_str()); @@ -297,6 +273,36 @@ namespace Editor { ImGui::PopID(); } + void AssetsPanel::RenderEntryContextMenu(const AssetFileEntry& inEntry, const char* inPopupId) + { + if (!ImGui::BeginPopupContextItem(inPopupId)) { + return; + } + selectedPath = inEntry.path; + const std::string openLabel = Widgets::Label(Icons::Tabler::externalLink, "Open"); + if (ImGui::MenuItem(openLabel.c_str(), nullptr, false, inEntry.directory)) { + NavigateTo(inEntry.path); + } + ImGui::Separator(); + const std::string cutLabel = Widgets::Label(Icons::Tabler::cut, "Cut"); + if (ImGui::MenuItem(cutLabel.c_str(), "Ctrl+X")) { + SetClipboard(inEntry.path, ClipboardMode::move); + } + const std::string copyLabel = Widgets::Label(Icons::Tabler::copy, "Copy"); + if (ImGui::MenuItem(copyLabel.c_str(), "Ctrl+C")) { + SetClipboard(inEntry.path, ClipboardMode::copy); + } + const std::string renameLabel = Widgets::Label(Icons::Tabler::edit, "Rename"); + if (ImGui::MenuItem(renameLabel.c_str(), "F2")) { + OpenRenamePopup(inEntry.path); + } + const std::string deleteLabel = Widgets::Label(Icons::Tabler::trash, "Delete"); + if (ImGui::MenuItem(deleteLabel.c_str(), "Delete")) { + OpenDeletePopup(inEntry.path); + } + ImGui::EndPopup(); + } + void AssetsPanel::RenderBackgroundMenu() { if (!ImGui::BeginPopupContextWindow("AssetBackgroundMenu", ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems)) { diff --git a/Editor/Src/Theme/Theme.cpp b/Editor/Src/Theme/Theme.cpp new file mode 100644 index 000000000..da39ae441 --- /dev/null +++ b/Editor/Src/Theme/Theme.cpp @@ -0,0 +1,308 @@ +#include + +#include + +#include + +namespace Editor::Internal { + static constexpr ImVec4 ThemeColorFromBytes(int inRed, int inGreen, int inBlue, int inAlpha = 255) + { + constexpr float scale = 1.0f / 255.0f; + return { + static_cast(inRed) * scale, + static_cast(inGreen) * scale, + static_cast(inBlue) * scale, + static_cast(inAlpha) * scale, + }; + } +} + +namespace Editor { + Theme::Theme() + : textColor(Internal::ThemeColorFromBytes(235, 226, 213)) + , textDisabledColor(Internal::ThemeColorFromBytes(145, 132, 116)) + , windowBackgroundColor(Internal::ThemeColorFromBytes(17, 15, 13)) + , popupBackgroundColor(Internal::ThemeColorFromBytes(29, 25, 21)) + , surfaceColor(Internal::ThemeColorFromBytes(31, 27, 23)) + , surfaceHoveredColor(Internal::ThemeColorFromBytes(45, 36, 29)) + , surfaceActiveColor(Internal::ThemeColorFromBytes(65, 47, 35)) + , accentColor(Internal::ThemeColorFromBytes(250, 197, 118)) + , accentHoveredColor(Internal::ThemeColorFromBytes(255, 215, 151)) + , accentActiveColor(Internal::ThemeColorFromBytes(226, 168, 104)) + , onAccentColor(Internal::ThemeColorFromBytes(32, 22, 14)) + , warningColor(Internal::ThemeColorFromBytes(239, 177, 88)) + , errorColor(Internal::ThemeColorFromBytes(238, 104, 86)) + { + } + + Theme::Theme(const Theme& inOther) = default; + + Theme::Theme(Theme&& inOther) noexcept = default; + + Theme::~Theme() = default; + + Theme& Theme::operator=(const Theme& inOther) = default; + + Theme& Theme::operator=(Theme&& inOther) noexcept = default; + + void Theme::Apply() const + { + ImGui::StyleColorsDark(); + + ImGuiStyle& style = ImGui::GetStyle(); + style.DisabledAlpha = 0.55f; + style.WindowPadding = ImVec2(10.0f, 10.0f); + style.WindowRounding = 6.0f; + style.WindowBorderSize = 1.0f; + style.ChildRounding = 5.0f; + style.ChildBorderSize = 1.0f; + style.PopupRounding = 8.0f; + style.PopupBorderSize = 1.0f; + style.FramePadding = ImVec2(8.0f, 4.0f); + style.FrameRounding = 5.0f; + style.FrameBorderSize = 0.0f; + style.ItemSpacing = ImVec2(8.0f, 6.0f); + style.ItemInnerSpacing = ImVec2(6.0f, 4.0f); + style.CellPadding = ImVec2(8.0f, 5.0f); + style.IndentSpacing = 20.0f; + style.ScrollbarSize = 13.0f; + style.ScrollbarRounding = 7.0f; + style.GrabMinSize = 10.0f; + style.GrabRounding = 4.0f; + style.TabRounding = 5.0f; + style.TabBorderSize = 0.0f; + style.TabBarBorderSize = 1.0f; + style.TabBarOverlineSize = 2.0f; + style.SeparatorSize = 1.0f; + style.DockingSeparatorSize = 2.0f; + style.ButtonTextAlign = ImVec2(0.5f, 0.5f); + + ImVec4* colors = style.Colors; + colors[ImGuiCol_Text] = textColor; + colors[ImGuiCol_TextDisabled] = textDisabledColor; + colors[ImGuiCol_WindowBg] = windowBackgroundColor; + colors[ImGuiCol_ChildBg] = Internal::ThemeColorFromBytes(20, 18, 16); + colors[ImGuiCol_PopupBg] = popupBackgroundColor; + colors[ImGuiCol_Border] = Internal::ThemeColorFromBytes(58, 48, 39); + colors[ImGuiCol_BorderShadow] = Internal::ThemeColorFromBytes(0, 0, 0, 0); + colors[ImGuiCol_FrameBg] = Internal::ThemeColorFromBytes(34, 29, 25); + colors[ImGuiCol_FrameBgHovered] = Internal::ThemeColorFromBytes(50, 40, 32); + colors[ImGuiCol_FrameBgActive] = surfaceActiveColor; + colors[ImGuiCol_TitleBg] = Internal::ThemeColorFromBytes(20, 17, 15); + colors[ImGuiCol_TitleBgActive] = Internal::ThemeColorFromBytes(28, 23, 19); + colors[ImGuiCol_TitleBgCollapsed] = Internal::ThemeColorFromBytes(20, 17, 15); + colors[ImGuiCol_MenuBarBg] = Internal::ThemeColorFromBytes(23, 20, 17); + colors[ImGuiCol_ScrollbarBg] = Internal::ThemeColorFromBytes(14, 13, 12); + colors[ImGuiCol_ScrollbarGrab] = Internal::ThemeColorFromBytes(70, 56, 45); + colors[ImGuiCol_ScrollbarGrabHovered] = Internal::ThemeColorFromBytes(102, 75, 54); + colors[ImGuiCol_ScrollbarGrabActive] = Internal::ThemeColorFromBytes(150, 99, 63); + colors[ImGuiCol_CheckMark] = accentColor; + colors[ImGuiCol_CheckboxSelectedBg] = Internal::ThemeColorFromBytes(112, 72, 43); + colors[ImGuiCol_SliderGrab] = Internal::ThemeColorFromBytes(195, 132, 74); + colors[ImGuiCol_SliderGrabActive] = accentColor; + colors[ImGuiCol_Button] = surfaceColor; + colors[ImGuiCol_ButtonHovered] = surfaceHoveredColor; + colors[ImGuiCol_ButtonActive] = surfaceActiveColor; + colors[ImGuiCol_Header] = Internal::ThemeColorFromBytes(43, 35, 29); + colors[ImGuiCol_HeaderHovered] = Internal::ThemeColorFromBytes(58, 43, 32); + colors[ImGuiCol_HeaderActive] = Internal::ThemeColorFromBytes(78, 51, 34); + colors[ImGuiCol_Separator] = Internal::ThemeColorFromBytes(55, 46, 38); + colors[ImGuiCol_SeparatorHovered] = Internal::ThemeColorFromBytes(178, 116, 67); + colors[ImGuiCol_SeparatorActive] = accentColor; + colors[ImGuiCol_ResizeGrip] = Internal::ThemeColorFromBytes(200, 129, 87, 55); + colors[ImGuiCol_ResizeGripHovered] = Internal::ThemeColorFromBytes(226, 168, 104, 170); + colors[ImGuiCol_ResizeGripActive] = accentColor; + colors[ImGuiCol_InputTextCursor] = accentColor; + colors[ImGuiCol_TabHovered] = Internal::ThemeColorFromBytes(55, 41, 31); + colors[ImGuiCol_Tab] = Internal::ThemeColorFromBytes(25, 22, 19); + colors[ImGuiCol_TabSelected] = Internal::ThemeColorFromBytes(40, 32, 26); + colors[ImGuiCol_TabSelectedOverline] = accentColor; + colors[ImGuiCol_TabDimmed] = Internal::ThemeColorFromBytes(20, 18, 16); + colors[ImGuiCol_TabDimmedSelected] = Internal::ThemeColorFromBytes(31, 27, 23); + colors[ImGuiCol_TabDimmedSelectedOverline] = Internal::ThemeColorFromBytes(200, 129, 87); + colors[ImGuiCol_DockingPreview] = Internal::ThemeColorFromBytes(250, 197, 118, 150); + colors[ImGuiCol_DockingEmptyBg] = Internal::ThemeColorFromBytes(12, 11, 10); + colors[ImGuiCol_PlotLines] = Internal::ThemeColorFromBytes(211, 181, 139); + colors[ImGuiCol_PlotLinesHovered] = accentColor; + colors[ImGuiCol_PlotHistogram] = Internal::ThemeColorFromBytes(200, 129, 87); + colors[ImGuiCol_PlotHistogramHovered] = Internal::ThemeColorFromBytes(239, 165, 105); + colors[ImGuiCol_TableHeaderBg] = Internal::ThemeColorFromBytes(37, 31, 26); + colors[ImGuiCol_TableBorderStrong] = Internal::ThemeColorFromBytes(66, 53, 43); + colors[ImGuiCol_TableBorderLight] = Internal::ThemeColorFromBytes(48, 40, 33); + colors[ImGuiCol_TableRowBg] = Internal::ThemeColorFromBytes(0, 0, 0, 0); + colors[ImGuiCol_TableRowBgAlt] = Internal::ThemeColorFromBytes(255, 224, 185, 8); + colors[ImGuiCol_TextLink] = Internal::ThemeColorFromBytes(239, 182, 111); + colors[ImGuiCol_TextSelectedBg] = Internal::ThemeColorFromBytes(250, 197, 118, 70); + colors[ImGuiCol_TreeLines] = Internal::ThemeColorFromBytes(72, 59, 48); + colors[ImGuiCol_DragDropTarget] = accentColor; + colors[ImGuiCol_DragDropTargetBg] = Internal::ThemeColorFromBytes(250, 197, 118, 25); + colors[ImGuiCol_UnsavedMarker] = Internal::ThemeColorFromBytes(200, 129, 87); + colors[ImGuiCol_NavCursor] = accentColor; + colors[ImGuiCol_NavWindowingHighlight] = Internal::ThemeColorFromBytes(250, 222, 180, 180); + colors[ImGuiCol_NavWindowingDimBg] = Internal::ThemeColorFromBytes(8, 7, 6, 145); + colors[ImGuiCol_ModalWindowDimBg] = Internal::ThemeColorFromBytes(8, 7, 6, 180); + } + + Theme& Theme::SetTextColor(const ImVec4& inColor) + { + textColor = inColor; + return *this; + } + + Theme& Theme::SetTextDisabledColor(const ImVec4& inColor) + { + textDisabledColor = inColor; + return *this; + } + + Theme& Theme::SetWindowBackgroundColor(const ImVec4& inColor) + { + windowBackgroundColor = inColor; + return *this; + } + + Theme& Theme::SetPopupBackgroundColor(const ImVec4& inColor) + { + popupBackgroundColor = inColor; + return *this; + } + + Theme& Theme::SetSurfaceColor(const ImVec4& inColor) + { + surfaceColor = inColor; + return *this; + } + + Theme& Theme::SetSurfaceHoveredColor(const ImVec4& inColor) + { + surfaceHoveredColor = inColor; + return *this; + } + + Theme& Theme::SetSurfaceActiveColor(const ImVec4& inColor) + { + surfaceActiveColor = inColor; + return *this; + } + + Theme& Theme::SetAccentColor(const ImVec4& inColor) + { + accentColor = inColor; + return *this; + } + + Theme& Theme::SetAccentHoveredColor(const ImVec4& inColor) + { + accentHoveredColor = inColor; + return *this; + } + + Theme& Theme::SetAccentActiveColor(const ImVec4& inColor) + { + accentActiveColor = inColor; + return *this; + } + + Theme& Theme::SetOnAccentColor(const ImVec4& inColor) + { + onAccentColor = inColor; + return *this; + } + + Theme& Theme::SetWarningColor(const ImVec4& inColor) + { + warningColor = inColor; + return *this; + } + + Theme& Theme::SetErrorColor(const ImVec4& inColor) + { + errorColor = inColor; + return *this; + } + + const ImVec4& Theme::GetTextColor() const + { + return textColor; + } + + const ImVec4& Theme::GetTextDisabledColor() const + { + return textDisabledColor; + } + + const ImVec4& Theme::GetWindowBackgroundColor() const + { + return windowBackgroundColor; + } + + const ImVec4& Theme::GetPopupBackgroundColor() const + { + return popupBackgroundColor; + } + + const ImVec4& Theme::GetSurfaceColor() const + { + return surfaceColor; + } + + const ImVec4& Theme::GetSurfaceHoveredColor() const + { + return surfaceHoveredColor; + } + + const ImVec4& Theme::GetSurfaceActiveColor() const + { + return surfaceActiveColor; + } + + const ImVec4& Theme::GetAccentColor() const + { + return accentColor; + } + + const ImVec4& Theme::GetAccentHoveredColor() const + { + return accentHoveredColor; + } + + const ImVec4& Theme::GetAccentActiveColor() const + { + return accentActiveColor; + } + + const ImVec4& Theme::GetOnAccentColor() const + { + return onAccentColor; + } + + const ImVec4& Theme::GetWarningColor() const + { + return warningColor; + } + + const ImVec4& Theme::GetErrorColor() const + { + return errorColor; + } + + Theme ThemeSwitcher::currentTheme; + + const Theme& ThemeSwitcher::GetCurrentTheme() + { + return currentTheme; + } + + void ThemeSwitcher::SwitchTheme(Theme inTheme) + { + currentTheme = std::move(inTheme); + if (ImGui::GetCurrentContext() != nullptr) { + ApplyCurrentTheme(); + } + } + + void ThemeSwitcher::ApplyCurrentTheme() + { + currentTheme.Apply(); + } +} diff --git a/Editor/Src/Widget/IconWidgets.cpp b/Editor/Src/Widget/IconWidgets.cpp index d4c3a36f5..ba1ad1d7c 100644 --- a/Editor/Src/Widget/IconWidgets.cpp +++ b/Editor/Src/Widget/IconWidgets.cpp @@ -1,9 +1,28 @@ #include #include +#include +#include #include +namespace Editor::Widgets::Internal { + static ImVec2 CalculateCenteredIconPosition(const char* inIcon, const ImVec2& inButtonMin, const ImVec2& inButtonMax) + { + unsigned int codepoint = 0; + const int consumedBytes = ImTextCharFromUtf8(&codepoint, inIcon, nullptr); + const ImFontGlyph* glyph = consumedBytes > 0 ? ImGui::GetFontBaked()->FindGlyphNoFallback(static_cast(codepoint)) : nullptr; + if (glyph == nullptr) { + const ImVec2 textSize = ImGui::CalcTextSize(inIcon); + return ImVec2(inButtonMin.x + (inButtonMax.x - inButtonMin.x - textSize.x) * 0.5f, inButtonMin.y + (inButtonMax.y - inButtonMin.y - textSize.y) * 0.5f); + } + + const float glyphWidth = glyph->X1 - glyph->X0; + const float glyphHeight = glyph->Y1 - glyph->Y0; + return ImVec2(inButtonMin.x + (inButtonMax.x - inButtonMin.x - glyphWidth) * 0.5f - glyph->X0, inButtonMin.y + (inButtonMax.y - inButtonMin.y - glyphHeight) * 0.5f - glyph->Y0); + } +} + namespace Editor::Widgets { std::string Label(const char* inIcon, std::string_view inText) { @@ -19,11 +38,35 @@ namespace Editor::Widgets { { ImGui::PushID(inId); const float size = ImGui::GetFrameHeight(); - const bool clicked = ImGui::Button(inIcon, ImVec2(size, size)); + const bool clicked = ImGui::Button("##IconButton", ImVec2(size, size)); + const ImVec2 iconPosition = Internal::CalculateCenteredIconPosition(inIcon, ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize(), iconPosition, ImGui::GetColorU32(ImGuiCol_Text), inIcon); if (inTooltip != nullptr && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("%s", inTooltip); } ImGui::PopID(); return clicked; } + + bool PrimaryButton(const char* inLabel, const ImVec2& inSize) + { + const Theme& theme = ThemeSwitcher::GetCurrentTheme(); + ImGui::PushStyleColor(ImGuiCol_Text, theme.GetOnAccentColor()); + ImGui::PushStyleColor(ImGuiCol_Button, theme.GetAccentColor()); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, theme.GetAccentHoveredColor()); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, theme.GetAccentActiveColor()); + const bool clicked = ImGui::Button(inLabel, inSize); + ImGui::PopStyleColor(4); + return clicked; + } + + void PushWarningTextColor() + { + ImGui::PushStyleColor(ImGuiCol_Text, ThemeSwitcher::GetCurrentTheme().GetWarningColor()); + } + + void PushErrorTextColor() + { + ImGui::PushStyleColor(ImGuiCol_Text, ThemeSwitcher::GetCurrentTheme().GetErrorColor()); + } } diff --git a/README.md b/README.md index 31e7fc1fd..64ecd33f6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,5 @@ -
-
Explosion Logo
-
- - Codacy Grade - - - License - - Commit Activity -
-
+Explosion Logo # Introduction