Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,38 @@ endif()

include(${CMAKE_CURRENT_SOURCE_DIR}/resources/cmake/bootstrap_deps.cmake)

# ── Embed locale JSON files as C++ raw string literals ────────────────────────
# Each src/locales/<lang>.json is read at configure time and appended into
# build/generated/locale_data.h as inline const char* kLocaleData_<lang>.
# Re-run cmake automatically when any JSON changes (CMAKE_CONFIGURE_DEPENDS).

set(_LOCALE_LANGS
english russian ukrainian german french spanish italian polish turkish
swedish dutch brazilian hungarian indonesian vietnamese
schinese tchinese japanese koreana latam
bulgarian danish
)
set(_LOCALE_HEADER "${CMAKE_BINARY_DIR}/generated/locale_data.h")

file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")

# Use file(WRITE/APPEND) directly — avoids CMake list/semicolon expansion bugs
# that occur when building large strings with string(APPEND) + file(WRITE).
file(WRITE "${_LOCALE_HEADER}" "// Auto-generated by CMake — do not edit. Re-run cmake to refresh.\n#pragma once\n\n")

foreach(_LANG ${_LOCALE_LANGS})
set(_JSON_PATH "${CMAKE_SOURCE_DIR}/src/locales/${_LANG}.json")
# Track for automatic re-configure when any JSON file changes
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_JSON_PATH}")
file(READ "${_JSON_PATH}" _JSON_CONTENT)
# Write open, content, close as separate appends so no CMake string
# processing can swallow the semicolon in )__LD__";
file(APPEND "${_LOCALE_HEADER}" "inline const char* kLocaleData_${_LANG} = R\"__LD__(\n")
file(APPEND "${_LOCALE_HEADER}" "${_JSON_CONTENT}")
file(APPEND "${_LOCALE_HEADER}" "\n)__LD__\";\n\n")
endforeach()
# ──────────────────────────────────────────────────────────────────────────────

add_library(imgui_lib STATIC
${imgui_SOURCE_DIR}/imgui.cpp
${imgui_SOURCE_DIR}/imgui_draw.cpp
Expand All @@ -62,6 +94,7 @@ set(SOURCES
src/window/wndproc.cc
src/window/renderer.cc
src/util/updater.cc
src/util/locale.cc
src/routes/router.cc
src/routes/home.cc
src/routes/install_prompt.cc
Expand Down Expand Up @@ -97,6 +130,8 @@ include_directories(SYSTEM ${stb_SOURCE_DIR})
include_directories(SYSTEM ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})

target_link_libraries(${PROJECT_NAME} PRIVATE imgui_lib)

target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/generated")
if(WIN32)
target_link_options(${PROJECT_NAME} PRIVATE /FORCE:MULTIPLE)
elseif(UNIX)
Expand Down Expand Up @@ -148,3 +183,4 @@ elseif(WIN32)
bcrypt
)
endif()

11 changes: 7 additions & 4 deletions src/components/bottombar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#include <dpi.h>
#include <texture.hh>
#include <animate.h>
#include <i18n.h>
#ifdef _WIN32
#endif
#include <format>
#include <util.h>

Expand Down Expand Up @@ -65,10 +68,10 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
const float cursorPosSave = GetCursorPosX();

SetCursorPosY(GetCursorPosY() - ScaleX(12));
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "Steam Homebrew & Millennium are not affiliated with");
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "%s", Locale::Get("installerDisclaimer1"));

SetCursorPos({ cursorPosSave, GetCursorPosY() - ScaleY(20) });
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "Steam®, Valve, or any of their partners.");
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "%s", Locale::Get("installerDisclaimer2"));

SameLine(0);
SetCursorPosY(GetCursorPosY() - ScaleY(25));
Expand All @@ -94,7 +97,7 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
PushStyleVar(ImGuiStyleVar_WindowRounding, 6);
PushStyleVar(ImGuiStyleVar_Alpha, discordIconHoverTransparency);
PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.098f, 0.102f, 0.11f, 1.0f));
SetTooltip("Join Discord Server");
SetTooltip("%s", Locale::Get("tooltipDiscord"));

if (IsItemClicked()) {
OpenUrl(discordInviteLink);
Expand Down Expand Up @@ -125,7 +128,7 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
PushStyleVar(ImGuiStyleVar_WindowRounding, 6);
PushStyleVar(ImGuiStyleVar_Alpha, githubIconHoverTransparency);
PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.098f, 0.102f, 0.11f, 1.0f));
SetTooltip("View Source Code");
SetTooltip("%s", Locale::Get("tooltipGithub"));

if (IsItemClicked()) {
OpenUrl(githubRepositoryUrl);
Expand Down
115 changes: 112 additions & 3 deletions src/components/titlebar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@
* SOFTWARE.
*/

#include <exception>
#include <imgui.h>
#include <texture.hh>
#include <dpi.h>
#include <components.h>
#include <animate.h>
#include <i18n.h>
#include <math.h>
#include <worker.h>
#include <renderer.h>
#include <cstdlib>
#include <format>
#ifndef _WIN32
#include <unistd.h>
#endif

using namespace ImGui;

Expand All @@ -46,8 +52,7 @@ using namespace ImGui;
*/
bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)
{
ImGuiIO& io = GetIO();
const std::string strTitleText = std::format("Steam Homebrew", io.Framerate);
const std::string strTitleText = Locale::Get("titlebarTitle");

ImGuiViewport* viewport = GetMainViewport();
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ScaleX(15), ScaleY(15)));
Expand Down Expand Up @@ -90,13 +95,109 @@ bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)
Text("%s", strTitleText.c_str());
SameLine();

