diff --git a/.gitmodules b/.gitmodules index 8aa167299c..1baff550bf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "tools/blender/fast64"] path = tools/blender/fast64 url = https://github.com/Fast-64/fast64.git +[submodule "hmui"] + path = hmui + url = https://github.com/KiritoDv/HMUI diff --git a/CMakeLists.txt b/CMakeLists.txt index c4870b59b0..9a30d2196b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,11 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/assets ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/port + ${CMAKE_CURRENT_SOURCE_DIR}/src/port/ui + ${CMAKE_CURRENT_SOURCE_DIR}/src/port/hmui + ${CMAKE_CURRENT_SOURCE_DIR}/src/port/hmui/os + ${CMAKE_CURRENT_SOURCE_DIR}/hmui/src ${CMAKE_CURRENT_SOURCE_DIR}/src/racing ${CMAKE_CURRENT_SOURCE_DIR}/src/ending ${CMAKE_CURRENT_SOURCE_DIR}/src/data @@ -283,6 +288,14 @@ file(GLOB_RECURSE ALL_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/enhancements/freecam/*.h" "src/engine/*.cpp" "src/engine/*.h" + "hmui/src/hmui/HMUI.cpp" + "hmui/src/hmui/Navigator.cpp" + "hmui/src/hmui/MouseEvents.cpp" + "hmui/src/hmui/input/FocusManager.cpp" + "hmui/src/hmui/*.h" + "hmui/src/hmui/graphics/ImGuiGraphicsContext.cpp" + "hmui/src/hmui/graphics/ImGuiGraphicsContext.h" + "hmui/src/hmui/graphics/GraphicsContext.h" "Resource.rc" ) diff --git a/hmui b/hmui new file mode 160000 index 0000000000..05f257e4e6 --- /dev/null +++ b/hmui @@ -0,0 +1 @@ +Subproject commit 05f257e4e6c38fc1214a14c976a479efb005eea2 diff --git a/include/align_asset_macro.h b/include/align_asset_macro.h index 8292815f85..d889c1db4f 100644 --- a/include/align_asset_macro.h +++ b/include/align_asset_macro.h @@ -1,6 +1,12 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif bool GameEngine_OTRSigCheck(const char* imgData); +#ifdef __cplusplus +} +#endif #if defined(_WIN32) #define ALIGN_ASSET(x) __declspec(align(x)) diff --git a/src/engine/TrackBrowser.h b/src/engine/TrackBrowser.h index 4bcccf16fe..47624e02c5 100644 --- a/src/engine/TrackBrowser.h +++ b/src/engine/TrackBrowser.h @@ -189,4 +189,4 @@ const char* TrackBrowser_GetMinimapTextureByIdx(size_t trackIndex); } #endif // __cplusplus -#endif // TRACKBROWSER_H \ No newline at end of file +#endif // TRACKBROWSER_H diff --git a/src/enhancements/freecam/freecam.h b/src/enhancements/freecam/freecam.h index 536b723795..66c113a905 100644 --- a/src/enhancements/freecam/freecam.h +++ b/src/enhancements/freecam/freecam.h @@ -15,6 +15,7 @@ void freecam_loop(Camera*); void freecam_update_controller(void); void freecam_mouse_manager(Camera*, Vec3f); void freecam_keyboard_manager(Camera*, Vec3f); +bool FreecamKeyDown(int virtualKey); extern f32 gFreecamSpeed; extern f32 gFreecamSpeedMultiplier; diff --git a/src/main.c b/src/main.c index 2fe0b453d6..d607a1e3e6 100644 --- a/src/main.c +++ b/src/main.c @@ -842,9 +842,24 @@ void game_state_handler(void) { case COURSE_SELECT_MENU_FROM_QUIT: // Display black osViBlack(0); - update_menus(); + if (CVarGetInteger("gNewMenu", true) == true) { + switch(gMenuSelection) { + case CONTROLLER_PAK_MENU: + case HARBOUR_MASTERS_MENU: + case LOGO_INTRO_MENU: + case START_MENU: + update_menus(); // Tick old menu + break; + } + } else { + update_menus(); + } init_rcp(); - func_80094A64(gGfxPool); + if ((CVarGetInteger("gNewMenus", true) == true) && (gMenuSelection == MAIN_MENU)) { + HMUI_SetActive(true); + } else { + func_80094A64(gGfxPool); // Draw old menu + } #if DVDL display_dvdl(); #endif diff --git a/src/main.h b/src/main.h index 5b2772631c..cc586c8656 100644 --- a/src/main.h +++ b/src/main.h @@ -63,6 +63,10 @@ typedef struct { u16 numTriangles; } CollisionGrid; +#ifdef __cplusplus +extern "C" { +#endif + void create_thread(OSThread*, OSId, void (*entry)(void*), void*, void*, OSPri); void main_func(void); void thread1_idle(void*); @@ -228,4 +232,8 @@ extern f32 gCourseTimer; // end of definition of main.c variables +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 2cd2c99b9a..8e8808fdd8 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -37,6 +37,12 @@ #include +#include "hmui/HMUI.h" +#include "hmui/graphics/ImGuiGraphicsContext.h" +#include "hmui/graphics/GraphicsContext.h" +#include "port/ui/hmui/os/LUSOSContext.h" +#include "port/ui/hmui/demo/Router.h" + #ifdef __SWITCH__ #include #endif @@ -64,6 +70,7 @@ Fast::Interpreter* GetInterpreter() { } GameEngine* GameEngine::Instance; +std::shared_ptr hmui; // New menu system bool CreateDirectoryRecursive(std::string const& dirName, std::error_code& err) { err.clear(); @@ -348,6 +355,11 @@ void GameEngine::Create() { InitModsSystem(); instance->gHMAS = new HMAS(); instance->AudioInit(); + + hmui = std::make_shared(); + hmui->initialize(std::make_shared(), std::make_shared()); + hmui->setRouter(std::make_shared()); + hmui->setActive(false); GameUI::SetupGuiElements(); #if defined(__SWITCH__) || defined(__WIIU__) CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u @@ -446,6 +458,8 @@ void GameEngine::ProcessGfxCommands(Gfx* pool) { // time_base = fps * original_fps (one second) int next_original_frame = fps; + float s = fps / (60.0f / 2 /*gVIsPerFrame*/); + hmui->update(s); // Get matrix replacements for intermediate frames while (time + original_fps <= next_original_frame) { @@ -471,6 +485,14 @@ void GameEngine::ProcessGfxCommands(Gfx* pool) { last_update_rate = 2; } +void GameEngine::RenderHMUI() { + auto wnd = std::dynamic_pointer_cast(Ship::Context::GetInstance()->GetWindow()); + if (wnd != nullptr) { + auto ctx = GfxList { (void*) ImGui::GetWindowDrawList() }; + hmui->draw(&ctx, wnd->GetWidth(), wnd->GetHeight()); + } +} + // Audio void GameEngine::HandleAudioThread() { while (audio.running) { @@ -617,6 +639,10 @@ ImFont* GameEngine::CreateFontWithSize(float size, std::string fontPath) { // End +extern "C" void HMUI_SetActive(bool state) { + hmui->setActive(state); +} + extern "C" uint32_t GameEngine_GetSampleRate() { auto player = Ship::Context::GetInstance()->GetAudio()->GetAudioPlayer(); if (player == nullptr) { diff --git a/src/port/Engine.h b/src/port/Engine.h index d5368a92f5..c81727ebc6 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -76,6 +76,7 @@ class GameEngine { void ProcessFrame(void (*run_one_game_iter)()) const; static void Destroy(); static void ProcessGfxCommands(Gfx* pool); + static void RenderHMUI(); static uint8_t GetBankIdByName(const std::string& name); static int ShowYesNoBox(const char* title, const char* box); static void ShowMessage(const char* title, const char* message, SDL_MessageBoxFlags type = SDL_MESSAGEBOX_ERROR); @@ -110,6 +111,7 @@ struct AudioSequenceData* GameEngine_LoadSequence(uint8_t seqId); uint32_t GameEngine_GetSequenceCount(); uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId); void GameEngine_UnloadSequence(uint8_t seqId); +void HMUI_SetActive(bool state); // bool GameEngine_OTRSigCheck(char* imgData); -> align_asset_macro.h float OTRGetAspectRatio(void); float OTRGetDimensionFromLeftEdge(float v); diff --git a/src/port/SpaghettiGui.cpp b/src/port/SpaghettiGui.cpp index d9c957d3f2..97d597927c 100644 --- a/src/port/SpaghettiGui.cpp +++ b/src/port/SpaghettiGui.cpp @@ -1,5 +1,6 @@ #include #include "SpaghettiGui.h" +#include "Engine.h" #include #include #ifdef __SWITCH__ @@ -158,6 +159,16 @@ namespace Ship { } ImGui::End(); + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(ImVec2((int)wnd->GetWidth(), (int)wnd->GetHeight())); +// ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); + ImGui::Begin("HMUI", nullptr, windowFlags); + ImGui::PopStyleVar(3); + GameEngine::RenderHMUI(); + ImGui::End(); } -} \ No newline at end of file +} diff --git a/src/port/ui/hmui/demo/CCView.h b/src/port/ui/hmui/demo/CCView.h new file mode 100644 index 0000000000..9a9b90483e --- /dev/null +++ b/src/port/ui/hmui/demo/CCView.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" + +// singleplayer, split-screen, online +class CCViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + std::string modes[] = {"50CC", "100CC", "150CC", "Extra"}; + + for(int i = 0; i < 4; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + + View_SetCC(i); + Navigator::push("/player_select"); + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~CCViewElements() override = default; +}; + +class CCView : public Drawable { +public: + std::shared_ptr build() override { + // Render CCView + return std::make_shared(); + } + + ~CCView() override = default; +}; diff --git a/src/port/ui/hmui/demo/DemoView.h b/src/port/ui/hmui/demo/DemoView.h new file mode 100644 index 0000000000..43c36189f4 --- /dev/null +++ b/src/port/ui/hmui/demo/DemoView.h @@ -0,0 +1,130 @@ +#pragma once + +#include +#include + +#include "hmui/widgets/AppContext.h" +#include "hmui/widgets/GestureDetector.h" +#include "hmui/widgets/InternalDrawable.h" +#include "hmui/widgets/Column.h" +#include "hmui/widgets/Container.h" +#include "hmui/widgets/Scrollable.h" +#include "hmui/widgets/Drawable.h" +#include "hmui/graphics/GraphicsContext.h" + +std::shared_ptr nestedTest(std::vector entries, size_t index = 0) { + if (index >= entries.size()) { + return Container(); // base case: empty container + } + + return Container( + .padding = EdgeInsets::all(10.0f), + .alignment = Alignment::Center, + .color = entries[index], + .child = GestureDetector( + .onTap = [](std::shared_ptr& child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr& child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = Color2D( + rand() % 256 / 255.0f, + rand() % 256 / 255.0f, + rand() % 256 / 255.0f + ); + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .child = nestedTest(entries, index + 1), + ) + ); +} + +class TestView : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + for(int i = 0; i < 30; ++i) { + entries.push_back(GestureDetector( + .onTap = [](std::shared_ptr& child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = Color2D( + rand() % 256 / 255.0f, + rand() % 256 / 255.0f, + rand() % 256 / 255.0f + ); + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr& child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + }, + .child = Container( + .width = 200.0f, + .height = 100.0f, + .color = Color2D( + rand() % 256 / 255.0f, + rand() % 256 / 255.0f, + rand() % 256 / 255.0f + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + return Container( + .child = Container( + .width = 200.0f, + .height = 300.0f, + .padding = EdgeInsets::all(5.0f), + .clipToBounds = true, + .color = Color2D(0.0f, 0.0f, 0.0f, 0.3f), + .child = Scrollable( + .direction = Direction::Vertical, + .child = Column( + .children = entries + ) + ) + ) + ); + } + + ~TestView() override = default; +}; + +class DemoSubView : public Drawable { +protected: + std::shared_ptr child; +public: + + explicit DemoSubView(std::shared_ptr _child): child(std::move(_child)) {} + + std::shared_ptr build() override { + // Render TestView + return this->child; + } + + ~DemoSubView() override = default; +}; + + + +class DemoView : public Drawable { +public: + std::shared_ptr build() override { + return AppContext( + .routes = { + { "/", []() { return std::make_shared(); }} + }, + .initialRoute = "/" + ); + } + ~DemoView() override = default; +}; + diff --git a/src/port/ui/hmui/demo/GamemodeView.h b/src/port/ui/hmui/demo/GamemodeView.h new file mode 100644 index 0000000000..1c3c972024 --- /dev/null +++ b/src/port/ui/hmui/demo/GamemodeView.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" +#include + +#include "port/Engine.h" +#include "hmui/Navigator.h" + +extern "C" { +#include "main.h" +} + +class GamemodeViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + std::string multiplayer_modes[] = {"Grand Prix", "Versus", "Battle"}; + std::string single_player_modes[] = {"Grand Prix", "Time Trials"}; + + // This is really bad code... Too bad! + std::string* modes = &multiplayer_modes[0]; + size_t numModes = 3; + if (view_singleplayer == true) { + numModes = 2; + modes = &single_player_modes[0]; + } + + for(size_t i = 0; i < numModes; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i, numModes](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + + switch(i) { + case 0: + Navigator::push("/cc"); + break; + case 1: + if (view_singleplayer) { + Navigator::push("/player_select"); + } else { + Navigator::push("/cc"); + } + break; + case 2: + Navigator::push("/player_select"); + break; + } + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + + return BuildMenuStack(stuff); + } + + ~GamemodeViewElements() override = default; +}; + +class GamemodeView : public Drawable { +public: + std::shared_ptr build() override { + // Render GamemodeView + return std::make_shared(); + } + + ~GamemodeView() override = default; +}; + diff --git a/src/port/ui/hmui/demo/LayoutSettings.h b/src/port/ui/hmui/demo/LayoutSettings.h new file mode 100644 index 0000000000..8d86358a81 --- /dev/null +++ b/src/port/ui/hmui/demo/LayoutSettings.h @@ -0,0 +1,159 @@ +#pragma once + +#include "port/Engine.h" +#include "port/ui/hmui/provider/LUSImageProvider.h" + +#include "hmui/Navigator.h" +#include "hmui/graphics/GraphicsContext.h" +#include "hmui/widgets/InternalDrawable.h" +#include "hmui/widgets/GestureDetector.h" +#include "hmui/widgets/Row.h" +#include "hmui/widgets/Column.h" +#include "hmui/widgets/Container.h" +#include "hmui/widgets/Scrollable.h" +#include "hmui/widgets/Drawable.h" +#include "hmui/widgets/Image.h" +#include "hmui/widgets/Text.h" +#include "hmui/widgets/Wrap.h" +#include "hmui/widgets/Stack.h" +#include "hmui/widgets/Image.h" + +#define DEBUG_COMPONENTS + +/** Variables **/ +bool view_singleplayer = false; + +/** Buttons **/ + +#define MENU_BUTTON_WIDTH 325.0f +#define MENU_BUTTON_HEIGHT 75.0f +#define MENU_BUTTON_COLOUR Color2D(0.0f, 0.0f, 0.0f, 0.0f) + +#define MENU_BUTTON_TEXT_COLOUR Color2D(1.0f, 1.0f, 1.0f, 1.0f) +#define MENU_BUTTON_TEXT_SCALE 1.8f + +/** General Container Layout **/ + +#define BACKGROUND_COLOUR Color2D(0.0f, 0.0f, 0.0f, 0.8f) +#define MARGIN_LEFT_SPACING EdgeInsets::only(580.0f, 360.0f, 0.0f, 0.0f) +#define CONTENT_HEIGHT entries.size() * MENU_BUTTON_HEIGHT + +/** Events **/ + +#define BUTTON_ON_TAP_COLOUR Color2D(0.0f, 0.0f, 0.0f, 0.60f) +#define BUTTON_ON_HOVER_COLOUR Color2D(0.0f, 0.0f, 0.0f, 0.40f) + +/** Multiplayer Player Count Select */ +#define MULTIPLAYER_SELECT_BUTTON_WIDTH 300.0f +#define MULTIPLAYER_SELECT_BUTTON_HEIGHT 300.0f +#define MULTIPLAYER_SELECT_BUTTON_SPACING 60.0f +#define MULTIPLAYER_SELECT_BUTTON_COLOUR Color2D(0.3f, 0.3f, 0.6f, 0.4f) + +/** Player Select **/ +#define PLAYER_SELECT_ROWS 4 +#define PLAYER_SELECT_BUTTON_WIDTH 367.0f +#define PLAYER_SELECT_BUTTON_HEIGHT 368.0f +#define PLAYER_SELECT_BUTTON_SPACING 29.0f +#define PLAYER_SELECT_BUTTON_RUN_SPACING 34.0f +#define PLAYER_SELECT_LABEL_HEIGHT 69.0f + +/** Track Select **/ +#define TRACK_SELECT_ROWS 3 +#define TRACK_SELECT_BUTTON_WIDTH 325.0f +#define TRACK_SELECT_BUTTON_HEIGHT 350.0f +#define TRACK_SELECT_BUTTON_SPACING 29.0f +#define TRACK_SELECT_BUTTON_RUN_SPACING 34.0f +#define TRACK_SELECT_LABEL_HEIGHT 69.0f + +/** + * This should be the base of your View as this + * allows the placing of multiple elements around the screen + */ +inline std::shared_ptr BuildMenuStack( + const std::vector> entries +) { + return Container( + + .child = Stack( + .children = entries, + .fit = StackFit::Expand, + ) + ); +} + +inline std::shared_ptr BuildMenuBackground() { + return Container( + .width = (float)OTRGetGameViewportWidth(), + .height = (float)OTRGetGameViewportHeight(), + .clipToBounds = true, + .color = BACKGROUND_COLOUR + ); +} + +inline std::shared_ptr BuildMenuContent( + const std::vector> entries +) { + return Container( + .margin = MARGIN_LEFT_SPACING, + .clipToBounds = false, + .child = Column( .children = entries ) + ); +} + +inline std::shared_ptr BuildMenuInfoBar() { + return Container( + .width = (float)OTRGetGameViewportWidth(), + .height = 100.0f, + + .color = Color2D(0.0f, 0.0f, 0.0f, 0.4f), + .child = Container( + .margin = EdgeInsets::only(25, 0, 0, 0), + .alignment = Alignment::CenterLeft(), + .child = Text( + .text = "Back: B, Backspace, or Right-click", + .scale = MENU_BUTTON_TEXT_SCALE, + .alignV = VerticalAlign::Center, + .color = MENU_BUTTON_TEXT_COLOUR + ) + ), + ); +} + +#include "engine/TrackBrowser.h" +extern "C" { +#include "main.h" +#include "menus.h" +#include "code_800029B0.h" +extern s8 gPlayerCount; +} + +/** Menu Game Actions **/ +void View_SetPlayerCount(size_t count) { + gPlayerCountSelection1 = count; + gPlayerCount = count; +} + +void View_SetScreenMode(s32 screenMode) { + gScreenModeSelection = screenMode; +} + +void View_SetCC(s32 cc) { + gCCSelection = cc; +} + +void View_SetGamemode(s32 gamemode) { + gModeSelection = gamemode; +} + +void View_SelectPlayer(s32 playerId, s32 characterId) { + if ((playerId >= 0) && (playerId < 4)) { + gCharacterSelections[playerId] = characterId; + } +} + +void View_SetTrack(const std::string& resourceName) { + TrackBrowser::Instance->SetTrack(resourceName.c_str()); + gGamestateNext = RACING; + Navigator::pushReplacement("/"); + HMUI::Instance->setActive(false); +} diff --git a/src/port/ui/hmui/demo/MainView.h b/src/port/ui/hmui/demo/MainView.h new file mode 100644 index 0000000000..c24c6acfae --- /dev/null +++ b/src/port/ui/hmui/demo/MainView.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" + +// singleplayer, split-screen, online +class MainViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + std::string modes[] = {"Singleplayer", "Multiplayer", "Online"}; + + for(int i = 0; i < 3; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + + switch(i) { + case 0: + view_singleplayer = true; + View_SetPlayerCount(1); + View_SetScreenMode(SCREEN_MODE_1P); + Navigator::push("/gamemode"); + break; + case 1: + view_singleplayer = false; + Navigator::push("/multiplayer"); + break; + case 2: + Navigator::push("/online_main"); + break; + } + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~MainViewElements() override = default; +}; + +class MainView : public Drawable { +public: + std::shared_ptr build() override { + // Render MainViewElements + return std::make_shared(); + } + + ~MainView() override = default; +}; + diff --git a/src/port/ui/hmui/demo/MultiplayerView.h b/src/port/ui/hmui/demo/MultiplayerView.h new file mode 100644 index 0000000000..bbf77b82e9 --- /dev/null +++ b/src/port/ui/hmui/demo/MultiplayerView.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" + +// singleplayer, split-screen, online +class MultiplayerViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + bool useCont[4] = {false, false, false, false}; + void init() override { + + std::string modes[] = {"1P", "2P", "3P", "4P"}; + + for(int i = 0; i < ARRAY_COUNT(modes); ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onButton = [this, modes, i](std::shared_ptr child, int controllerId, uint16_t btn) { + + // Get text element + std::shared_ptr c = std::dynamic_pointer_cast(child); + auto text = std::dynamic_pointer_cast(c->properties.child); // + + if (controllerId != i) { + return; + } + + if (btn & BTN_START) { + View_SetPlayerCount(i); + Navigator::push("/gamemode"); + this->useCont[controllerId] = true; + return; + } + + if (btn & A_BUTTON) { + useCont[controllerId] = true; + text->properties.text = modes[i] + " Connected!"; + } + + if (btn & B_BUTTON) { + text->properties.text = modes[i] + " Disconnected!"; + } + + }, + .child = Container( + .width = MULTIPLAYER_SELECT_BUTTON_WIDTH, + .height = MULTIPLAYER_SELECT_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MULTIPLAYER_SELECT_BUTTON_COLOUR, + .child = Text( + .text = std::string(modes[i] + " " + (useCont[i] ? "Connected!" : "Disconnected!")), + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = Container( + .alignment = Alignment::Center(), + .child = Row( + .spacing = MULTIPLAYER_SELECT_BUTTON_SPACING, + .mainAxisAlignment = MainAxisAlignment::SPACE_BETWEEN, + .children = entries, + ) + ), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + virtual bool onBack(int controllerId) override { + if (useCont[controllerId] == true) { + useCont[controllerId] = false; + return true; + } + + for (size_t i = 0; i < 4; i++) { + if (useCont[i] == true) { + return true; + } + } + + Navigator::pop(); + return true; + } + + ~MultiplayerViewElements() override = default; +}; + +class MultiplayerView : public Drawable { +public: + std::shared_ptr build() override { + // Render MultiplayerViewElements + return std::make_shared(); + } + + ~MultiplayerView() override = default; +}; diff --git a/src/port/ui/hmui/demo/OnlineBattleView.h b/src/port/ui/hmui/demo/OnlineBattleView.h new file mode 100644 index 0000000000..3c4054a696 --- /dev/null +++ b/src/port/ui/hmui/demo/OnlineBattleView.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" + +// singleplayer, split-screen, online +class OnlineBattleViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + std::string modes[] = {"8P", "4P"}; + + for(int i = 0; i < 2; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + switch(i) { + default: + Navigator::push("/player_select"); + break; + } + + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~OnlineBattleViewElements() override = default; +}; + +class OnlineBattleView : public Drawable { +public: + std::shared_ptr build() override { + // Render OnlineBattleView + return std::make_shared(); + } + + ~OnlineBattleView() override = default; +}; diff --git a/src/port/ui/hmui/demo/OnlineMainView.h b/src/port/ui/hmui/demo/OnlineMainView.h new file mode 100644 index 0000000000..72dd8f2150 --- /dev/null +++ b/src/port/ui/hmui/demo/OnlineMainView.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" + +// singleplayer, split-screen, online +class OnlineMainViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + std::string modes[] = {"Race!", "Battle!"}; + + for(int i = 0; i < 2; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + switch(i) { + case 0: + Navigator::push("/online_race"); + break; + case 1: + Navigator::push("/online_battle"); + break; + } + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~OnlineMainViewElements() override = default; +}; + +class OnlineMainView : public Drawable { +public: + std::shared_ptr build() override { + // Render OnlineMainView + return std::make_shared(); + } + + ~OnlineMainView() override = default; +}; + diff --git a/src/port/ui/hmui/demo/OnlineRaceView.h b/src/port/ui/hmui/demo/OnlineRaceView.h new file mode 100644 index 0000000000..c19c566309 --- /dev/null +++ b/src/port/ui/hmui/demo/OnlineRaceView.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" +#include "hmui/widgets/InternalDrawable.h" +#include "hmui/widgets/GestureDetector.h" +#include "hmui/widgets/Column.h" +#include "hmui/widgets/Container.h" +#include "hmui/widgets/Scrollable.h" +#include "hmui/widgets/Drawable.h" +#include "hmui/widgets/Text.h" +#include "hmui/widgets/Image.h" +#include "hmui/graphics/GraphicsContext.h" + +// singleplayer, split-screen, online +class OnlineRaceViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + std::string modes[] = {"8P", "4P"}; + + for(int i = 0; i < 2; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + switch(i) { + default: + Navigator::push("/player_select"); + break; + } + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = MENU_BUTTON_WIDTH, + .height = MENU_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = modes[i], + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = BuildMenuContent(entries), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~OnlineRaceViewElements() override = default; +}; + +class OnlineRaceView : public Drawable { +public: + std::shared_ptr build() override { + // Render OnlineRaceView + return std::make_shared(); + } + + ~OnlineRaceView() override = default; +}; diff --git a/src/port/ui/hmui/demo/PlayerSelectView.h b/src/port/ui/hmui/demo/PlayerSelectView.h new file mode 100644 index 0000000000..44c0b3eac5 --- /dev/null +++ b/src/port/ui/hmui/demo/PlayerSelectView.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" +#include "hmui/widgets/InternalDrawable.h" + +extern "C" { +#include "menu_items.h" +} + +// singleplayer, split-screen, online +class PlayerSelectViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + void init() override { + + for(int i = 0; i < 8; ++i) { + entries.push_back(GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [i](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + Navigator::push("/track_select"); + View_SelectPlayer(0, i); + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = PLAYER_SELECT_BUTTON_WIDTH, + .height = PLAYER_SELECT_BUTTON_HEIGHT, + .color = MENU_BUTTON_COLOUR, + .child = Stack( + .children = { + Positioned( + .child = Container( + .width = PLAYER_SELECT_BUTTON_WIDTH, + // .height = PLAYER_SELECT_BUTTON_HEIGHT - 80, + .alignment = Alignment::Center(), + .child = Text(.text = "Preview") + ), + .top = 0, + .bottom = PLAYER_SELECT_LABEL_HEIGHT, + ), + Positioned( + .child = Container( + .width = PLAYER_SELECT_BUTTON_WIDTH, + .height = PLAYER_SELECT_LABEL_HEIGHT, + .alignment = Alignment::Center(), + .child = Text( + .text = D_800E76A8[i], // names + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ), + ), + .bottom = 0, + ), + }, + .fit = StackFit::Expand, + ), + ) + )); + } + Drawable::init(); + } + + std::shared_ptr build() override { + + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = Container( + .width = (PLAYER_SELECT_BUTTON_WIDTH + PLAYER_SELECT_BUTTON_SPACING) * PLAYER_SELECT_ROWS, + .height = entries.size() * PLAYER_SELECT_BUTTON_HEIGHT, + .alignment = Alignment::Center(), + .child = Wrap( + .direction = Direction::Horizontal, + .spacing = PLAYER_SELECT_BUTTON_SPACING, + .runSpacing = PLAYER_SELECT_BUTTON_RUN_SPACING, + .alignment = MainAxisAlignment::CENTER, + .children = entries + ), + ), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~PlayerSelectViewElements() override = default; +}; + +class PlayerSelectView : public Drawable { +public: + std::shared_ptr build() override { + // Render PlayerSelectView + return std::make_shared(); + } + + ~PlayerSelectView() override = default; +}; diff --git a/src/port/ui/hmui/demo/Router.h b/src/port/ui/hmui/demo/Router.h new file mode 100644 index 0000000000..82a580f352 --- /dev/null +++ b/src/port/ui/hmui/demo/Router.h @@ -0,0 +1,38 @@ +#pragma once + +#include "hmui/widgets/AppContext.h" +#include "hmui/widgets/Drawable.h" +#include "hmui/widgets/InternalDrawable.h" +#include "MainView.h" +#include "MultiplayerView.h" +#include "GamemodeView.h" +#include "PlayerSelectView.h" +#include "TrackSelectView.h" +#include "OnlineMainView.h" +#include "OnlineRaceView.h" +#include "OnlineBattleView.h" +#include "CCView.h" + +class RouterView : public Drawable { +public: + s32 menuOptions; + + std::shared_ptr build() override { + return AppContext( + .routes = { + { "/", []() { return std::make_shared(); }}, + { "/multiplayer", []() { return std::make_shared(); }}, + { "/gamemode", []() { return std::make_shared(); }}, + { "/cc", []() { return std::make_shared(); }}, + { "/player_select", []() { return std::make_shared(); }}, + { "/track_select", []() { return std::make_shared(); }}, + { "/online_main", []() { return std::make_shared(); }}, + { "/online_race", []() { return std::make_shared(); }}, + { "/online_battle", []() { return std::make_shared(); }}, + }, + .initialRoute = "/" + ); + } + + ~RouterView() override = default; +}; diff --git a/src/port/ui/hmui/demo/TrackSelectView.h b/src/port/ui/hmui/demo/TrackSelectView.h new file mode 100644 index 0000000000..586c3ce530 --- /dev/null +++ b/src/port/ui/hmui/demo/TrackSelectView.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include "LayoutSettings.h" + +#include "hmui/Navigator.h" +#include "hmui/widgets/InternalDrawable.h" +#include "hmui/widgets/Wrap.h" + +#include "port/Game.h" +#include "engine/registry/Registry.h" + +extern "C" { +#include "menu_items.h" +} + +// singleplayer, split-screen, online +class TrackSelectViewElements : public Drawable { +public: + // std::vector entries; + std::vector> entries; + + + void init() override { + std::vector> col1; + + std::vector infos = gTrackRegistry.GetAllInfo(); + + for(const TrackInfo* info : infos) { + auto entry = GestureDetector( + .focusable = true, + .focusDecorator = FocusDecorator { + .color = Color2D(1.0f, 1.0f, 1.0f, 0.8f), + .thickness = 2.0f + }, + .onTap = [info](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_TAP_COLOUR; + View_SetTrack(info->ResourceName); + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onTapRelease = [](std::shared_ptr child, float x, float y) { + // Handle tap event + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + std::cout << "Tapped on child at (" << x << ", " << y << ")\n"; + }, + .onHover = [](std::shared_ptr child, float x, float y) { + // Handle hover event + std::cout << "Hovered over child at (" << x << ", " << y << ")\n"; + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = BUTTON_ON_HOVER_COLOUR; + }, + .onHoverEnd = [](std::shared_ptr child, float x, float y) { + std::shared_ptr c = std::dynamic_pointer_cast(child); + c->properties.color = MENU_BUTTON_COLOUR; + }, + .child = Container( + .width = TRACK_SELECT_BUTTON_WIDTH, + .height = TRACK_SELECT_BUTTON_HEIGHT, + .color = MENU_BUTTON_COLOUR, + .child = Stack( + .children = { + Positioned( + .child = Container( + .width = TRACK_SELECT_BUTTON_WIDTH, + .alignment = Alignment::Center(), + .child = Text(.text = "Preview"), + ), + .top = 0, + .bottom = TRACK_SELECT_LABEL_HEIGHT + ), + Positioned( + .child = Container( + .width = TRACK_SELECT_BUTTON_WIDTH, + .height = TRACK_SELECT_LABEL_HEIGHT, + .alignment = Alignment::Center(), + .color = MENU_BUTTON_COLOUR, + .child = Text( + .text = info->Name, + .scale = MENU_BUTTON_TEXT_SCALE, + .color = MENU_BUTTON_TEXT_COLOUR + ) + ), + .bottom = 0, + ), + }, + .fit = StackFit::Expand, + ) + ) + ); + + entries.push_back(entry); + } + Drawable::init(); + } + + std::shared_ptr build() override { + std::vector> stuff = { + Positioned( + .child = BuildMenuBackground(), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + ), + Positioned( + .child = BuildMenuInfoBar(), + .left = 0, + .bottom = 0 + ), + Positioned( + .child = Container( + .width = (TRACK_SELECT_BUTTON_WIDTH + TRACK_SELECT_BUTTON_SPACING) * TRACK_SELECT_ROWS, + .height = 900.0f, + .alignment = Alignment::Center(), + .clipToBounds = true, + .child = Scrollable( + .direction = Direction::Vertical, + .child = Wrap( + .direction = Direction::Horizontal, + .spacing = TRACK_SELECT_BUTTON_SPACING, + .runSpacing = TRACK_SELECT_BUTTON_RUN_SPACING, + .alignment = MainAxisAlignment::CENTER, + .children = entries + ), + ), + ), + .left = 0, + .top = 0, + .right = 0, + .bottom = 0 + ), + }; + return BuildMenuStack(stuff); + } + + ~TrackSelectViewElements() override = default; +}; + +class TrackSelectView : public Drawable { +public: + std::shared_ptr build() override { + // Render TrackSelectView + return std::make_shared(); + } + + ~TrackSelectView() override = default; +}; diff --git a/src/port/ui/hmui/os/LUSOSContext.cpp b/src/port/ui/hmui/os/LUSOSContext.cpp new file mode 100644 index 0000000000..814c435cfe --- /dev/null +++ b/src/port/ui/hmui/os/LUSOSContext.cpp @@ -0,0 +1,169 @@ +#include "LUSOSContext.h" +#include "libultraship.h" +#include "Engine.h" +#include "freecam/freecam.h" + +#include "main.h" +#define MAX_AXIS_VALUE 85.0f + +void LUSOSContext::init() {} +void LUSOSContext::update() { + auto wnd = GameEngine::Instance->context->GetWindow(); + + for (int i = 0; i < 3; i++) { + m_LastMouseState[i] = m_CurrentMouseState[i]; + m_CurrentMouseState[i] = wnd->GetMouseState(static_cast(i)); + } +} +void LUSOSContext::dispose() {} + +Coord LUSOSContext::getMouseDelta() { + auto wnd = GameEngine::Instance->context->GetWindow(); + Ship::Coords delta = wnd->GetMouseDelta(); + return Coord(static_cast(delta.x), static_cast(delta.y)); +} + +Coord LUSOSContext::getMousePosition() { + auto wnd = GameEngine::Instance->context->GetWindow(); + Ship::Coords mouse = wnd->GetMousePos(); + return Coord(static_cast(mouse.x), static_cast(mouse.y)); +} + +void LUSOSContext::setMousePosition(Coord& pos) { + auto wnd = GameEngine::Instance->context->GetWindow(); + Ship::Coords mousePos = { static_cast(pos.x), static_cast(pos.y) }; + wnd->SetMousePos(mousePos); +} + +Coord LUSOSContext::getMouseWheel() { + auto wnd = GameEngine::Instance->context->GetWindow(); + Ship::CoordsF wheel = wnd->GetMouseWheel(); + return Coord(wheel.x * 4.0f, wheel.y * 4.0f); +} + +bool LUSOSContext::isMouseButtonPressed(int button) { + return m_CurrentMouseState[button] && !m_LastMouseState[button]; +} + +bool LUSOSContext::isMouseButtonReleased(int button) { + return !m_CurrentMouseState[button] && m_LastMouseState[button]; +} + +bool LUSOSContext::isMouseButtonDown(int button) { + return m_CurrentMouseState[button]; +} + +void LUSOSContext::setMouseCursor(int cursor) { + // Not implemented +} + +void LUSOSContext::setClipboardText(const char* text) { + // Not implemented +} + +bool LUSOSContext::isTouchDevice() { + auto wnd = GameEngine::Instance->context->GetWindow(); + // Not implemented + return false; +} + +bool LUSOSContext::isTouchActive() { + auto wnd = GameEngine::Instance->context->GetWindow(); + // Not implemented + return false; +} + +const char* LUSOSContext::getClipboardText() { + // Not implemented + return ""; +} + +void LUSOSContext::showCursor(bool show) { + auto wnd = GameEngine::Instance->context->GetWindow(); + wnd->SetCursorVisibility(show); +} + +bool LUSOSContext::isGamepadAvailable(int id) { + return true; +} + +bool LUSOSContext::isGamepadButtonPressed(int id, ControllerButton button) { + switch(button) { + case ControllerButton::LEFT_FACE_UP: + return gControllers[id].buttonPressed & BTN_DUP; + case ControllerButton::LEFT_FACE_RIGHT: + return gControllers[id].buttonPressed & BTN_DRIGHT; + case ControllerButton::LEFT_FACE_DOWN: + return gControllers[id].buttonPressed & BTN_DDOWN; + case ControllerButton::LEFT_FACE_LEFT: + return gControllers[id].buttonPressed & BTN_DLEFT; + case ControllerButton::RIGHT_FACE_UP: + return false; + case ControllerButton::RIGHT_FACE_RIGHT: + return false; + case ControllerButton::RIGHT_FACE_DOWN: + return gControllers[id].buttonPressed & BTN_A; + case ControllerButton::RIGHT_FACE_LEFT: + return gControllers[id].buttonPressed & BTN_B; + case ControllerButton::LEFT_TRIGGER_1: + return gControllers[id].buttonPressed & BTN_L; + case ControllerButton::RIGHT_TRIGGER_1: + return gControllers[id].buttonPressed & BTN_R; + case ControllerButton::LEFT_TRIGGER_2: + return gControllers[id].buttonPressed & BTN_Z; + case ControllerButton::RIGHT_TRIGGER_2: + return gControllers[id].buttonPressed & BTN_Z; + case ControllerButton::MIDDLE_LEFT: + case ControllerButton::MIDDLE: + return gControllers[id].buttonPressed & BTN_START; + case ControllerButton::MIDDLE_RIGHT: + // Not implemented + break; + } + + return false; +} + +uint16_t LUSOSContext::getButtons(int id) { + return gControllers[id].buttonDepressed; +} + +bool LUSOSContext::isBackButtonPressed(int id) { + bool pressed = gControllers[id].buttonDepressed & B_BUTTON; + if (pressed) { + return pressed; + } +#define SDL_SCANCODE_BACKSPACE 42 +#define VK_BACK 8 +#ifdef _WIN32 + if (isKeyboardButtonPressed(VK_BACK) || isMouseButtonPressed(2)) { // right click +#else + if (isKeyboardButtonPressed(SDL_SCANCODE_BACKSPACE) || isMouseButtonPressed(2)) { +#endif + return true; + } +} + +float LUSOSContext::getGamepadAxis(int id, ControllerAxis axis) { + switch(axis) { + case ControllerAxis::LEFT_X: + return gControllers[id].rawStickX / MAX_AXIS_VALUE; + case ControllerAxis::LEFT_Y: + return -gControllers[id].rawStickY / MAX_AXIS_VALUE; + case ControllerAxis::RIGHT_X: + return gControllers[id].rightRawStickX / MAX_AXIS_VALUE; + case ControllerAxis::RIGHT_Y: + return -gControllers[id].rightRawStickY / MAX_AXIS_VALUE; + case ControllerAxis::LEFT_TRIGGER: + return (gControllers[id].button & BTN_L) ? 1.0f : 0.0f; + case ControllerAxis::RIGHT_TRIGGER: + return (gControllers[id].button & BTN_R) ? 1.0f : 0.0f; + } + + return 0.0f; +} + +// Usage isKeyboardButtonPressed('A') for keyboard A button +bool LUSOSContext::isKeyboardButtonPressed(int virtualKey) { + return FreecamKeyDown(virtualKey); +} diff --git a/src/port/ui/hmui/os/LUSOSContext.h b/src/port/ui/hmui/os/LUSOSContext.h new file mode 100644 index 0000000000..47d40cbde7 --- /dev/null +++ b/src/port/ui/hmui/os/LUSOSContext.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "hmui/src/hmui/os/OSContext.h" + +class LUSOSContext : public OSContext { +public: + void init() override; + void update() override; + void dispose() override; + Coord getMouseDelta() override; + Coord getMousePosition() override; + void setMousePosition(Coord& pos) override; + Coord getMouseWheel() override; + bool isMouseButtonPressed(int button) override; + bool isMouseButtonReleased(int button) override; + bool isMouseButtonDown(int button) override; + void setMouseCursor(int cursor) override; + bool isTouchDevice() override; + bool isTouchActive() override; + void setClipboardText(const char* text) override; + const char* getClipboardText() override; + void showCursor(bool show) override; + bool isGamepadAvailable(int id) override; + bool isGamepadButtonPressed(int id, ControllerButton button) override; + float getGamepadAxis(int id, ControllerAxis axis) override; + bool isKeyboardButtonPressed(int virtualKey) override; + uint16_t getButtons(int id) override; + bool isBackButtonPressed(int id) override; + ~LUSOSContext() override = default; +private: + std::unordered_map m_LastMouseState; + std::unordered_map m_CurrentMouseState; +}; diff --git a/src/port/ui/hmui/provider/LUSImageProvider.cpp b/src/port/ui/hmui/provider/LUSImageProvider.cpp new file mode 100644 index 0000000000..0eff9e41f0 --- /dev/null +++ b/src/port/ui/hmui/provider/LUSImageProvider.cpp @@ -0,0 +1,38 @@ +#include "LUSImageProvider.h" + +#include "port/Engine.h" + +std::unordered_map textureCache; + +ImageHandle* D_ImageProvider::load() { + if(imagePath.find("__OTR__") != std::string::npos) { + imagePath = imagePath.substr(7); + } + auto it = textureCache.find(imagePath); + if (it != textureCache.end()) { + return &it->second; + } + + auto gui = GameEngine::Instance->context->GetWindow()->GetGui(); + if(!gui->HasTextureByName(imagePath)){ + throw std::runtime_error("Texture not found: " + imagePath); + } + + auto size = gui->GetTextureSize(imagePath); + auto tex = gui->GetTextureByName(imagePath); + + textureCache[imagePath] = { + (int) size.x, + (int) size.y, + (void*) tex + }; + + return &textureCache[imagePath]; +} + +void D_ImageProvider::dispose() { + auto it = textureCache.find(imagePath); + if (it != textureCache.end()) { + textureCache.erase(it); + } +} \ No newline at end of file diff --git a/src/port/ui/hmui/provider/LUSImageProvider.h b/src/port/ui/hmui/provider/LUSImageProvider.h new file mode 100644 index 0000000000..9e32f24601 --- /dev/null +++ b/src/port/ui/hmui/provider/LUSImageProvider.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include "hmui/graphics/GraphicsContext.h" + +class D_ImageProvider : public ImageProvider { +public: + explicit D_ImageProvider(const std::string& path) : imagePath(path) {} + + ImageHandle* load() override; + void dispose() override; + +private: + std::string imagePath; + ImageHandle* texture; +}; + +#define AssetImage(path) std::dynamic_pointer_cast(std::make_shared(path))