From 1007ac469ad50fe35b49b05d11d8a2f280daed21 Mon Sep 17 00:00:00 2001 From: EmsiaetKadosh Date: Fri, 17 Jan 2025 16:36:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=B5=8B=E8=AF=95=EF=BC=8CRe?= =?UTF-8?q?nderableString=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 2 +- CMakeLists.txt | 10 +- Chars.h | 71 +++++++++++++ Game.h | 14 +-- Renderer.cpp | 9 ++ Renderer.h | 5 +- Text.h | 265 +++++++++++++++++++++++++++++++++++++++++++++++++ Window.h | 48 +++++++++ def.h | 10 +- main.cpp | 15 +++ 10 files changed, 433 insertions(+), 16 deletions(-) create mode 100644 Chars.h create mode 100644 Text.h diff --git a/.clang-format b/.clang-format index 847c9ec..4887ded 100644 --- a/.clang-format +++ b/.clang-format @@ -121,7 +121,7 @@ CommentPragmas: true CompactNamespaces: false # BTW false, true 如果为true,连续的namespace声明会被放在同一行里,除非太长 # 构造函数参数列表和继承列表的缩进 ConstructorInitializerIndentWidth: 4 # 4 or 2,尽量与IndentWidth一致 -ContinuationIndentWidth: 4 # 4 or 2,尽量与IndentWidth一致 +ContinuationIndentWidth: 2 # 4 or 2,尽量与IndentWidth一致 Cpp11BracedListStyle: false # 如果为false,花括号里有东西的时候,会在左花括号右侧、右花括号左侧插入空格 # BTW false, true # 如果为true,会检查文件里最常出现的一种&和*的安排方式,然后统一; diff --git a/CMakeLists.txt b/CMakeLists.txt index 460bcd7..055a360 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,9 @@ add_definitions(-DCINTERFACE) add_definitions(-D__CARLBEKS_CMAKE_VSCODE__) set(CMAKE_CXX_STANDARD 23) set(CMAKE_WIN32_EXECUTABLE true) -#if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") -#add_compile_options(${PROJECT_NAME} -Wno-microsoft-string-literal-from-predefined) -#endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(${PROJECT_NAME} -Wno-microsoft-string-literal-from-predefined) +endif () #add_compile_options(${PROJECT_NAME} /utf-8) include(CTest) @@ -27,7 +27,9 @@ add_executable(${PROJECT_NAME} main.cpp Window.h Hud.cpp Hud.h - includes.h) + includes.h + Text.h + Chars.h) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/Chars.h b/Chars.h new file mode 100644 index 0000000..bbe6ae2 --- /dev/null +++ b/Chars.h @@ -0,0 +1,71 @@ +// +// Created by EmsiaetKadosh on 25-1-17. +// + +#pragma once + +#include "def.h" + +[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept { + unsigned int ret = 0; + while (*string != L'\0') { + if (ret & 0xf0000000) return 0xffffffff; + if (*string >= L'0' && *string <= L'9') { + ret <<= 4; + ret += *string - L'0'; + } else if (*string >= L'A' && *string <= L'F') { + ret <<= 4; + ret += *string - 0x41; // L'A' - 10 + } else if (*string >= L'a' && *string <= L'f') { + ret <<= 4; + ret += *string - 0x61; // L'a' - 10 + } else return 0xffffffff; + ++string; + } + return ret; +} + +[[nodiscard]] inline unsigned int wtouib16(const wchar* const string, const unsigned long long length) noexcept { + unsigned int ret = 0; + unsigned long long i = 0; + while (i < length) { + if (ret & 0xf0000000) return 0xffffffff; + if (string[i] >= L'0' && string[i] <= L'9') { + ret <<= 4; + ret += string[i] - L'0'; + } else if (string[i] >= L'A' && string[i] <= L'F') { + ret <<= 4; + ret += string[i] - 0x41; // L'A' - 10 + } else if (string[i] >= L'a' && string[i] <= L'f') { + ret <<= 4; + ret += string[i] - 0x61; // L'a' - 10 + } else return 0xffffffff; + ++i; + } + return ret; +} + +/** + * 将数字转换为字符串 + * @param value 要转换的数字 + * @param fills 填充位数。返回的字符串长度一定不小于该值 + * @return String类型 + */ +[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept { + static constexpr const wchar* const table = L"0123456789ABCDEF"; + String ret; + if (value < static_cast(1) << fills - 1) { + ret.assign(fills, L'0'); + for (unsigned int i = fills - 1; i != 0; --i) { + ret[i] = table[value & 0xf]; + value >>= 4; + } + } + else { + while (value) { + ret.push_back(table[value & 0xf]); + value >>= 4; + } + } + return ret; +} diff --git a/Game.h b/Game.h index 85e6790..b391793 100644 --- a/Game.h +++ b/Game.h @@ -5,8 +5,6 @@ #pragma once #include "Hud.h" -#include "InteractManager.h" -#include "Renderer.h" #include "Window.h" class Game { @@ -15,19 +13,23 @@ class Game { public: explicit Game() = default; - void render() const noexcept { if (window) window->render(); hud.render(); } - void setWindow(Window* window) noexcept { - if (this->window == window) return; + int setWindow(Window* window) noexcept { + if (this->window == window) Success(); if (this->window) this->window->onClose(); this->window = nullptr; - if (window && window->onOpen()) + if (window && window->onOpen()) { this->window = window; + Success(); + } + Failed(); } + + [[nodiscard]] Window* getWindow() const noexcept { return window; } }; inline static Game game = Game(); diff --git a/Renderer.cpp b/Renderer.cpp index 4a63515..31aa765 100644 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -4,4 +4,13 @@ #include "Renderer.h" +#include "Game.h" +#include "hbp.h" + void Renderer::initialize() noexcept { MainDC = GetDC(MainWindowHandle); } + +void Renderer::resize(const int width, const int height) noexcept(false) { + windowWidth = width; + windowHeight = height; + if (game.getWindow()) game.getWindow()->onResize(); +} diff --git a/Renderer.h b/Renderer.h index 799abd0..fc52c29 100644 --- a/Renderer.h +++ b/Renderer.h @@ -19,10 +19,7 @@ public: static void initialize() noexcept; explicit Renderer() = default; - void resize(const int width, const int height) noexcept(false) { - windowWidth = width; - windowHeight = height; - } + void resize(int width, int height) noexcept(false); [[nodiscard]] int getWidth() const noexcept { return windowWidth; } [[nodiscard]] int getHeight() const noexcept { return windowHeight; } diff --git a/Text.h b/Text.h new file mode 100644 index 0000000..af01739 --- /dev/null +++ b/Text.h @@ -0,0 +1,265 @@ +// +// Created by EmsiaetKadosh on 25-1-16. +// + +#pragma once +#include "Chars.h" +#include "def.h" + +class IdentifiedText {}; + +class RenderableString { + struct StringConfig { + String text; + /** + * 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。 + * 其余,以0xAARRGGBB的形式标记颜色。 + */ + unsigned int color = -1; + /** + * 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。 + * 其余,以0xAARRGGBB的形式标记颜色。 + */ + unsigned int background = -1; + wchar idFont = 0; + bool bold = false; + bool italic = false; + bool underline = false; + bool strikeThrough = false; + + explicit StringConfig() = default; + + void reset() noexcept { + idFont = 0; + color = -1; + background = -1; + bold = false; + italic = false; + underline = false; + strikeThrough = false; + } + + [[nodiscard]] StringConfig copyConfig() const noexcept { + StringConfig ret; + ret.idFont = idFont; + ret.color = color; + ret.background = background; + ret.bold = bold; + ret.italic = italic; + ret.underline = underline; + ret.strikeThrough = strikeThrough; + return ret; + } + + [[nodiscard]] String toString() const noexcept { + String ret; + ret.append(L"#"); + ret.append(uitowb16(color)); + ret.append(L"."); + ret.append(uitowb16(background)); + ret.append(L",F"); + ret.append(std::to_wstring(idFont)); + ret.append(L","); + if (bold) ret.append(L"="); + if (italic) ret.append(L"/"); + if (underline) ret.append(L"_"); + if (strikeThrough) ret.append(L"-"); + ret.append(L":"); + ret.append(text); + return ret; + } + }; + + List configs; + +public: + explicit RenderableString(const String& string): RenderableString(string.c_str(), string.length()) {} + explicit RenderableString(String&& string) : RenderableString(string.c_str(), string.length()) {} + + explicit RenderableString(const wchar* const string, const QWORD length = static_cast(-1)) { + if (length == -1) parseAppend(string); + else parseAppend(string, length); + } + + [[nodiscard]] String toString() const noexcept { + String ret; + for (const auto& config : configs) { + ret.append(config.toString()); + ret.append(L";\n"); + } + return ret; + } + +private: + void parseAppend(const wchar* string) noexcept { + StringConfig config; + if (!configs.empty()) { + config = configs.back(); + configs.pop_back(); + } + while (*string != L'\0') { + const wchar* start = string; + if (!config.text.empty()) { + configs.push_back(std::move(config)); + config = config.copyConfig(); + } + while (*string != L'\0' && *string != '\\') ++string; + if (*string == L'\0') { + if (*start != L'\0') config.text.append(start, string); + configs.push_back(std::move(config)); + return; + } + if (start != string) config.text.append(start, string); + if (*++string == L'\0') { + config.text.append(start, string); + configs.push_back(std::move(config)); + return; + } + if (!config.text.empty()) { + configs.push_back(std::move(config)); + config = config.copyConfig(); + } + switch (*string) { + case L'\\': + config.text.append(1, L'\\'); + ++string; + continue; + case L'#': { + unsigned long long i = 0; + while (i < 9) if (string[i++] == L'\0') goto end; + config.color = wtouib16(++string, 8); + string += 8; + continue; + } + case L'.': { + unsigned long long i = 0; + while (i < 9) if (string[i++] == L'\0') goto end; + config.background = wtouib16(++string, 8); + string += 8; + continue; + } + case L'F': + case L'f': + if (*++string == L'\0') goto end; + config.idFont = *string; + break; + case L'-': + case L's': + case L'S': + config.strikeThrough = true; + break; + case L'_': + case L'u': + case L'U': + config.underline = true; + break; + case L'/': + case L'i': + case L'I': + config.italic = true; + break; + case L'=': + case L'b': + case L'B': + config.bold = true; + break; + case L'r': + case L'R': + if (!config.text.empty()) configs.push_back(std::move(config)); + config = StringConfig(); + break; + default: + config.text.append(string - 1, 2); + break; + } + ++string; + } + end: + configs.push_back(std::move(config)); + } + + void parseAppend(const wchar* string, const QWORD length) noexcept { + StringConfig config = StringConfig(); + if (!configs.empty()) { + config = configs.back(); + configs.pop_back(); + } + const wchar* const end = string + length; + while (string < end) { + const wchar* start = string; + if (!config.text.empty()) { + configs.push_back(std::move(config)); + config = config.copyConfig(); + } + while (string < end && *string != '\\') ++string; + if (string >= end) { + if (start != end) config.text.append(start, end); + configs.push_back(std::move(config)); + return; + } + if (start != string) config.text.append(start, string); + if (++string == end) { + config.text.append(start, end); + configs.push_back(std::move(config)); + return; + } + if (!config.text.empty()) { + configs.push_back(std::move(config)); + config = config.copyConfig(); + } + switch (*string) { + case L'\\': + config.text.append(1, L'\\'); + ++string; + continue; + case L'#': { + if (end - string < 9) break; + config.color = wtouib16(++string, 8); + string += 8; + continue; + } + case L'.': { + if (end - string < 9) break; + config.background = wtouib16(++string, 8); + string += 8; + continue; + } + case L'F': + case L'f': + if (++string == end) break; + config.idFont = *string; + break; + case L'-': + case L's': + case L'S': + config.strikeThrough = true; + break; + case L'_': + case L'u': + case L'U': + config.underline = true; + break; + case L'/': + case L'i': + case L'I': + config.italic = true; + break; + case L'=': + case L'b': + case L'B': + config.bold = true; + break; + case L'r': + case L'R': + if (!config.text.empty()) configs.push_back(std::move(config)); + config = StringConfig(); + break; + default: + config.text.append(string - 1, 2); + break; + } + ++string; + } + configs.push_back(std::move(config)); + } +}; diff --git a/Window.h b/Window.h index 64926e2..9b9c68d 100644 --- a/Window.h +++ b/Window.h @@ -4,10 +4,45 @@ #pragma once +#include "def.h" #include "Renderer.h" +class Widget : public Renderable { +protected: + int left = 0, top = 0, width = 0, height = 0; + mutable bool hasMouse = false; + +public: + using Action = Function; + double x, y, w, h; + Action hover; + Action longHover; + Action mouseDown; + Action mouseUp; + Action mouseLeave; + explicit Widget(const double x, const double y, const double w, const double h) : x(x), y(y), w(w), h(h) {} + void render() const noexcept override {} + virtual void onResize() {} + + virtual bool isMouseIn(int x, int y) noexcept { + x -= left; + y -= top; + if (0 <= x and x <= width and 0 <= y and y <= height) hasMouse = true; + hasMouse = false; + return hasMouse; + } + + virtual void onHover() noexcept { if (hover) hover(); } + virtual void onLongHover() noexcept { if (longHover) longHover(); } + virtual void onMouseDown() noexcept { if (mouseDown) mouseDown(); } + virtual void onMouseUp() noexcept { if (mouseUp) mouseUp(); } + virtual void onMouseLeave() noexcept { if (mouseLeave) mouseLeave(); } +}; + class Window : public Renderable { protected: + List widgets; + Window() = default; public: @@ -24,4 +59,17 @@ public: * 注意,关闭未必就是删除。 */ virtual void onClose() = 0; + virtual void onResize(); +}; + +class FloatWindow final : public Window { +public: + void render() const noexcept override; + bool onOpen() override { return true; } + void onClose() override {} +}; + +class Button final : public Widget { +public: + explicit Button(const double x, const double y, const double w, const double h) : Widget(x, y, w, h) {} }; diff --git a/def.h b/def.h index 6f13fae..a693cb2 100644 --- a/def.h +++ b/def.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include #include #include @@ -12,6 +14,12 @@ using wchar = wchar_t; using QWORD = unsigned long long int; using String = std::wstring; template, typename Alloc = std::allocator>> using Map = std::map; +template> using List = std::list; +template using Function = std::function; + +#define Success() { return 0; } +#define Failed() { return 1; } +#define Error() { return -1; } #if false #ifdef _MSC_VER @@ -23,8 +31,8 @@ template, typename Alloc = s #endif #endif -#if false #define _WINSOCKAPI_ /* 防止winsock.h被引入。winsock.h和winsock2.h冲突。 */ +#if false #include #endif diff --git a/main.cpp b/main.cpp index dda43e7..5a81abc 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ #include "includes.h" +#include "Text.h" LRESULT __stdcall WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -158,6 +159,20 @@ int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCm ShowWindow(MainWindowHandle, nCmdShow); InteractManager::initialize(); Renderer::initialize(); + SetConsoleOutputCP(65001); + const String string = String(L"\\#12345678Hello\\/\\f\1Well\\.87654321你好\\r\\r\\u\\-"); + std::cout << "你好" << std::endl; + std::wcout.imbue(std::locale("zh_CN")); + std::wcout + << sizeof(L"你好") << " " << sizeof("Hu") << std::endl + << L"GetConsoleOutputCP => " << GetConsoleOutputCP() << std::endl +#if defined(UNICODE) + << L"UNICODE" << std::endl +#else + << L"Non-UNICODE" << std::endl +#endif + << string << std::endl + << RenderableString(string).toString() << std::endl; HACCEL hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCE(109)); MSG msg = { nullptr }; while (GetMessageW(&msg, nullptr, 0, 0)) {