ImVec2 closeButtonDimensions = { ceil(ScaleX(70)), ceil(ScaleY(43)) };

static bool isCloseButtonHovered = false;

if (isCloseButtonHovered) {
PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.769f, 0.169f, 0.11f, 1.0f));
}

ImVec2 closeButtonDimensions = { ceil(ScaleX(70)), ceil(ScaleY(43)) };
static bool langPopupOpen = false;

ImVec2 langButtonDimensions = { ceil(ScaleX(50)), ceil(ScaleY(43)) };
SetCursorPos({ viewport->Size.x - closeButtonDimensions.x - langButtonDimensions.x, 0 });

PushStyleVar(ImGuiStyleVar_ChildRounding, 0);
PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.f, 0.f, 0.f, 0.f));
static bool isLangButtonHovered = false;
if (isLangButtonHovered)
PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.f, 1.f, 1.f, 0.06f));

BeginChild("##LangButton", { langButtonDimensions.x, langButtonDimensions.y }, false, ImGuiWindowFlags_NoScrollbar);
{
SetCursorPos({ (langButtonDimensions.x - ScaleX(20)) * 0.5f, ScaleY(12) });
Image((ImTextureID)(intptr_t)languageIconTexture, { ScaleX(20), ScaleY(20) });
}
EndChild();

ImVec2 langBtnMin = GetItemRectMin();
ImVec2 langBtnMax = GetItemRectMax();

if (isLangButtonHovered)
PopStyleColor();
PopStyleColor();
PopStyleVar();

if (IsItemClicked(ImGuiMouseButton_Left))
OpenPopup("##LangPopup");

if (IsItemHovered())
SetMouseCursor(ImGuiMouseCursor_Hand);

isLangButtonHovered = IsItemHovered();

ImGuiIO& io = GetIO();
ImFont* vietItemFont = (io.Fonts->Fonts.Size > 2) ? io.Fonts->Fonts[2] : nullptr;
ImFont* dropdownFont = (io.Fonts->Fonts.Size > 3) ? io.Fonts->Fonts[3] : nullptr;

const auto& langs = Locale::GetAvailableLanguages();
const std::string& currentLangId = Locale::GetCurrentLanguageId();

const float popupWidth = ScaleX(400);
float anim = EaseInOutFloat("##LangPopupAnim", 0.f, 1.f, IsPopupOpen("##LangPopup"), 0.35f);

float popupY = langBtnMax.y - ScaleY(6) * (1.f - anim);
SetNextWindowPos({ langBtnMax.x - popupWidth, popupY });
SetNextWindowSize({ popupWidth, ScaleY(500) });
SetNextWindowBgAlpha(anim);

PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.10f, 0.10f, 0.11f, 1.0f));
PushStyleColor(ImGuiCol_Header, ImVec4(0.20f, 0.21f, 0.22f, 1.0f));
PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.26f, 0.27f, 0.28f, 1.0f));
PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.23f, 0.25f, 1.0f));
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ScaleX(6), ScaleY(6)));
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ScaleX(8), ScaleY(6)));
PushStyleVar(ImGuiStyleVar_PopupRounding, ScaleX(4));

if (dropdownFont)
PushFont(dropdownFont);

if (BeginPopup("##LangPopup", ImGuiWindowFlags_NoMove)) {
if (!::IsWindowFocused())
CloseCurrentPopup();
PushStyleVar(ImGuiStyleVar_Alpha, anim);
for (const auto& lang : langs) {
bool isSelected = (lang.id == currentLangId);
bool useVietFont = (lang.id == "vietnamese") && (vietItemFont != nullptr);
if (useVietFont) {
if (dropdownFont)
PopFont();
PushFont(vietItemFont);
}
if (Selectable(lang.displayName.c_str(), isSelected, 0, ImVec2(popupWidth, 0))) {
Locale::SetLanguage(lang.id);
RequestFontRebuild();
CloseCurrentPopup();
}
if (isSelected)
SetItemDefaultFocus();
if (useVietFont) {
PopFont();
if (dropdownFont)
PushFont(dropdownFont);
}
}
PopStyleVar();
EndPopup();
}

if (dropdownFont)
PopFont();

PopStyleVar(3);
PopStyleColor(4);

SetCursorPos({ viewport->Size.x - closeButtonDimensions.x, 0 });

PushStyleVar(ImGuiStyleVar_ChildRounding, 0);
Expand All @@ -110,7 +211,11 @@ bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)

if (IsItemClicked(ImGuiMouseButton_Left) && !IsWorkerBusy()) {
JoinWorker();
#ifdef _WIN32
std::exit(0);
#else
_exit(0);
#endif
}

if (isCloseButtonHovered) {
Expand All @@ -124,3 +229,7 @@ bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)

return IsItemHovered() || (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && IsMouseDown(ImGuiMouseButton_Left));
}

void RenderLanguageSelector(float xPos)
{
}
Loading