diff --git a/CMakeLists.txt b/CMakeLists.txt index b9dd69f..5042649 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,15 @@ add_executable(${PROJECT_NAME} main.cpp Hud.cpp Hud.h includes.h - Text.h + IText.h Chars.h - Text.cpp) + IText.cpp + xWindows.h + exception.h + def.cpp + TestCode.h + TextureManager.cpp + TextureManager.h) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/Game.cpp b/Game.cpp index d9b697c..5351045 100644 --- a/Game.cpp +++ b/Game.cpp @@ -3,3 +3,6 @@ // #include "Game.h" + +void Game::initialize() noexcept { renderer.setGame(game); } + diff --git a/Game.h b/Game.h index b391793..8a9597c 100644 --- a/Game.h +++ b/Game.h @@ -7,13 +7,14 @@ #include "Hud.h" #include "Window.h" -class Game { +class Game : public IRenderable { Hud hud = Hud(); Window* window = nullptr; public: explicit Game() = default; - void render() const noexcept { + static void initialize() noexcept; + void render() const noexcept override { if (window) window->render(); hud.render(); } diff --git a/Text.cpp b/IText.cpp similarity index 52% rename from Text.cpp rename to IText.cpp index 4f8c016..c5a3bae 100644 --- a/Text.cpp +++ b/IText.cpp @@ -2,16 +2,25 @@ // Created by EmsiaetKadosh on 25-1-18. // -#include "Text.h" +#include "IText.h" #include "def.h" const String& TranslatableText::getText() const noexcept { + refreshText(); + return target->getText(); +} + +const RenderableString& TranslatableText::getRenderableString() const noexcept { + refreshText(); + return target->getRenderableString(); +} + +void TranslatableText::refreshText() const noexcept { if (target == nullptr || langConfig == translator.getConfigVersion()) { target = translator.getText(idSrc); langConfig = translator.getConfigVersion(); } - return *target; } diff --git a/Text.h b/IText.h similarity index 85% rename from Text.h rename to IText.h index a8a05d3..e2bac29 100644 --- a/Text.h +++ b/IText.h @@ -6,7 +6,8 @@ #include "Chars.h" #include "def.h" -class IdentifiedText {}; +class LiteralText; +class Translator; /** * 注意一些默认值。 @@ -14,6 +15,8 @@ class IdentifiedText {}; * color和background=0xffffffff,表示继承使用默认颜色; */ class RenderableString { + friend class LiteralText; + struct StringConfig { String text; /** @@ -111,6 +114,46 @@ public: return ret; } + RenderableString& append(const RenderableString& other) { + auto iterator = other.configs.cbegin(); + int flags = 0; + bool clr = true, bg = true, font = true; + unsigned int color = configs.back().color; + unsigned int background = configs.back().background; + wchar idFont = configs.back().idFont; + while (iterator != other.configs.cend()) { + if (flags >= 3) break; + StringConfig config = iterator->copy(); + if (font) { + if (config.idFont == idFont || config.idFont != 0) { + font = false; + ++flags; + } else config.idFont = idFont; + } + if (clr) { + if (config.color == color || config.color != 0xffffffff) { + clr = false; + ++flags; + } else config.color = color; + } + if (bg) { + if (config.background == background || config.background != 0xffffffff) { + bg = false; + ++flags; + } else config.background = background; + } + configs.push_back(std::move(config)); + ++iterator; + } + while (iterator != other.configs.cend()) configs.push_back(iterator->copy()); + return *this; + } + + RenderableString& append(const String& other) { + parseAppend(other.c_str(), other.length()); + return *this; + } + private: void parseAppend(const wchar* string) noexcept { StringConfig config; @@ -283,88 +326,62 @@ private: } configs.push_back(std::move(config)); } - - RenderableString& append(const RenderableString& other) { - auto iterator = other.configs.cbegin(); - int flags = 0; - bool clr = true, bg = true, font = true; - unsigned int color = configs.back().color; - unsigned int background = configs.back().background; - wchar idFont = configs.back().idFont; - while (iterator != other.configs.cend()) { - if (flags >= 3) break; - StringConfig config = iterator->copy(); - if (font) { - if (config.idFont == idFont || config.idFont != 0) { - font = false; - ++flags; - } else config.idFont = idFont; - } - if (clr) { - if (config.color == color || config.color != 0xffffffff) { - clr = false; - ++flags; - } else config.color = color; - } - if (bg) { - if (config.background == background || config.background != 0xffffffff) { - bg = false; - ++flags; - } else config.background = background; - } - configs.push_back(std::move(config)); - ++iterator; - } - while (iterator != other.configs.cend()) configs.push_back(iterator->copy()); - return *this; - } - - RenderableString& append(const String& other) { - parseAppend(other.c_str(), other.length()); - return *this; - } }; -interface Text { - virtual ~Text() = default; +interface IText { + virtual ~IText() = default; virtual const String& getText() const noexcept = 0; + virtual const RenderableString& getRenderableString() const noexcept = 0; }; -class Translator; +typedef class LiteralText : public IText { + const String string; + mutable RenderableString renderableString; -class TranslatableText final : public Text { +public: + LiteralText(const String& string): string(string), renderableString(string) {} + LiteralText(String&& string): string(std::move(string)), renderableString(string) {} + + const String& getText() const noexcept override { return string; } + + const RenderableString& getRenderableString() const noexcept override { return renderableString; } +} TranslatedText; + +class TranslatableText final : public IText { const String idSrc; - mutable const String* target = nullptr; + mutable const LiteralText* target = nullptr; mutable QWORD langConfig = 0; public: - explicit TranslatableText(const String& id) : idSrc(id) {} - explicit TranslatableText(String&& id) : idSrc(std::move(id)) {} + explicit TranslatableText(const String& id) : idSrc(id) { std::wcout << L"Lvalue init\n"; } + explicit TranslatableText(String&& id) : idSrc(std::move(id)) { std::wcout << L"Rvalue init\n"; } const String& getText() const noexcept override; + const RenderableString& getRenderableString() const noexcept override; + void refreshText() const noexcept; }; struct Language { - Map translateTable; + Map translateTable; int id = 1; }; class Translator { Map langMap{}; List langList{}; + TranslatedText nullText{ L"\\#FF""EE0000" }; String lang = L"zh-cn"; - String nullText = L""; - QWORD langConfig = 1; + int langConfig = 1; int idLangMax = 1; - using IterID = Map::const_iterator; + using IterID = Map::const_iterator; using IterLang = List::const_iterator; public: explicit Translator() { langMap.insert(std::make_pair(String(L"zh-cn"), 1)); } - void addLang(const String& lang) noexcept(this->langMap.insert(std::make_pair(lang, this->idLangMax))) { langMap.insert(std::make_pair(lang, ++idLangMax)); } - void addLang(String&& lang) noexcept(this->langMap.insert(std::make_pair(lang, this->idLangMax))) { langMap.insert(std::make_pair(lang, ++idLangMax)); } + void addLang(const String& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); } + void addLang(String&& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); } void loadLang(); - const String* getText(const String& id) const noexcept { + [[nodiscard]] const TranslatedText* getText(const String& id) const noexcept { for (const auto& [translateTable, _] : langList) { IterID iterator = translateTable.find(id); if (iterator != translateTable.cend()) return &iterator->second; @@ -372,7 +389,7 @@ public: return &nullText; } - int getConfigVersion() const noexcept { return langConfig; } + [[nodiscard]] int getConfigVersion() const noexcept { return langConfig; } }; inline static Translator translator = Translator(); diff --git a/Renderer.cpp b/Renderer.cpp index 31aa765..dd2f996 100644 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -12,5 +12,10 @@ void Renderer::initialize() noexcept { MainDC = GetDC(MainWindowHandle); } void Renderer::resize(const int width, const int height) noexcept(false) { windowWidth = width; windowHeight = height; + if (resizeTime > 0) return; // 防抖、防止频繁触发 + if (resizeTime < 0) { + resizeTime = 20; + return; + } if (game.getWindow()) game.getWindow()->onResize(); } diff --git a/Renderer.h b/Renderer.h index d1c7366..04f3054 100644 --- a/Renderer.h +++ b/Renderer.h @@ -6,28 +6,47 @@ #include "def.h" +class Game; /** * 用于标记相对位置。 */ -enum class Location { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM, RIGHT_TOP, RIGHT, RIGHT_BOTTOM }; +enum class Location : char { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM, RIGHT_TOP, RIGHT, RIGHT_BOTTOM }; interface IRenderable { virtual ~IRenderable() = default; virtual void render() const noexcept = 0; }; -class Renderer { +interface ITickable { + virtual ~ITickable() = default; + virtual void tick() noexcept = 0; +}; + +class Renderer : public ITickable { + /** + * 后续可能用不到,可能可删 + */ + Game* pGame = nullptr; inline static HDC MainDC; int windowWidth = 0, windowHeight = 0; + int resizeTime = 0; public: static void initialize() noexcept; explicit Renderer() = default; - void resize(int width, int height) noexcept(false); - + void setGame(Game& game) noexcept { pGame = &game; } [[nodiscard]] int getWidth() const noexcept { return windowWidth; } [[nodiscard]] int getHeight() const noexcept { return windowHeight; } + + void tick() noexcept override { + // 联合处理resize防抖 + if (resizeTime > 0) --resizeTime; + else if (resizeTime == 0) { + resize(windowWidth, windowHeight); + resizeTime = -1; + } + } }; inline static Renderer renderer = Renderer(); diff --git a/TestCode.h b/TestCode.h new file mode 100644 index 0000000..0009536 --- /dev/null +++ b/TestCode.h @@ -0,0 +1,24 @@ +// +// Created by EmsiaetKadosh on 25-1-20. +// + +#pragma once + +struct B { + int a = 1; + B() { std::wcout << L"common constructor\n"; } + B(B&) { std::wcout << L"copy constructor\n"; } + // B(B&& other) noexcept { std::wcout << L"move constructor\n"; } +}; + +struct A { + B b; + + explicit A(B&& other) : b(other) {} +}; + +inline void test() { + B b; + A a{ B() }; + a = A(std::move(a.b)); +} diff --git a/TextureManager.cpp b/TextureManager.cpp new file mode 100644 index 0000000..108b7a5 --- /dev/null +++ b/TextureManager.cpp @@ -0,0 +1,5 @@ +// +// Created by EmsiaetKadosh on 25-1-21. +// + +#include "TextureManager.h" diff --git a/TextureManager.h b/TextureManager.h new file mode 100644 index 0000000..8ab76ba --- /dev/null +++ b/TextureManager.h @@ -0,0 +1,81 @@ +// +// Created by EmsiaetKadosh on 25-1-21. +// + +#pragma once + +#include "def.h" +#include "exception.h" + +class ITexture {}; + +class Font { + String name; + HFONT fonts[16]{}; + + static int idOf(const bool italic, const bool bold, const bool underline, const bool strike) { + int ret = 0; + if (italic) ret |= 8; + if (bold) ret |= 4; + if (underline) ret |= 2; + if (strike) ret |= 1; + return ret; + } + +public: + explicit Font(const String& name) : name(name) {} + + void load(const int size) noexcept(false) { + LOGFONTW font{ + .lfHeight = size, + .lfWidth = 0, + .lfEscapement = 0, + .lfOrientation = 0, + .lfCharSet = ANSI_CHARSET, + .lfOutPrecision = OUT_DEFAULT_PRECIS, + .lfClipPrecision = CLIP_DEFAULT_PRECIS, + .lfQuality = DEFAULT_QUALITY, + .lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE, + }; + if (name.size() < LF_FACESIZE) memcpy(font.lfFaceName, name.c_str(), name.size()); + else throw ArrayIndexOutOfBoundException(L"Font name is too long: " + name); + for (int i = 0; i < 16; i++) { + font.lfWeight = i & 4 ? 700 : 400; + font.lfItalic = i & 8; + font.lfUnderline = i & 2; + font.lfStrikeOut = i & 1; + fonts[i] = CreateFontIndirectW(&font); + } + } + + void unload() noexcept(false) { + for (int i = 0; i < 16; ++i) { + DeleteObject(fonts[i]); + fonts[i] = nullptr; + } + } +}; + +class TextureManager { + Map fonts; + Map> textures; + Font* defaultFont = nullptr; + using IterFont = Map::const_iterator; + using IterTexture = Map>::const_iterator; +public: + TextureManager() { + try { + fonts.insert(std::make_pair(0, Font(L"Arial"))); + defaultFont = &fonts.at(0); + } + catch (...) { + + } + } + Font& getFont(const int id) const noexcept { + IterFont iterator = fonts.find(id); + if (iterator == fonts.cend()) return + } +}; + +inline static TextureManager textureManager = TextureManager(); diff --git a/Window.h b/Window.h index cfd1c84..367eea6 100644 --- a/Window.h +++ b/Window.h @@ -6,6 +6,7 @@ #include "def.h" #include "Renderer.h" +#include "IText.h" enum class MouseActionCode : char { MAC_MOVE, MAC_HOVER, MAC_DOWN, MAC_UP, MAC_DOUBLE @@ -17,6 +18,7 @@ protected: mutable bool hasMouse = false; public: + Location location; using Action = Function; double x, y, w, h; Action hover;// 传入int忽略 @@ -25,7 +27,7 @@ public: Action mouseUp;// 传入int表示变更按键。0左, 1中, 2右 Action mouseLeave;// 传入int忽略 Action mouseClick;// 传入int表示变更按键。0x0左, 0x1中, 0x2右;0xf表示是否双击 - explicit Widget(const double x, const double y, const double w, const double h) : x(x), y(y), w(w), h(h) {} + explicit Widget(const double x, const double y, const double w, const double h, Location location) : x(x), y(y), w(w), h(h), location(location) {} void render() const noexcept override {} virtual void onResize() {} @@ -48,23 +50,24 @@ public: hasMouse = false; } hasMouse = true; - switch (action & 0xf) { - case MouseActionCode::MAC_HOVER: + switch (static_cast(action & 0xf)) { + case static_cast(MouseActionCode::MAC_HOVER): onLongHover(0); break; - case MouseActionCode::MAC_MOVE: + case static_cast(MouseActionCode::MAC_MOVE): onHover(0); break; - case MouseActionCode::MAC_DOWN: + case static_cast(MouseActionCode::MAC_DOWN): onMouseDown(value); break; - case MouseActionCode::MAC_UP: + case static_cast(MouseActionCode::MAC_UP): onMouseUp(value); break; - case MouseActionCode::MAC_DOUBLE: + case static_cast(MouseActionCode::MAC_DOUBLE): onMouseClick(1); break; default: + break; } } }; @@ -75,6 +78,8 @@ protected: Window() = default; + ~Window() override { for (Widget*& widget : widgets) { delete widget; } } + public: /** * 在Game.setWindow()时,本窗口开启时调用。 @@ -101,5 +106,6 @@ public: 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) {} + ObjectHolder name; + explicit Button(const double x, const double y, const double w, const double h, Location location, ObjectHolder text) : Widget(x, y, w, h, location), name(text) {} }; diff --git a/def.cpp b/def.cpp new file mode 100644 index 0000000..76bc6aa --- /dev/null +++ b/def.cpp @@ -0,0 +1,55 @@ +// +// Created by EmsiaetKadosh on 25-1-18. +// + +#include "def.h" + +#include "exception.h" + +template Base& ObjectHolder::operator->() noexcept(false) { + if (value) return *value; + throw NullPointerException(L"value is null"); +} + +template const Base& ObjectHolder::operator->() const noexcept(false) { + if (value) return *value; + throw NullPointerException(L"value is null"); +} + +template Base& ObjectHolder::operator*() noexcept(false) { + if (value) return *value; + throw NullPointerException(L"value is null"); +} + +template const Base& ObjectHolder::operator*() const noexcept(false) { + if (value) return *value; + throw NullPointerException(L"value is null"); +} + +template template requires std::is_base_of_v && TypeName +void ObjectHolder::set(const T& value) { + if (!value) this->value = new T(value); + else { + if (hasValue) delete this->value; + this->value = new T(value); + } +} + +template template requires std::is_base_of_v && TypeName +void ObjectHolder::set(T&& value) { + if (!value) this->value = new T(value); + else { + if (hasValue) delete this->value; + this->value = new T(value); + } +} + +template const T& Reference::getLvalue() const noexcept(false) { + if (type == ReferenceValueType::LVALUE) return *lvalue; + throw NullPointerException(L"reference is rvalue"); +} + +template T&& Reference::getRvalue() noexcept(false) { + if (type == ReferenceValueType::RVALUE) return std::move(*rvalue); + throw NullPointerException(L"reference is lvalue"); +} diff --git a/def.h b/def.h index a693cb2..2501274 100644 --- a/def.h +++ b/def.h @@ -9,6 +9,7 @@ #include #include #include +#include using wchar = wchar_t; using QWORD = unsigned long long int; @@ -51,3 +52,99 @@ template using Function = std::function; #pragma comment(lib, "dwmapi.lib") #pragma comment(lib, "Uxtheme.lib") #pragma comment(lib, "winmm.lib") + +template +concept Copyable = requires(const T& t) { T(t); }; +template +concept NewCopyable = requires(const T& t) { delete new T(t); }; +template +concept Moveable = requires(T&& t) { T(std::move(t)); }; +template +concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); }; +template +concept NonreferenceType = !std::is_reference_v; +template +concept NonpointerType = !std::is_pointer_v; +template +concept ReferenceType = std::is_reference_v; +template +concept PointerType = std::is_pointer_v; +template +concept TypeName = NonreferenceType && NonpointerType; + +template class ObjectHolder { + Base* value; + bool hasValue; + char padding[7]{}; + +public: + /** + * 用于延迟初始化。 + */ + ObjectHolder() : value(nullptr), hasValue(false) {} + + ObjectHolder(Base* const value) : value(value), hasValue(false) {} + + template requires std::is_base_of_v && TypeName + ObjectHolder(const T& value) : value(new T(value)), hasValue(true) {} + + template requires std::is_base_of_v && TypeName + ObjectHolder(T&& value) : value(new T(std::forward(value))), hasValue(true) {} + + ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {} + + ObjectHolder(ObjectHolder&& other) noexcept: value(other.value), hasValue(other.hasValue) { + other.value = nullptr; + other.hasValue = false; + } + + template requires std::is_base_of_v && TypeName + void set(const T& value); + + template requires std::is_base_of_v && TypeName + void set(T&& value); + + + ~ObjectHolder() { + if (hasValue) delete value; + value = nullptr; + } + + Base& operator->() noexcept(false); + const Base& operator->() const noexcept(false); + Base& operator*() noexcept(false); + const Base& operator*() const noexcept(false); + operator bool() const noexcept { return value; } +}; + +enum class ReferenceValueType { + LVALUE, + RVALUE +}; + +template class Reference { + const T* lvalue = nullptr; + T* rvalue = nullptr; + +public: + const ReferenceValueType type; + Reference(const T& lvalue) : lvalue(&lvalue), type(ReferenceValueType::LVALUE) {} + Reference(T&& rvalue) : rvalue(&rvalue), type(ReferenceValueType::RVALUE) {} + Reference& operator=(const Reference&) = delete; + Reference& operator=(Reference&&) = delete; + + Reference& operator=(const T& lvalue) { + this->lvalue = &lvalue; + this->type = ReferenceValueType::LVALUE; + return *this; + } + + Reference& operator=(T&& rvalue) { + this->rvalue = &rvalue; + this->type = ReferenceValueType::RVALUE; + return *this; + } + + const T& getLvalue() const noexcept(false); + T&& getRvalue() noexcept(false); +}; diff --git a/exception.h b/exception.h new file mode 100644 index 0000000..2653726 --- /dev/null +++ b/exception.h @@ -0,0 +1,36 @@ +// +// Created by EmsiaetKadosh on 25-1-18. +// + +#pragma once + +class Exception : public std::exception { + const String* type; + +protected: + String msg; + + Exception(String&& msg, const String* type) : type(type), msg(std::move(msg)) {} + Exception(const String& msg, const String* type) : type(type), msg(msg) {} + +public: + [[nodiscard]] String getMessage() const noexcept { return msg; } + [[nodiscard]] const String* getType() const noexcept { return type; } + [[nodiscard]] const char* what() const override { return "Use Exception::getMessage() instead.";} +}; + +class NullPointerException final : public Exception { + inline static const String type = L"NullPointerException"; + +public: + explicit NullPointerException(String&& msg) : Exception(std::move(msg), &type) {} + explicit NullPointerException(const String& msg) : Exception(msg, &type) {} +}; + +class ArrayIndexOutOfBoundException final : public Exception { + inline static const String type = L"ArrayIndexOutOfBoundException"; + +public: + explicit ArrayIndexOutOfBoundException(String&& msg) : Exception(std::move(msg), &type) {} + explicit ArrayIndexOutOfBoundException(const String& msg) : Exception(msg, &type) {} +}; diff --git a/includes.h b/includes.h index 39ae2b3..0f86135 100644 --- a/includes.h +++ b/includes.h @@ -5,11 +5,15 @@ #pragma once #include "def.h" +#include "exception.h" #include "hbp.h" #include "Renderer.h" #include "InteractManager.h" +#include "TextureManager.h" #include "Hud.h" #include "Window.h" #include "Game.h" + +#include "xWindows.h" diff --git a/main.cpp b/main.cpp index 5a81abc..4d16224 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include "includes.h" -#include "Text.h" +#include "IText.h" +#include "TestCode.h" LRESULT __stdcall WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -159,20 +160,19 @@ int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCm ShowWindow(MainWindowHandle, nCmdShow); InteractManager::initialize(); Renderer::initialize(); + Game::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; + { + 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 + << string << std::endl + << RenderableString(string).toString() << std::endl; + test(); + } HACCEL hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCE(109)); MSG msg = { nullptr }; while (GetMessageW(&msg, nullptr, 0, 0)) { diff --git a/xWindows.h b/xWindows.h new file mode 100644 index 0000000..5fcab88 --- /dev/null +++ b/xWindows.h @@ -0,0 +1,15 @@ +// +// Created by EmsiaetKadosh on 25-1-18. +// + +#pragma once + +#include "Window.h" + +class StartWindow : public Window { +public: + StartWindow() { + Button* button = new Button(0, 0, 0.4, 0.08, Location::CENTER, TranslatableText(L"123")); + widgets.push_back(button); + } +};