From 787d18e12beaea089e12fff786a2c9e80004d1e0 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 27 Dec 2023 00:45:34 +0100 Subject: [PATCH] Add launcher window --- src/CMakeLists.txt | 8 + src/common/platform/win32/i_system.cpp | 5 + src/common/widgets/launcherwindow.cpp | 233 +++++++++++++++++++++++++ src/common/widgets/launcherwindow.h | 39 +++++ 4 files changed, 285 insertions(+) create mode 100644 src/common/widgets/launcherwindow.cpp create mode 100644 src/common/widgets/launcherwindow.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e43ab843cf..8efa53170a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -324,6 +324,8 @@ if (HAVE_VULKAN) list( APPEND PROJECT_LIBRARIES "zvulkan" ) endif() +list( APPEND PROJECT_LIBRARIES "zwidget" ) + # ZMUSIC if( MSVC ) @@ -589,6 +591,7 @@ file( GLOB HEADER_FILES common/models/*.h common/textures/*.h common/startscreen/*.h + common/widgets/*.h common/textures/hires/hqnx/*.h common/textures/hires/hqnx_asm/*.h common/textures/hires/xbr/*.h @@ -996,6 +999,7 @@ set (PCH_SOURCES common/startscreen/startscreen_strife.cpp common/startscreen/startscreen_generic.cpp common/startscreen/endoom.cpp + common/widgets/launcherwindow.cpp common/fonts/singlelumpfont.cpp common/fonts/singlepicfont.cpp common/fonts/specialfont.cpp @@ -1287,6 +1291,7 @@ include_directories( common/fonts common/objects common/startscreen + common/widgets common/rendering common/rendering/hwrenderer/data common/rendering/gl_load @@ -1322,6 +1327,7 @@ include_directories( scripting/zscript rendering ../libraries/ZVulkan/include + ../libraries/ZWidget/include ${SYSTEM_SOURCES_DIR} ) @@ -1481,6 +1487,8 @@ source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/co source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+") source_group("Common\\File System\\Include" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/include/.+") source_group("Common\\File System\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/source/.+") +source_group("Common\\Startscreen" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/startscreen/.+") +source_group("Common\\Widgets" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/widgets/.+") source_group("Common\\Scripting" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/.+") source_group("Common\\Scripting\\Interface" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/interface/.+") source_group("Common\\Scripting\\Frontend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/frontend/.+" FILES ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.c ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.h) diff --git a/src/common/platform/win32/i_system.cpp b/src/common/platform/win32/i_system.cpp index 568a18a21e..e2d57b51d4 100644 --- a/src/common/platform/win32/i_system.cpp +++ b/src/common/platform/win32/i_system.cpp @@ -87,6 +87,8 @@ #include "i_interface.h" #include "i_mainwindow.h" +#include "common/widgets/launcherwindow.h" + // MACROS ------------------------------------------------------------------ #ifdef _MSC_VER @@ -493,12 +495,15 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad, int& } if (showwin || (vkey != 0 && GetAsyncKeyState(vkey))) { + /* WadList = wads; NumWads = numwads; DefaultWad = defaultiwad; return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG), (HWND)mainwindow.GetHandle(), (DLGPROC)IWADBoxCallback); + */ + return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags); } return defaultiwad; } diff --git a/src/common/widgets/launcherwindow.cpp b/src/common/widgets/launcherwindow.cpp new file mode 100644 index 0000000000..9355042951 --- /dev/null +++ b/src/common/widgets/launcherwindow.cpp @@ -0,0 +1,233 @@ + +#include "launcherwindow.h" +#include "v_video.h" +#include "version.h" +#include "i_interface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXTERN_CVAR(Bool, queryiwad); + +int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags) +{ + Size screenSize = GetScreenSize(); + double windowWidth = 615.0; + double windowHeight = 668.0; + + auto launcher = new LauncherWindow(wads, numwads, defaultiwad, autoloadflags); + launcher->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight); + launcher->Show(); + + DisplayWindow::RunLoop(); + + return 0; +} + +LauncherWindow::LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags) : Widget(nullptr, WidgetType::Window) +{ + SetWindowBackground(Colorf::fromRgba8(51, 51, 51)); + SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51)); + SetWindowCaptionColor(Colorf::fromRgba8(33, 33, 33)); + SetWindowCaptionTextColor(Colorf::fromRgba8(226, 223, 219)); + SetWindowTitle(GAMENAME); + + Logo = new ImageBox(this); + WelcomeLabel = new TextLabel(this); + VersionLabel = new TextLabel(this); + SelectLabel = new TextLabel(this); + GeneralLabel = new TextLabel(this); + ExtrasLabel = new TextLabel(this); + FullscreenCheckbox = new CheckboxLabel(this); + DisableAutoloadCheckbox = new CheckboxLabel(this); + DontAskAgainCheckbox = new CheckboxLabel(this); + LightsCheckbox = new CheckboxLabel(this); + BrightmapsCheckbox = new CheckboxLabel(this); + WidescreenCheckbox = new CheckboxLabel(this); + PlayButton = new PushButton(this); + ExitButton = new PushButton(this); + GamesList = new ListView(this); + + SelectLabel->SetText("Select which game file (IWAD) to run."); + PlayButton->SetText("Play Game"); + ExitButton->SetText("Exit"); + + GeneralLabel->SetText("General"); + ExtrasLabel->SetText("Extra Graphics"); + FullscreenCheckbox->SetText("Fullscreen"); + DisableAutoloadCheckbox->SetText("Disable autoload"); + DontAskAgainCheckbox->SetText("Don't ask me again"); + LightsCheckbox->SetText("Lights"); + BrightmapsCheckbox->SetText("Brightmaps"); + WidescreenCheckbox->SetText("Widescreen"); + + FString welcomeText, versionText; + welcomeText.Format("Welcome to %s!", GAMENAME); + versionText.Format("Version %s.", GetVersionString()); + WelcomeLabel->SetText(welcomeText.GetChars()); + VersionLabel->SetText(versionText.GetChars()); + + int flags = *autoloadflags; + FullscreenCheckbox->SetChecked(vid_fullscreen); + DisableAutoloadCheckbox->SetChecked(flags & 1); + DontAskAgainCheckbox->SetChecked(!queryiwad); + LightsCheckbox->SetChecked(flags & 2); + BrightmapsCheckbox->SetChecked(flags & 4); + WidescreenCheckbox->SetChecked(flags & 8); + + for (int i = 0; i < numwads; i++) + { + const char* filepart = strrchr(wads[i].Path.GetChars(), '/'); + if (filepart == NULL) + filepart = wads[i].Path.GetChars(); + else + filepart++; + + FString work; + if (*filepart) work.Format("%s (%s)", wads[i].Name.GetChars(), filepart); + else work = wads[i].Name.GetChars(); + + GamesList->AddItem(work.GetChars()); + } + + /*try + { + auto filedata = ReadAllBytes("C:/Development/ZWidget/example/banner.png"); + std::vector pixels; + unsigned long width = 0, height = 0; + int result = decodePNG(pixels, width, height, (const unsigned char*)filedata.data(), filedata.size(), true); + if (result == 0) + { + Logo->SetImage(Image::Create(width, height, ImageFormat::R8G8B8A8, pixels.data())); + } + } + catch (...) + { + }*/ +} + +void LauncherWindow::OnGeometryChanged() +{ + double y = 0.0; + + Logo->SetFrameGeometry(0.0, y, GetWidth(), Logo->GetPreferredHeight()); + y += Logo->GetPreferredHeight(); + + y += 10.0; + + WelcomeLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, WelcomeLabel->GetPreferredHeight()); + y += WelcomeLabel->GetPreferredHeight(); + + VersionLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, VersionLabel->GetPreferredHeight()); + y += VersionLabel->GetPreferredHeight(); + + y += 10.0; + + SelectLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, SelectLabel->GetPreferredHeight()); + y += SelectLabel->GetPreferredHeight(); + + double listViewTop = y + 10.0; + + y = GetHeight() - 15.0 - PlayButton->GetPreferredHeight(); + PlayButton->SetFrameGeometry(20.0, y, 120.0, PlayButton->GetPreferredHeight()); + ExitButton->SetFrameGeometry(GetWidth() - 20.0 - 120.0, y, 120.0, PlayButton->GetPreferredHeight()); + + y -= 20.0; + + double panelWidth = 150.0; + y -= DontAskAgainCheckbox->GetPreferredHeight(); + DontAskAgainCheckbox->SetFrameGeometry(20.0, y, 190.0, DontAskAgainCheckbox->GetPreferredHeight()); + WidescreenCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, WidescreenCheckbox->GetPreferredHeight()); + + y -= DisableAutoloadCheckbox->GetPreferredHeight(); + DisableAutoloadCheckbox->SetFrameGeometry(20.0, y, 190.0, DisableAutoloadCheckbox->GetPreferredHeight()); + BrightmapsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, BrightmapsCheckbox->GetPreferredHeight()); + + y -= FullscreenCheckbox->GetPreferredHeight(); + FullscreenCheckbox->SetFrameGeometry(20.0, y, 190.0, FullscreenCheckbox->GetPreferredHeight()); + LightsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, LightsCheckbox->GetPreferredHeight()); + + y -= GeneralLabel->GetPreferredHeight(); + GeneralLabel->SetFrameGeometry(20.0, y, 190.0, GeneralLabel->GetPreferredHeight()); + ExtrasLabel->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, ExtrasLabel->GetPreferredHeight()); + + double listViewBottom = y - 10.0; + GamesList->SetFrameGeometry(20.0, listViewTop, GetWidth() - 40.0, std::max(listViewBottom - listViewTop, 0.0)); +} + + +// To do: clean this up +#ifdef WIN32 +#include +#include +static std::wstring to_utf16(const std::string& str) +{ + if (str.empty()) return {}; + int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0); + if (needed == 0) + throw std::runtime_error("MultiByteToWideChar failed"); + std::wstring result; + result.resize(needed); + needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size()); + if (needed == 0) + throw std::runtime_error("MultiByteToWideChar failed"); + return result; +} + +static std::vector ReadAllBytes(const std::string& filename) +{ + HANDLE handle = CreateFile(to_utf16(filename).c_str(), 0x0001/*FILE_READ_ACCESS*/, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (handle == INVALID_HANDLE_VALUE) + throw std::runtime_error("Could not open " + filename); + + LARGE_INTEGER fileSize; + BOOL result = GetFileSizeEx(handle, &fileSize); + if (result == FALSE) + { + CloseHandle(handle); + throw std::runtime_error("GetFileSizeEx failed"); + } + + std::vector buffer(fileSize.QuadPart); + + DWORD bytesRead = 0; + result = ReadFile(handle, buffer.data(), (DWORD)buffer.size(), &bytesRead, nullptr); + if (result == FALSE || bytesRead != buffer.size()) + { + CloseHandle(handle); + throw std::runtime_error("ReadFile failed"); + } + + CloseHandle(handle); + + return buffer; +} + +std::vector LoadWidgetFontData(const std::string& name) +{ + return ReadAllBytes("C:\\Windows\\Fonts\\segoeui.ttf"); +} + +Size LauncherWindow::GetScreenSize() +{ + HDC screenDC = GetDC(0); + int screenWidth = GetDeviceCaps(screenDC, HORZRES); + int screenHeight = GetDeviceCaps(screenDC, VERTRES); + double dpiScale = GetDeviceCaps(screenDC, LOGPIXELSX) / 96.0; + ReleaseDC(0, screenDC); + + return Size(screenWidth / dpiScale, screenHeight / dpiScale); +} + +#else +std::vector LoadWidgetFontData(const std::string& name) +{ + return {}; +} +#endif diff --git a/src/common/widgets/launcherwindow.h b/src/common/widgets/launcherwindow.h new file mode 100644 index 0000000000..daad2df8cf --- /dev/null +++ b/src/common/widgets/launcherwindow.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +class ImageBox; +class TextLabel; +class CheckboxLabel; +class PushButton; +class ListView; +struct WadStuff; + +class LauncherWindow : public Widget +{ +public: + static int ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags); + +private: + LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags); + + void OnGeometryChanged() override; + + ImageBox* Logo = nullptr; + TextLabel* WelcomeLabel = nullptr; + TextLabel* VersionLabel = nullptr; + TextLabel* SelectLabel = nullptr; + TextLabel* GeneralLabel = nullptr; + TextLabel* ExtrasLabel = nullptr; + CheckboxLabel* FullscreenCheckbox = nullptr; + CheckboxLabel* DisableAutoloadCheckbox = nullptr; + CheckboxLabel* DontAskAgainCheckbox = nullptr; + CheckboxLabel* LightsCheckbox = nullptr; + CheckboxLabel* BrightmapsCheckbox = nullptr; + CheckboxLabel* WidescreenCheckbox = nullptr; + PushButton* PlayButton = nullptr; + PushButton* ExitButton = nullptr; + ListView* GamesList = nullptr; + + static Size GetScreenSize(); +};