diff --git a/include/td/render/renderer/HotkeysRenderer.h b/include/td/render/renderer/HotkeysRenderer.h new file mode 100644 index 0000000..32775dc --- /dev/null +++ b/include/td/render/renderer/HotkeysRenderer.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace td { +namespace render { + +/** + * \brief This is a debug class + */ +class HotkeysRenderer : public BasicRenderer { + public: + virtual void Render(float a_Lerp) override; + + HotkeysRenderer(); + ~HotkeysRenderer() {} +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/ImHotkey.h b/include/td/render/renderer/ImHotkey.h new file mode 100644 index 0000000..622e77e --- /dev/null +++ b/include/td/render/renderer/ImHotkey.h @@ -0,0 +1,357 @@ +// ImHotKey v1.0 +// https://github.com/CedricGuillemet/ImHotKey +// +// The MIT License(MIT) +// +// Copyright(c) 2019 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#pragma once + +#define SDL_h_ + +/* +How to use it? + +// Get some hotkeys composed of: +// - hotkey name +// - hotkey comment/lib +// - hotkey scancodes. Computed by the editor. Store that value in your app. + +static std::vector hotkeys = { { "Layout", "Reorder nodes in a simpler layout", 0xFFFF261D} + ,{"Save", "Save the current graph", 0xFFFF1F1D} + ,{"Load", "Load an existing graph file", 0xFFFF181D} + ,{"Play/Stop", "Play or stop the animation from the current graph", 0xFFFFFF3F} + ,{"SetKey", "Make a new animation key with the current parameters values at the current time", 0xFFFFFF1F} + }; + +// The editor is a modal window. bring it with something like that + if (ImGui::Button("Edit Hotkeys")) +{ + ImGui::OpenPopup("HotKeys Editor"); +} +ImHotKey::Edit(hotkeys.data(), hotkeys.size(), "HotKeys Editor"); + +// ImHotKey also provides a way to retrieve HotKey +int hotkey = ImHotKey::GetHotKey(hotkeys.data(), hotkeys.size()); +if (hotkey != -1) +{ + // handle the hotkey index! +} + +Awesome, you are done! +To help you integrate in your app, you can get a text (like "Ctrl + O") to integrate in your menu +static void GetHotKeyLib(unsigned int functionKeys, char* buffer, size_t bufferSize); + +*/ + +#include "imgui.h" +#include "imgui_internal.h" + +namespace ImHotKey +{ + struct HotKey + { + const char *functionName; + const char *functionLib; + unsigned int functionKeys; + }; + + struct Key + { + const char* lib = nullptr; + unsigned int order; + unsigned int scanCodePage1 = 0; // win32 scancode + unsigned int scanCodePage7 = 0; // HID (SDL,...) + float offset = 0; + float width = 40; + }; + + static const Key Keys[6][18] = { + { {"Esc", 4, 0x1, 0x29, 18}, {"F1", 5, 0x3B, 0x3A, 18}, {"F2", 6, 0x3C, 0x3B}, {"F3", 7, 0x3D, 0x3C}, {"F4", 8, 0x3E, 0x3D}, {"F5", 9, 0x3F, 0x3E, 24}, {"F6", 10, 0x40, 0x3F}, {"F7", 11, 0x41, 0x40}, {"F8", 12, 0x42, 0x41}, {"F9", 13, 0x43, 0x42, 24}, {"F10", 14, 0x44, 0x43}, {"F11", 15, 0x57, 0x44}, {"F12", 16, 0x58, 0x45}, {"PrSn", 17, 0x37, 0x46, 24}, {"ScLk", 18, 0x46}, {"Brk", 19, 126, 0x47} }, + { {"~", 20, 0x29, 0x35}, {"1", 21, 0x2, 0x1E}, {"2", 22, 0x3, 0x1F}, {"3", 23, 0x4, 0x20}, {"4", 24, 0x5, 0x21}, {"5", 25, 0x6, 0x22}, {"6", 26, 0x7, 0x23}, {"7", 27, 0x8, 0x24}, {"8", 28, 0x9, 0x25}, {"9", 29, 0xA, 0x26}, {"0", 30, 0xB, 0x27}, {"-", 31, 0xC, 0x2D}, {"+", 32, 0xD, 0x2E},{"Backspace", 33, 0xE, 0x2A, 0, 80}, {"Ins", 34, 0x52, 0x49, 24}, {"Hom", 35, 0x47, 0x4A}, {"PgU", 36, 0x49, 0x4B} }, + { {"Tab", 3, 0xF, 0x2B, 0, 60}, {"Q", 37, 0x10, 0x14}, {"W", 38, 0x11, 0x1A}, {"E", 39, 0x12, 0x08}, {"R", 40, 0x13, 0x15}, {"T", 41, 0x14, 0x17}, {"Y", 42, 0x15, 0x1C}, {"U", 43, 0x16, 0x18}, {"I", 44, 0x17, 0x0C}, {"O", 45, 0x18, 0x12}, {"P", 46, 0x19, 0x13}, {"[", 47, 0x1A, 0x2F}, {"]", 48, 0x1B, 0x30}, {"|", 49, 0x2B, 0x31, 0, 60}, {"Del", 50, 0x53, 0x4C, 24}, {"End", 51, 0x4F, 0x4D}, {"PgD", 52, 0x51, 0x4E} }, + { {"Caps Lock", 53, 0x3A, 0x39, 0, 80}, {"A", 54, 0x1E, 0x04}, {"S", 55, 0x1F, 0x16}, {"D", 56, 0x20, 0x07}, {"F", 57, 0x21, 0x09}, {"G", 58, 0x22, 0x0A}, {"H", 59, 0x23, 0x0B}, {"J", 60, 0x24, 0x0D}, {"K", 61, 0x25, 0x0E}, {"L", 62, 0x26, 0x0F}, {";", 63, 0x27, 0x33}, {"'", 64, 0x28, 0x34}, {"Ret", 65, 0x1C, 0X28, 0, 84} }, + { {"Shift", 2, 0x2A, 0xE1, 0, 104}, {"Z", 66, 0x2C, 0x1D}, {"X", 67, 0x2D, 0x1B}, {"C", 68, 0x2E, 0x06}, {"V", 69, 0x2F, 0x19}, {"B", 70, 0x30, 0x05}, {"N", 71, 0x31, 0x11}, {"M", 72, 0x32, 0x10}, {",", 73, 0x33, 0x36}, {".", 74, 0x34, 0x37}, {"/", 75, 0x35, 0x38}, {"Shift", 2, 0x2A, 0xE5, 0, 104}, {"Up", 76, 0x48, 0x52, 68} }, + { {"Ctrl", 0, 0x1D, 0xE0, 0, 60}, {"Alt", 1, 0x38, 0xE2, 68, 60}, {"Space", 77, 0x39, 0X2c, 0, 260}, {"Alt", 1, 0x38, 0xE6, 0, 60}, {"Ctrl", 0, 0x1D, 0xE4, 68, 60}, {"Left", 78, 0x4B, 0x50, 24}, {"Down", 79, 0x50, 0x51}, {"Right", 80, 0x4D, 0x52} } + }; + + static const Key& GetKeyForScanCode(unsigned int scancode) + { + for (unsigned int y = 0; y < 6; y++) + { + int x = 0; + while (Keys[y][x].lib) + { +#ifdef SDL_h_ + if (Keys[y][x].scanCodePage7 == scancode) +#elif WIN32 + if (Keys[y][x].scanCodePage1 == scancode) +#else +#error +#endif + return Keys[y][x]; + x++; + } + } + return Keys[0][0]; + } + + static unsigned int GetOrderedScanCodes(unsigned char scanCodes[4], unsigned char order[4]) + { + for (int pass = 0; pass < 2; pass++) + { + for (int o = 0; o < 3; o++) + { + if (order[o] > order[o + 1]) + { + ImSwap(order[o], order[o + 1]); ImSwap(scanCodes[o], scanCodes[o + 1]); + } + } + } + return (scanCodes[3] << 24) + (scanCodes[2] << 16) + (scanCodes[1] << 8) + scanCodes[0]; + } + + static void HotKeySPrintf(char* buffer, size_t bufferSize, const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + ImFormatStringV(buffer, bufferSize, fmt, args); + va_end(args); + } + + static void GetHotKeyLib(unsigned int functionKeys, char* buffer, size_t bufferSize, const char *functionLib = nullptr) + { + static const char* str[4] = { "%s", "%s + %s", "%s + %s +%s", "%s + %s + %s + %s" }; + static const char* strLib[4] = { "%s (%s)", "%s (%s + %s)", "%s (%s + %s +%s)", "%s (%s + %s + %s + %s)" }; + static const char* lib[4]; + int scanCodeCount = 0; + for (int i = 0; i < 4; i++) + { + unsigned char scanCode = (unsigned char)(functionKeys >> i * 8); + if (scanCode == 0xFF) + { + continue; + } + lib[scanCodeCount] = GetKeyForScanCode(scanCode).lib; + scanCodeCount++; + } + if (!scanCodeCount) + { + buffer[0] = 0; + return; + } + + if (functionLib) + { + const char* fmt = strLib[scanCodeCount - 1]; + HotKeySPrintf(buffer, bufferSize, fmt, functionLib, lib[0], lib[1], lib[2], lib[3]); + } + else + { + const char* fmt = str[scanCodeCount - 1]; + HotKeySPrintf(buffer, bufferSize, fmt, lib[0], lib[1], lib[2], lib[3]); + } + } + + static void Edit(HotKey *hotkey, size_t hotkeyCount, const char *popupModal) + { + static int editingHotkey = -1; + if (!hotkeyCount) + return; + static bool keyDown[512] = {}; + + ImGui::SetNextWindowSize(ImVec2(1060, 400)); + if (!ImGui::BeginPopupModal(popupModal, NULL, ImGuiWindowFlags_NoResize)) + return; + + ImGui::BeginChildFrame(127, ImVec2(220, -1)); + for(size_t i = 0;i< hotkeyCount;i++) + { + char hotKeyLib[128]; + GetHotKeyLib(hotkey[i].functionKeys, hotKeyLib, sizeof(hotKeyLib), hotkey[i].functionName); + if (ImGui::Selectable(hotKeyLib, editingHotkey == int(i)) || editingHotkey == -1) + { + editingHotkey = int(i); + memset(keyDown, 0, sizeof(keyDown)); + for (int j = 0; j < 4; j++) + { + int scan = (hotkey[editingHotkey].functionKeys >> (8 * j)) & 0xFF; + if (scan != 0xFF) + { + keyDown[scan] = true; + } + } + } + } + ImGui::EndChildFrame(); + ImGui::SameLine(); + ImGui::BeginGroup(); + + for (int i = ImGuiKey_NamedKey_BEGIN; i < ImGuiKey_NamedKey_END; i++) + { + if (ImGui::IsKeyPressed(ImGuiKey(i), false)) + { + int imKey; +#ifdef SDL_h_ + imKey = i; +#elif WIN32 + imKey = MapVirtualKeyA(i, MAPVK_VK_TO_VSC); +#else + imKey = i; +#endif + keyDown[imKey - ImGuiKey_NamedKey_BEGIN] = !keyDown[imKey - ImGuiKey_NamedKey_BEGIN]; + } + } + for (unsigned int y = 0; y < 6; y++) + { + int x = 0; + ImGui::BeginGroup(); + while (Keys[y][x].lib) + { + const Key& key = Keys[y][x]; + const float ofs = key.offset + (x?4.f:0.f); + + const float width = key.width; + if (x) + { + ImGui::SameLine(0.f, ofs); + } + else + { + if (ofs >= 1.f) + { + ImGui::Indent(ofs); + } + } +#ifdef SDL_h_ + bool& butSwtch = keyDown[key.scanCodePage7]; +#elif WIN32 + bool& butSwtch = keyDown[key.scanCodePage1]; +#else +#error +#endif + ImGui::PushStyleColor(ImGuiCol_Button, butSwtch ? 0xFF1040FF : 0x80000000); + if (ImGui::Button(Keys[y][x].lib, ImVec2(width, 40))) + { + butSwtch = !butSwtch; + } + ImGui::PopStyleColor(); + x++; + } + ImGui::EndGroup(); + } + ImGui::InvisibleButton("space", ImVec2(10, 55)); + ImGui::BeginChildFrame(18, ImVec2(540, 40)); + ImGui::Text("%s :", hotkey[editingHotkey].functionName); + ImGui::SameLine(); + ImGui::TextWrapped("%s", hotkey[editingHotkey].functionLib); + ImGui::EndChildFrame(); + ImGui::SameLine(); + int keyDownCount = 0; + for (auto d : keyDown) + { + keyDownCount += d ? 1 : 0; + } + if (ImGui::Button("Clear", ImVec2(80, 40))) + { + memset(keyDown, 0, sizeof(keyDown)); + } + ImGui::SameLine(); + if (keyDownCount && keyDownCount < 5) + { + if (ImGui::Button("Set", ImVec2(80, 40))) + { + unsigned char scanCodes[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + unsigned char order[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int scanCodeCount = 0; + hotkey[editingHotkey].functionKeys = 0; + for (int i = 1; i < sizeof(keyDown); i++) + { + if (keyDown[i]) + { + scanCodes[scanCodeCount] = (unsigned char)i; + order[scanCodeCount] = (unsigned char)GetKeyForScanCode(i).order; + scanCodeCount++; + } + } + + hotkey[editingHotkey].functionKeys = GetOrderedScanCodes(scanCodes, order); + } + ImGui::SameLine(0.f, 20.f); + } + else + { + ImGui::SameLine(0.f, 100.f); + } + + if (ImGui::Button("Done", ImVec2(80, 40))) { ImGui::CloseCurrentPopup(); } + ImGui::EndGroup(); + ImGui::EndPopup(); + } + + static int GetHotKey(HotKey *hotkey, size_t hotkeyCount) + { + static unsigned int lastHotKey = 0xFFFFFFFF; + unsigned char scanCodes[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + unsigned char order[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int scanCodeCount = 0; + for (int i = ImGuiKey_Aliases_BEGIN; i < ImGuiKey_Aliases_END; i++) + { + if (ImGui::IsKeyDown(ImGuiKey(i))) + { + int imKey; +#ifdef SDL_h_ + imKey = i - ImGuiKey_NamedKey_BEGIN; +#elif WIN32 + imKey = MapVirtualKeyA(i, MAPVK_VK_TO_VSC); +#else + imKey = i; +#endif + scanCodes[scanCodeCount] = (unsigned char)imKey; + order[scanCodeCount] = (unsigned char)GetKeyForScanCode(imKey).order; + scanCodeCount++; + if (scanCodeCount == 4) + break; + } + } + + unsigned int newHotKey = GetOrderedScanCodes(scanCodes, order); + + if (scanCodeCount) + { + if (newHotKey != lastHotKey) + { + for (size_t i = 0; i < hotkeyCount; i++) + { + if (hotkey[i].functionKeys == newHotKey) + { + lastHotKey = newHotKey; + return int(i); + } + } + lastHotKey = 0xFFFFFFFF; + } + return -1; + } + lastHotKey = 0xFFFFFFFF; + return -1; + } +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4bd56a2..62d0374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,12 @@ #include -#include +#include #include int main(int argc, char** argv) { // init GL context td::Display display(1920, 1080, "Tower-Defense 2"); - display.ChangeState(); + display.ChangeState(); td::Timer timer; while (!display.IsCloseRequested()) { diff --git a/src/td/display/state/DebugWorldState.cpp b/src/td/display/state/DebugWorldState.cpp index 7baab80..d3ce354 100644 --- a/src/td/display/state/DebugWorldState.cpp +++ b/src/td/display/state/DebugWorldState.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,8 @@ #include #include +#include + namespace td { DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) { @@ -41,6 +44,7 @@ DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) { m_Renderer.AddRenderer(m_Camera, clientWorld); m_Renderer.AddRenderer(m_Camera, clientWorld); m_Renderer.AddRenderer(m_Camera, clientWorld); + m_Renderer.AddRenderer(); auto& list = m_Renderer.AddRenderer(m_Client->GetPlayers()); @@ -52,7 +56,7 @@ DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) { }); list.OnPlayerKick.Connect([this](PlayerID a_Player) { - auto it = std::find_if(m_FakeClients.begin(), m_FakeClients.end(), [a_Player](auto& clientPtr){ + auto it = std::find_if(m_FakeClients.begin(), m_FakeClients.end(), [a_Player](auto& clientPtr) { if (!clientPtr->GetId().has_value()) return false; return clientPtr->GetId().value() == a_Player; diff --git a/src/td/render/renderer/HotkeysRenderer.cpp b/src/td/render/renderer/HotkeysRenderer.cpp new file mode 100644 index 0000000..873b607 --- /dev/null +++ b/src/td/render/renderer/HotkeysRenderer.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include +#include + +#include + +namespace td { +namespace render { + +void HotkeysRenderer::Render(float a_Lerp) { + ImGui::Begin("Hotkeys"); + static std::vector hotkeys = {{"Layout", "Reorder nodes in a simpler layout", 0xFFFF261D}, + {"Save", "Save the current graph", 0xFFFF1F1D}, {"Load", "Load an existing graph file", 0xFFFF181D}, + {"Play/Stop", "Play or stop the animation from the current graph", 0xFFFFFF3F}, + {"SetKey", "Make a new animation key with the current parameters values at the current time", 0xFFFFFF1F}}; + + // The editor is a modal window. bring it with something like that + if (ImGui::Button("Edit Hotkeys")) { + ImGui::OpenPopup("HotKeys Editor"); + } + ImHotKey::Edit(hotkeys.data(), hotkeys.size(), "HotKeys Editor"); + + // ImHotKey also provides a way to retrieve HotKey + int hotkey = ImHotKey::GetHotKey(hotkeys.data(), hotkeys.size()); + if (hotkey != -1) { + // handle the hotkey index! + } + ImGui::End(); +} + +HotkeysRenderer::HotkeysRenderer() {} + +} // namespace render +} // namespace td