This commit is contained in:
EmsiaetKadosh
2025-01-21 15:41:32 +08:00
parent b86053c3a7
commit edcc095979
17 changed files with 471 additions and 88 deletions
+8 -2
View File
@@ -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)
+3
View File
@@ -3,3 +3,6 @@
//
#include "Game.h"
void Game::initialize() noexcept { renderer.setGame(game); }
+3 -2
View File
@@ -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();
}
+11 -2
View File
@@ -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;
}
+73 -56
View File
@@ -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<String, String> translateTable;
Map<String, TranslatedText> translateTable;
int id = 1;
};
class Translator {
Map<String, int> langMap{};
List<Language> langList{};
TranslatedText nullText{ L"\\#FF""EE0000<translator-null>" };
String lang = L"zh-cn";
String nullText = L"<translator-null>";
QWORD langConfig = 1;
int langConfig = 1;
int idLangMax = 1;
using IterID = Map<String, String>::const_iterator;
using IterID = Map<String, TranslatedText>::const_iterator;
using IterLang = List<Language>::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();
+5
View File
@@ -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();
}
+23 -4
View File
@@ -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();
+24
View File
@@ -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));
}
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-1-21.
//
#include "TextureManager.h"
+81
View File
@@ -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<int, Font> fonts;
Map<String, ObjectHolder<ITexture>> textures;
Font* defaultFont = nullptr;
using IterFont = Map<int, Font>::const_iterator;
using IterTexture = Map<String, ObjectHolder<ITexture>>::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();
+14 -8
View File
@@ -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<void(int)>;
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<char>(action & 0xf)) {
case static_cast<char>(MouseActionCode::MAC_HOVER):
onLongHover(0);
break;
case MouseActionCode::MAC_MOVE:
case static_cast<char>(MouseActionCode::MAC_MOVE):
onHover(0);
break;
case MouseActionCode::MAC_DOWN:
case static_cast<char>(MouseActionCode::MAC_DOWN):
onMouseDown(value);
break;
case MouseActionCode::MAC_UP:
case static_cast<char>(MouseActionCode::MAC_UP):
onMouseUp(value);
break;
case MouseActionCode::MAC_DOUBLE:
case static_cast<char>(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<IText> name;
explicit Button(const double x, const double y, const double w, const double h, Location location, ObjectHolder<IText> text) : Widget(x, y, w, h, location), name(text) {}
};
+55
View File
@@ -0,0 +1,55 @@
//
// Created by EmsiaetKadosh on 25-1-18.
//
#include "def.h"
#include "exception.h"
template<TypeName Base> Base& ObjectHolder<Base>::operator->() noexcept(false) {
if (value) return *value;
throw NullPointerException(L"value is null");
}
template<TypeName Base> const Base& ObjectHolder<Base>::operator->() const noexcept(false) {
if (value) return *value;
throw NullPointerException(L"value is null");
}
template<TypeName Base> Base& ObjectHolder<Base>::operator*() noexcept(false) {
if (value) return *value;
throw NullPointerException(L"value is null");
}
template<TypeName Base> const Base& ObjectHolder<Base>::operator*() const noexcept(false) {
if (value) return *value;
throw NullPointerException(L"value is null");
}
template<TypeName Base> template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void ObjectHolder<Base>::set(const T& value) {
if (!value) this->value = new T(value);
else {
if (hasValue) delete this->value;
this->value = new T(value);
}
}
template<TypeName Base> template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void ObjectHolder<Base>::set(T&& value) {
if (!value) this->value = new T(value);
else {
if (hasValue) delete this->value;
this->value = new T(value);
}
}
template<TypeName T> const T& Reference<T>::getLvalue() const noexcept(false) {
if (type == ReferenceValueType::LVALUE) return *lvalue;
throw NullPointerException(L"reference is rvalue");
}
template<TypeName T> T&& Reference<T>::getRvalue() noexcept(false) {
if (type == ReferenceValueType::RVALUE) return std::move(*rvalue);
throw NullPointerException(L"reference is lvalue");
}
+97
View File
@@ -9,6 +9,7 @@
#include <list>
#include <string>
#include <map>
#include <atomic>
using wchar = wchar_t;
using QWORD = unsigned long long int;
@@ -51,3 +52,99 @@ template<typename F> using Function = std::function<F>;
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "Uxtheme.lib")
#pragma comment(lib, "winmm.lib")
template<typename T>
concept Copyable = requires(const T& t) { T(t); };
template<typename T>
concept NewCopyable = requires(const T& t) { delete new T(t); };
template<typename T>
concept Moveable = requires(T&& t) { T(std::move(t)); };
template<typename T>
concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); };
template<typename T>
concept NonreferenceType = !std::is_reference_v<T>;
template<typename T>
concept NonpointerType = !std::is_pointer_v<T>;
template<typename T>
concept ReferenceType = std::is_reference_v<T>;
template<typename T>
concept PointerType = std::is_pointer_v<T>;
template<typename T>
concept TypeName = NonreferenceType<T> && NonpointerType<T>;
template<TypeName Base> class ObjectHolder {
Base* value;
bool hasValue;
char padding[7]{};
public:
/**
* 用于延迟初始化。
*/
ObjectHolder() : value(nullptr), hasValue(false) {}
ObjectHolder(Base* const value) : value(value), hasValue(false) {}
template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
ObjectHolder(const T& value) : value(new T(value)), hasValue(true) {}
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
ObjectHolder(T&& value) : value(new T(std::forward<T>(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<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void set(const T& value);
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
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<TypeName T> 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);
};
+36
View File
@@ -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) {}
};
+4
View File
@@ -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"
+14 -14
View File
@@ -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)) {
+15
View File
@@ -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);
}
};