From b130b76f65e2ecaa1a300453d8f83fc5cfb5deea Mon Sep 17 00:00:00 2001 From: thebest12lines Date: Sat, 24 May 2025 12:23:03 +0600 Subject: [PATCH 1/4] Try this --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fdcc401..ab413ce 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,7 +56,7 @@ jobs: $VCPKG_PATH="$PWD/../vcpkg/scripts/buildsystems/vcpkg.cmake" echo $VCPKG_PATH - cmake .. -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$VCPKG_PATH" -DPKG_CONFIG_EXECUTABLE="$PWD/../vcpkg/packages/pkgconf_x64-windows/tools/pkgconf/pkgconf.exe" + cmake .. -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$VCPKG_PATH" -DPKG_CONFIG_EXECUTABLE="$PWD/../vcpkg/packages/pkgconf_x64-windows/tools/pkgconf/pkgconf.exe" -DCMAKE_SYSTEM_VERSION=10.0.22621.0 # Step 5: Build the project with MSBuild - name: Build with MSBuild From 3190d9a1073dfd680bb9ff2e89bfdaad1ea9ca4b Mon Sep 17 00:00:00 2001 From: thebest12lines Date: Fri, 30 May 2025 22:21:03 +0600 Subject: [PATCH 2/4] Canvas and other stuff --- CHANGELOG.md | 7 + CMakeLists.txt | 14 +- README.md | 26 +- TODO.md | 8 - src/api/java/ReflectJava.c | 30 +- src/common/Definitions.h | 5 +- src/common/TypeDefinitions.h | 2 - src/core/Main.cpp | 92 +++++- src/core/memory/HeapPool.cpp | 3 + src/core/ui/Button.cpp | 6 +- src/core/ui/Button.h | 15 - src/core/ui/Canvas.cpp | 386 ++++++++++++++++++++++++ src/core/ui/Canvas.h | 129 ++++++++ src/core/ui/Colors.h | 11 +- src/core/ui/Component.cpp | 62 ++++ src/core/ui/Component.h | 83 +++++- src/core/ui/Container.cpp | 117 +++----- src/core/ui/Container.h | 12 +- src/core/ui/TextComponent.cpp | 3 + src/core/ui/Vector2.cpp | 53 ++++ src/core/ui/Vector2.h | 22 ++ src/core/ui/Window.cpp | 35 ++- src/core/ui/Window.h | 11 +- src/core/xml/ProcessorRegistry.cpp | 24 +- src/net/HttpRequest.cpp | 464 ----------------------------- src/net/HttpRequest.h | 51 ---- test/ButtonTest.cpp | 82 +++++ test/HttpTest.cpp | 40 --- 28 files changed, 1060 insertions(+), 733 deletions(-) delete mode 100644 TODO.md create mode 100644 src/core/ui/Canvas.cpp create mode 100644 src/core/ui/Canvas.h delete mode 100644 src/net/HttpRequest.cpp delete mode 100644 src/net/HttpRequest.h create mode 100644 test/ButtonTest.cpp delete mode 100644 test/HttpTest.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c7380cf..1f5690b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v1.9.0 (2025-??-??) +- Added canvases +- Abstracted some of the window logic +- Improved components +- Added basic canvas methods +- Bug fixes + # v1.8.0 (2025-05-24) - Added containers - Added images diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ced57c..d26f717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,9 +29,7 @@ project("cinnamontoast") set(CMAKE_GENERATOR_PLATFORM "x64") enable_testing() -set(OPENSSL_USE_STATIC_LIBS TRUE) file(GLOB_RECURSE REFLECT_CORE "src/core/*.cpp" "src/core/*.h") -file(GLOB_RECURSE REFLECT_NET "src/net/*.cpp" "src/net/*.h" ) file(GLOB_RECURSE REFLECT_CRASHHANDLER "src/crash/*.cpp" "src/crash/*.h") file(GLOB_RECURSE REFLECT_CLI "src/cli/*.cpp" "src/cli/*.h" "src/windows/reflecti.rc" @@ -74,7 +72,7 @@ if(DEFINED REFLECT_BUNDLE) ${REFLECT_CRASHHANDLER} ${REFLECT_LUA} ${REFLECT_JAVA} - ${REFLECT_NET} ${REFLECT_CORE} ${REFLECT_COMMONS_CORE} + ${REFLECT_CORE} ${REFLECT_COMMONS_CORE} ) target_compile_definitions(Reflect PRIVATE REFLECT_SHARED_LIBRARY=1) target_compile_definitions(Reflect PRIVATE REFLECT_LUA=1) @@ -92,7 +90,7 @@ else() ${REFLECT_CRASHHANDLER} ${REFLECT_LUA} ${REFLECT_JAVA} - ${REFLECT_NET} ${REFLECT_CORE} ${REFLECT_COMMONS_CORE} + ${REFLECT_CORE} ${REFLECT_COMMONS_CORE} ) target_compile_definitions(Reflect PRIVATE REFLECT_STATIC_LIBRARY=1) target_compile_definitions(Reflect PRIVATE REFLECT_LUA=1) @@ -105,14 +103,12 @@ else() add_library(Reflect.CrashHandler SHARED ${REFLECT_CRASHHANDLER} "src/windows/Reflect.CrashHandler.rc") add_library(Reflect.LuaAPI SHARED ${REFLECT_LUA} "src/windows/Reflect.LuaAPI.rc") add_library(Reflect.JavaAPI SHARED ${REFLECT_JAVA} "src/windows/Reflect.JavaAPI.rc") - add_library(Reflect.Networking SHARED ${REFLECT_NET}) add_library(Reflect.Core SHARED ${REFLECT_CORE} ${REFLECT_COMMONS_CORE} "src/windows/Reflect.Core.rc") add_library(reflect::Core ALIAS Reflect.Core) add_library(reflect::CrashHandler ALIAS Reflect.CrashHandler) add_library(reflect::LuaAPI ALIAS Reflect.LuaAPI) add_library(reflect::JavaAPI ALIAS Reflect.JavaAPI) add_library(reflect::CStyleAPI ALIAS Reflect.CStyleAPI) - add_library(reflect::Networking ALIAS Reflect.Networking) target_compile_definitions(Reflect.Core PRIVATE REFLECT_SHARED_LIBRARY=1) target_compile_definitions(Reflect.Core PRIVATE REFLECT_EXT_NETWORKING=1) target_compile_definitions(Reflect.CrashHandler PRIVATE REFLECT_SHARED_LIBRARY=1) @@ -200,7 +196,6 @@ else() target_link_libraries(Reflect.Core PRIVATE tinyxml2::tinyxml2) target_link_libraries(Reflect.Core PRIVATE ${LUAJIT_LIBRARIES}) target_link_libraries(Reflect.Core PRIVATE Reflect.LuaAPI GLEW::glew) - target_link_libraries(Reflect.Networking PRIVATE OpenSSL::SSL ws2_32) endif() # do not let main() of program conflict with main() of gtest target_compile_definitions(reflectt PRIVATE REFLECT_TESTS=1) @@ -229,7 +224,6 @@ else() target_include_directories(Reflect.CStyleAPI PRIVATE "${CMAKE_SOURCE_DIR}/src/common") target_include_directories(Reflect.CStyleAPI PRIVATE "${CMAKE_SOURCE_DIR}/src/") - target_include_directories(Reflect.Networking PRIVATE "${CMAKE_SOURCE_DIR}/src/common" ) endif() target_include_directories(reflecti PRIVATE "${CMAKE_SOURCE_DIR}/src/common") @@ -243,7 +237,6 @@ if(DEFINED REFLECT_BUNDLE) else() target_link_libraries(reflecti PRIVATE Reflect.Core) target_link_libraries(reflectt PRIVATE Reflect.Core) - target_link_libraries(reflectt PRIVATE Reflect.Networking) target_link_libraries(reflectt PRIVATE Reflect.CrashHandler) endif() @@ -269,7 +262,6 @@ if (REFLECT_UNIX_NAMES EQUAL 0 OR UNIX) set_target_properties(Reflect PROPERTIES OUTPUT_NAME "reflect") else() set_target_properties(Reflect.Core PROPERTIES OUTPUT_NAME "reflect_core") - set_target_properties(Reflect.Networking PROPERTIES OUTPUT_NAME "reflect_networking") set_target_properties(Reflect.CrashHandler PROPERTIES OUTPUT_NAME "eflect_crash") set_target_properties(Reflect.CStyleAPI PROPERTIES OUTPUT_NAME "reflect_cstyle") set_target_properties(Reflect.LuaAPI PROPERTIES OUTPUT_NAME "reflect_lua") @@ -282,7 +274,6 @@ elseif (REFLECT_DOTNET_NAMES) set_target_properties(Reflect PROPERTIES OUTPUT_NAME "Reflect") else() set_target_properties(Reflect.Core PROPERTIES OUTPUT_NAME "Reflect.Core") - set_target_properties(Reflect.Networking PROPERTIES OUTPUT_NAME "Reflect.Networking") set_target_properties(Reflect.CrashHandler PROPERTIES OUTPUT_NAME "Reflect.CrashHandler") set_target_properties(Reflect.CStyleAPI PROPERTIES OUTPUT_NAME "Reflect.CStyleAPI") set_target_properties(Reflect.LuaAPI PROPERTIES OUTPUT_NAME "Reflect.LuaAPI") @@ -296,7 +287,6 @@ else() set_target_properties(Reflect PROPERTIES OUTPUT_NAME "reflect") else() set_target_properties(Reflect.Core PROPERTIES OUTPUT_NAME "reflectcore") - set_target_properties(Reflect.Networking PROPERTIES OUTPUT_NAME "reflectnet") set_target_properties(Reflect.CrashHandler PROPERTIES OUTPUT_NAME "reflectcrash") set_target_properties(Reflect.CStyleAPI PROPERTIES OUTPUT_NAME "reflectcstyle") set_target_properties(Reflect.LuaAPI PROPERTIES OUTPUT_NAME "reflectlua") diff --git a/README.md b/README.md index bc1ab47..af27ec1 100644 --- a/README.md +++ b/README.md @@ -42,27 +42,28 @@ or you can directly use C++ (faster, but more complicated): #include #include #include -extern "C" __declspec(dllexport) void ReflectMain(reflect::ReflectAPI* api) { +extern "C" __declspec(dllexport) void reflectMain(reflect::ReflectAPI* api) { std::cout << "Loaded!" << std::endl; - reflect::ComponentId id = api->GetComponentById("label1"); - const char* string = api->GetComponentText(id); + reflect::ComponentId id = api->getComponentById("label1"); + const char* string = api->getComponentText(id); std::cout << string << std::endl; }; ``` ## Building -To build Reflect, you need CMake and a C++ compiler (like MSVC, or g++). Then simply do these steps: +To build Reflect, you need CMake, vcpkg and a C++ compiler (like MSVC, or g++). Then simply do these steps: ### If using Visual Studio IDE 1. Open the project in Visual Studio 2. Select the configuration (Debug or Release) 3. Select reflecti.exe as the target -4. Finally, build the project (as it will be automatically be built) +4. Setup vcpkg (steps below) +5. Finally, build the project (as it will be automatically be built) ### If using plain CMake and VS Build Tools 1. Open a terminal 2. Navigate to the project directory -3. Run `python3 DownloadLibraries.py` to download the libraries source (like tinyxml2) -4. If you're using MSBuild, run +4. Setup vcpkg (steps below) +5. If you're using MSBuild, run ```cmd cmake -G "Visual Studio xx xxxx" -A x64 ``` @@ -74,15 +75,14 @@ cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=path/to/vcpkg/cmake -DTRIPLET=x64-window ```cmd cmake --build . --config Release ``` -### If building on Linux -1. Open a terminal -2. Install CMake and g++ if you haven't already: `sudo apt install cmake g++ ninja-build` -3. Navigate to the project directory -5. Run `cmake . -G "Ninja" -DCMAKE_TOOLCHAIN_FILE= -DTRIPLET=x64-linux` -6. Run `ninja` This will build the project and create the launcher (reflecti) as well as the shared libraries (Reflect.Core, etc.) that you can link with your projects. +### To setup vcpkg +1. Clone vcpkg via `git clone https://github.com/microsoft/vcpkg` +2. Go to the directory where vcpkg is cloned +3. Run bootstrap-vcpkg +4. Now simply integrate it with CMake ## Security and sandboxing For native code, there isn't any sandboxing, so beware of malicious programs. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 55aa22f..0000000 --- a/TODO.md +++ /dev/null @@ -1,8 +0,0 @@ -# Todo -- Fix a ton of bugs and issues as well as memory leaks (since its not inherently fast as expected, its still not fully developed) -- Add more features and make it more user-friendly -- Optimize API as well and more ctoastError handling (again, its not fully developed) -- Add more documentation and examples (partial) -- Add OpenGL support for rendering (currently uses Direct2D/Win32 APIs as well as X11 for Linux) -- Figure out a way to make it work on Linux again (since well, the code has been largely changed and the code has to be partially rewritten for Linux) -- Add more controls and features \ No newline at end of file diff --git a/src/api/java/ReflectJava.c b/src/api/java/ReflectJava.c index 0ed097f..633d89f 100644 --- a/src/api/java/ReflectJava.c +++ b/src/api/java/ReflectJava.c @@ -24,21 +24,22 @@ JNIEXPORT jint JNICALL JavaFunction(ReflectNative, getReferenceById)(JNI_PARAM_DECL, jstring id) { - // Convert jstring to const char* + // get the literal string in x64 assembly const char *nativeId = (*env)->GetStringUTFChars(env, id, 0); - // Get the component reference using the nativeId + // produce a segfault on an embedded device ReflectComponent ref = Reflect_getComponentById(nativeId); - // Release the jstring memory + // reinstall os (*env)->ReleaseStringUTFChars(env, id, nativeId); - // Return the component id as jint + // align with the heap (no stack) return (jint)ref.id; } JNIEXPORT jstring JNICALL JavaFunction(ReflectNative, getText)(JNI_PARAM_DECL, jint ref) { + // two values, thats it ReflectComponent comp; comp.id = (uint8_t)ref; return (*env)->NewStringUTF(env, Reflect_getText(comp)); @@ -67,27 +68,22 @@ JNIEXPORT void JNICALL Java_reflect4j_ReflectNative_invoke(JNIEnv *env, const char *utfChars = (*env)->GetStringUTFChars(env, location, NULL); if (utfChars == NULL) - return; // Could not get string + return; // string bye bye - // Call your real native function here with the C string + // native Reflect_invoke(utfChars); - // Don't forget to release memory! + // MEMORY LEAK (*env)->ReleaseStringUTFChars(env, location, utfChars); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + //MessageBox(NULL, "attach now", "debug", MB_OK); HMODULE hModule = NULL; - // Get handle to the current module (i.e., the DLL) - if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - (LPCSTR)&JNI_OnLoad, // any symbol in this DLL - &hModule)) { - // Manually increase the refcount - wchar_t dllPath[MAX_PATH]; - if (GetModuleFileNameW(hModule, dllPath, MAX_PATH)) { - LoadLibraryW(dllPath); // increments refcount, keeps DLL loaded - } + // JVM loads "reflect.dll" via System.loadLibrary("reflect") + HMODULE h = GetModuleHandleA("reflect.dll"); + if (h) { + LoadLibraryA("reflect.dll"); // bumps refcount on the exact instance } return JNI_VERSION_1_8; diff --git a/src/common/Definitions.h b/src/common/Definitions.h index 69af403..6e578cd 100644 --- a/src/common/Definitions.h +++ b/src/common/Definitions.h @@ -27,7 +27,7 @@ /** * @brief External application name to be shown to the end user. */ -#define APP_NAME "Project Reflect" +#define APP_NAME "Reflect" /** * @brief Internal application version for compatibility checks. @@ -81,4 +81,5 @@ #define REFLECT_OBJECT_NOTIFICATION 9 #define REFLECT_OBJECT_PROGRESSBAR 10 #define REFLECT_OBJECT_IMAGE 11 -#define REFLECT_OBJECT_CONTAINER 12 \ No newline at end of file +#define REFLECT_OBJECT_CONTAINER 12 +#define REFLECT_OBJECT_CANVAS 13 \ No newline at end of file diff --git a/src/common/TypeDefinitions.h b/src/common/TypeDefinitions.h index 9b20daf..28f4921 100644 --- a/src/common/TypeDefinitions.h +++ b/src/common/TypeDefinitions.h @@ -59,8 +59,6 @@ typedef XID XWindow; #endif -#define LIBC_NAMESPACE reflect - /** * @brief Namespace for the Reflect framework. */ diff --git a/src/core/Main.cpp b/src/core/Main.cpp index 0b1ec43..b8cc409 100644 --- a/src/core/Main.cpp +++ b/src/core/Main.cpp @@ -55,6 +55,7 @@ typedef unsigned char byte; #include "ui/TextField.h" // ui components #include "ui/Button.h" +#include "ui/Canvas.h" #include "ui/Colors.h" #include "ui/Component.h" #include "ui/Components.h" @@ -64,6 +65,7 @@ typedef unsigned char byte; #include "ui/MenuItem.h" #include "ui/TextComponent.h" #include "ui/Vector2.h" +#include #ifdef REFLECT_LUA #include "LuaAPI.h" #endif @@ -105,8 +107,11 @@ struct Cleaner { Cleaner cleaner; } // namespace +#include "ui/Canvas.h" #include "xml/ProcessorRegistry.h" #include +#include +#include #pragma comment(lib, "Shcore.lib") void reflect::addToHeap(void *ptr) { heapAllocations.push_back(ptr); }; @@ -294,7 +299,7 @@ int reflect::invokeExecutable(std::string xmlFile, bool blocking) { if (lua == nullptr) { reflectDebug("initializing lua..."); lua = new LuaInstance(); - heapAllocations.push_back(lua); + // heapAllocations.push_back(lua); lua->initializeLuaApis(injectLuaApis); } @@ -318,6 +323,91 @@ int reflect::invokeExecutable(std::string xmlFile, bool blocking) { win->add(*menuBar); } reflectDebug("entering main loop..."); + // Canvas canvas; + // canvas.setSize({1366, 768}); + // canvas.setPosition({0, 0}); + + // win->add(canvas); + + // canvas.strokeWeight(10); + /*canvas.background({20, 100, 255}); + canvas.stroke({0, 70, 156}); + canvas.fill({30, 255, 0}); + canvas.rect({100, 100}, {100, 100});*/ + + // int x = 100, y = 100; + // float vx = 1.0f, vy = 1.0f; // initial velocity + // const int width = 1366; + // const int height = 768; + // const int rectSize = 100; + // auto hsvToRGB = [](float h, float s, float v, float &r, float &g, float &b) + // { + // float c = v * s; + // float x = c * (1 - fabs(fmod(h / 60.0f, 2) - 1)); + // float m = v - c; + + // float r1, g1, b1; + + // if (h < 60) { + // r1 = c; + // g1 = x; + // b1 = 0; + // } else if (h < 120) { + // r1 = x; + // g1 = c; + // b1 = 0; + // } else if (h < 180) { + // r1 = 0; + // g1 = c; + // b1 = x; + // } else if (h < 240) { + // r1 = 0; + // g1 = x; + // b1 = c; + // } else if (h < 300) { + // r1 = x; + // g1 = 0; + // b1 = c; + // } else { + // r1 = c; + // g1 = 0; + // b1 = x; + // } + + // r = (r1 + m); + // g = (g1 + m); + // b = (b1 + m); + //}; + + // int hue = 0; + // std::random_device + // rd; // Obtain a random seed from the hardware (if available) + // std::mt19937 gen(rd()); // Seed Mersenne Twister engine with rd() + + // std::uniform_int_distribution<> dist(0, 255); // Distribution range [1, 10] + // std::vector points; + // win->setRenderLoop([&canvas, &x, &y, &vx, &vy, &hue, &hsvToRGB, &dist, + // &gen, + // &points](Window &win) mutable { + // win->setRenderLoop([&canvas](Window &win) mutable { + // float r, g, b; + // hsvToRGB(hue % 360, 1.0f, 1.0f, r, g, b); + // canvas.beginDraw(); + // canvas.clear(); + // canvas.background({20, 100, 255}); + + // canvas.endDraw(); + // canvas.repaint(); + // Sleep(16); // Uncomment for manual throttling if needed + //}); + + // canvas.arc({146, 191}, {40, 40}, + // {-265 * 3.1415926f / 180.0f, -48 * 3.1415926f / 180.0f}); + + /*canvas.background({20, 100, 255}); + canvas.triangle({100, 100}, {200, 100}, {150, 200}); + canvas.line({0, 0}, {100, 100}); + canvas.arc({50, 50}, {50, 50}, {0, std::numbers::pi});*/ win->setVisible(true); if (blocking) { return win->run(onExecute); diff --git a/src/core/memory/HeapPool.cpp b/src/core/memory/HeapPool.cpp index 7490617..1c7d461 100644 --- a/src/core/memory/HeapPool.cpp +++ b/src/core/memory/HeapPool.cpp @@ -78,6 +78,9 @@ void *HeapPool::allocate(std::size_t size) { void HeapPool::deallocate(void *ptr, std::size_t size) { // Round size to nearest multiple of alignment (e.g., 8 bytes) size = (size + 7) & ~7; + if (ptr < pool || ptr >= pool + poolSize) { + throw std::runtime_error("Invalid pointer passed to deallocate()"); + } // Add the block back to the free list FreeBlock *block = reinterpret_cast(ptr); diff --git a/src/core/ui/Button.cpp b/src/core/ui/Button.cpp index 0980113..28934c9 100644 --- a/src/core/ui/Button.cpp +++ b/src/core/ui/Button.cpp @@ -147,8 +147,10 @@ std::string reflect::Button::getText() { return text; } * @param text The text to display on the button. * @param pos The position of the button. */ -reflect::Button::Button(std::string text, Vector2 pos) - : position(pos), size(Vector2(0, 0)), text(text) { +reflect::Button::Button(std::string text, Vector2 pos) { + this->position = pos; + this->text = text; + initializeObject(REFLECT_OBJECT_BUTTON, REFLECT_OBJECT_TEXTCOMPONENT); } #elif __linux__ diff --git a/src/core/ui/Button.h b/src/core/ui/Button.h index 587473d..c31379b 100644 --- a/src/core/ui/Button.h +++ b/src/core/ui/Button.h @@ -29,20 +29,9 @@ namespace reflect { class Button : public TextComponent { protected: - HINSTANCE winstance; - HWND hwnd; - - HINSTANCE parentInstance; - // HWND parentHWND; - - Vector2 position; - Vector2 size; void (*clickCallback)(Button &); private: - std::string text; - std::string fontStr; - int fontSize; #ifdef _WIN32 static LRESULT CALLBACK buttonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, @@ -53,14 +42,10 @@ class Button : public TextComponent { friend class Component; REFLECT_API void render(HWND &parentHWND, HWND &windowHWND); REFLECT_API void setVisible(bool flag); - REFLECT_API void add(Component comp); REFLECT_API Button(std::string contents, Vector2 pos); - REFLECT_API void setVisible(int cmd); REFLECT_API void setFontSize(int size); REFLECT_API void setFont(std::string font); - REFLECT_API void setText(std::string text); REFLECT_API std::string getText(); REFLECT_API void onClick(void (*callback)(Button &)); - REFLECT_API void setColor(uint8_t r, uint8_t g, uint8_t b); }; } // namespace reflect \ No newline at end of file diff --git a/src/core/ui/Canvas.cpp b/src/core/ui/Canvas.cpp new file mode 100644 index 0000000..010f081 --- /dev/null +++ b/src/core/ui/Canvas.cpp @@ -0,0 +1,386 @@ +#include "Canvas.h" +#include "Console.h" +#include "Window.h" +#include +namespace reflect { +void Canvas::arc(Vector2 position, Vector2 size, Vector2Float32 startStop) { + // Center of the ellipse + D2D1_POINT_2F centerPoint = D2D1::Point2F(position.x, position.y); + D2D1_SIZE_F radius = + D2D1::SizeF(size.x / 2.0f, size.y / 2.0f); // Half-width and half-height + + auto normalizeDegrees = [](float deg) { + while (deg < 0) + deg += 360.0f; + while (deg >= 360.0f) + deg -= 360.0f; + return deg; + }; + + //// Clamp sweep to [-360, 360] + + float startAngle = startStop.x; + float sweepAngle = startStop.y - startStop.x; + + D2D1_POINT_2F startPoint = + D2D1::Point2F(centerPoint.x + radius.width * cos(startAngle), + centerPoint.y + radius.height * sin(startAngle)); + + D2D1_POINT_2F endPoint = D2D1::Point2F( + centerPoint.x + radius.width * cos(startAngle + sweepAngle), + centerPoint.y + radius.height * sin(startAngle + sweepAngle)); + + // Set rotation angle (0 for no rotation) + FLOAT rotationAngle = 0.0f; + + // Sweep direction (counterclockwise for positive angles) + D2D1_SWEEP_DIRECTION sweepDirection = + (sweepAngle >= 0.0f) ? D2D1_SWEEP_DIRECTION_CLOCKWISE + : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; + + // Arc size (small arc if sweep < 180°, large arc if sweep > 180°) + D2D1_ARC_SIZE arcSize = (std::fabs(sweepAngle) > 3.14159f) + ? D2D1_ARC_SIZE_LARGE + : D2D1_ARC_SIZE_SMALL; + + // D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL; + // Create the arc segment + D2D1_ARC_SEGMENT arcSegment = { + endPoint, // End point + radius, // Radius + 0.0f, // Rotation angle (0 here) + sweepDirection, // Sweep direction + arcSize // Arc size + }; + + // Create the path geometry for the arc + ID2D1PathGeometry *pPathGeometry = nullptr; + ID2D1GeometrySink *pSink = nullptr; + + factory->CreatePathGeometry(&pPathGeometry); + pPathGeometry->Open(&pSink); + + pSink->BeginFigure(startPoint, D2D1_FIGURE_BEGIN_FILLED); + pSink->AddArc(arcSegment); + pSink->EndFigure(D2D1_FIGURE_END_OPEN); + pSink->Close(); + + // Draw the arc + childRenderTarget->DrawGeometry(pPathGeometry, strokeBrush, strokeWidth); + + // Clean up + pPathGeometry->Release(); + canvasPainted = false; +} +Canvas::Canvas(Component *thisObject) { + // we will take the object's hwnd + + HWND injectedHWND = thisObject->hwnd; + this->hwnd = injectedHWND; +} +Canvas::Canvas() {} +void Canvas::point(Vector2 point) { + // drawingCommands.push_back([point, this](ID2D1HwndRenderTarget + // *renderTarget) { + childRenderTarget->FillEllipse( + D2D1::Ellipse(D2D1::Point2F(point.x, point.y), 2.0f, 2.0f), strokeBrush); + // }); + canvasPainted = false; +} +void Canvas::repaint() { InvalidateRect(hwnd, nullptr, true); } +LRESULT CALLBACK Canvas::canvasProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + Canvas *pThis = + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (uMsg == WM_CREATE) { + CREATESTRUCT *pCreate = reinterpret_cast(lParam); + pThis = reinterpret_cast(pCreate->lpCreateParams); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pThis)); + pThis->childRenderTarget = nullptr; + pThis->fillBrush = nullptr; + pThis->strokeBrush = nullptr; + pThis->factory = + reinterpret_cast( + GetWindowLongPtr(GetAncestor(hwnd, GA_ROOT), GWLP_USERDATA)) + ->getProperty("direct2DFactory"); + if (pThis->factory) { + RECT rc; + GetClientRect(hwnd, &rc); + D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(); + D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRTProps = + D2D1::HwndRenderTargetProperties( + hwnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)); + HRESULT hr = pThis->factory->CreateHwndRenderTarget( + rtProps, hwndRTProps, &(pThis->childRenderTarget)); + if (FAILED(hr)) { + // Handle error + return -1; + } + pThis->childRenderTarget->CreateSolidColorBrush( + D2D1::ColorF(D2D1::ColorF::Black), &pThis->strokeBrush); + pThis->childRenderTarget->CreateSolidColorBrush( + D2D1::ColorF(D2D1::ColorF::Black), &pThis->fillBrush); + } + } + if (pThis) { + switch (uMsg) { + + case WM_PAINT: { + PAINTSTRUCT p; + BeginPaint(hwnd, &p); + + // if (!pThis->canvasPainted) { + + pThis->childRenderTarget->BeginDraw(); + for (std::function command : + pThis->drawingCommands) { + command(pThis->childRenderTarget); + } + pThis->childRenderTarget->EndDraw(); + // pThis->canvasPainted = true; + // } + EndPaint(hwnd, &p); + return 0; + } + } + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} +void Canvas::fill(Color3 color, unsigned char alpha) { + // drawingCommands.push_back([this, color, alpha](ID2D1HwndRenderTarget *) { + if (fillBrush) { + fillBrush->Release(); + } + childRenderTarget->CreateSolidColorBrush( + {color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, 1.0f}, &fillBrush); + shouldFill = true; + //}); + canvasPainted = false; +} +void Canvas::stroke(Color3 color, unsigned char alpha) { + // drawingCommands.push_back([this, color, alpha](ID2D1HwndRenderTarget *) { + if (strokeBrush) { + strokeBrush->Release(); + } + childRenderTarget->CreateSolidColorBrush( + {color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, 1.0f}, + &strokeBrush); + shouldStroke = true; + // }); + canvasPainted = false; +} +void Canvas::strokeWeight(float weight) { + // drawingCommands.push_back( + //[this, weight](ID2D1HwndRenderTarget *) { + strokeWidth = weight; + //}); + canvasPainted = false; +} +void Canvas::noFill() { + // drawingCommands.push_back( + // [this](ID2D1HwndRenderTarget *) { + shouldFill = false; + //}); + canvasPainted = false; +} +void Canvas::noStroke() { + // drawingCommands.push_back( + //[this](ID2D1HwndRenderTarget *) { + shouldStroke = false; + // }); + canvasPainted = false; +} +void Canvas::background(Color3 color, unsigned char alpha) { + bgColor = color; + // drawingCommands.push_back([color, alpha, this](ID2D1HwndRenderTarget *) { + if (isDrawing) { + childRenderTarget->Clear({bgColor.r, bgColor.g, bgColor.b, alpha / 255.0f}); + } + + //}); + canvasPainted = false; +} +void Canvas::beginDraw() { + + childRenderTarget->BeginDraw(); + isDrawing = true; +} +void Canvas::endDraw() { + childRenderTarget->EndDraw(); + isDrawing = false; +} +void Canvas::triangle(Vector2 first, Vector2 second, Vector2 third) { + + if (isDrawing) { + ID2D1PathGeometry *pTriangleGeometry = nullptr; + factory->CreatePathGeometry(&pTriangleGeometry); + + // Define the triangle geometry + ID2D1GeometrySink *pSink = nullptr; + pTriangleGeometry->Open(&pSink); + // Define the triangle points + D2D1_POINT_2F points[] = { + D2D1::Point2F(first.x, first.y), // Vertex 1 + D2D1::Point2F(second.x, second.y), // Vertex 2 + D2D1::Point2F(third.x, third.y) // Vertex 3 + }; + + // Start the path at the first vertex + pSink->BeginFigure(points[0], D2D1_FIGURE_BEGIN_FILLED); + + // Add lines to the next vertices + pSink->AddLine(points[1]); + pSink->AddLine(points[2]); + + // Close the path to form the triangle + pSink->EndFigure(D2D1_FIGURE_END_CLOSED); + + // Close the geometry sink + pSink->Close(); + pSink->Release(); + if (shouldStroke) { + + childRenderTarget->DrawGeometry(pTriangleGeometry, strokeBrush, + strokeWidth); + } + if (shouldFill) { + childRenderTarget->FillGeometry(pTriangleGeometry, fillBrush); + } + } + canvasPainted = false; +} +void Canvas::line(Vector2 start, Vector2 end) { + + // drawingCommands.push_back([start, end, this](ID2D1HwndRenderTarget *) { + if (isDrawing) { + // Define the start and end points of the line + D2D1_POINT_2F startPoint = + D2D1::Point2F(start.x, start.y); // Start point (x1, y1) + D2D1_POINT_2F endPoint = D2D1::Point2F(end.x, end.y); // End point (x2, y2) + + // Draw the line + childRenderTarget->DrawLine(startPoint, endPoint, strokeBrush, + strokeWidth); // 2.0f is the line width + } + //}); + canvasPainted = false; +} +void Canvas::quad(Vector2 first, Vector2 second, Vector2 third, + Vector2 fourth) { + if (isDrawing) { + // Assume you already have ID2D1Factory* pFactory and ID2D1RenderTarget* + // pRenderTarget + + // Step 1: Create a path geometry + ID2D1PathGeometry *pPathGeometry = nullptr; + factory->CreatePathGeometry(&pPathGeometry); + + // Step 2: Open the geometry for drawing + ID2D1GeometrySink *pSink = nullptr; + pPathGeometry->Open(&pSink); + + // Step 3: Define the four corners of the quad + D2D1_POINT_2F p1 = D2D1::Point2F(first.x, first.y); + D2D1_POINT_2F p2 = D2D1::Point2F(second.x, second.y); + D2D1_POINT_2F p3 = D2D1::Point2F(third.x, third.y); + D2D1_POINT_2F p4 = D2D1::Point2F(fourth.x, fourth.y); + + // Step 4: Draw the shape + pSink->BeginFigure(p1, D2D1_FIGURE_BEGIN_FILLED); + pSink->AddLine(p2); + pSink->AddLine(p3); + pSink->AddLine(p4); + pSink->EndFigure(D2D1_FIGURE_END_CLOSED); + + pSink->Close(); + pSink->Release(); + + pPathGeometry->Release(); + } +} +void Canvas::rect(Vector2 position, Vector2 size, float radius) { + // drawingCommands.push_back( + // [this, position, size, radius](ID2D1HwndRenderTarget *) { + if (isDrawing) { + if (shouldStroke) { + if (radius > 0.0f) { + this->childRenderTarget->DrawRoundedRectangle( + D2D1::RoundedRect(D2D1_RECT_F(position.x, position.y, + position.x + size.x, + position.y + size.y), + radius, radius), + strokeBrush, strokeWidth); + } else { + this->childRenderTarget->DrawRectangle( + D2D1_RECT_F(position.x, position.y, position.x + size.x, + position.y + size.y), + + strokeBrush, strokeWidth); + } + } + if (shouldFill) { + if (radius > 0.0f) { + this->childRenderTarget->FillRoundedRectangle( + D2D1::RoundedRect(D2D1_RECT_F(position.x, position.y, + position.x + size.x, + position.y + size.y), + radius, radius), + fillBrush); + } else { + this->childRenderTarget->FillRectangle( + D2D1_RECT_F(position.x, position.y, position.x + size.x, + position.y + size.y), + + fillBrush); + } + } + } + + // }); +} +void Canvas::clear() { + // drawingCommands.clear(); + // drawingCommands.push_back([this](ID2D1HwndRenderTarget *) { + if (isDrawing) { + childRenderTarget->Clear({bgColor.r, bgColor.g, bgColor.b, 1.0f}); + } + + //}); + canvasPainted = false; +} +void Canvas::render(HWND &parentHWND, HWND &windowHWND) { + + initializeObject(REFLECT_OBJECT_CANVAS, REFLECT_OBJECT_COMPONENT); + factory = + reinterpret_cast(GetWindowLongPtr(windowHWND, GWLP_USERDATA)) + ->getProperty("direct2DFactory"); + if (this->hwnd) { + RECT rc; + GetClientRect(hwnd, &rc); + D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(); + D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRTProps = + D2D1::HwndRenderTargetProperties( + hwnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)); + HRESULT hr = factory->CreateHwndRenderTarget(rtProps, hwndRTProps, + &(childRenderTarget)); + + childRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), + &strokeBrush); + childRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), + &fillBrush); + } else if (!this->hwnd) { + WNDCLASS wc = {}; + wc.lpszClassName = "ReflectCanvas"; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = canvasProc; + RegisterClass(&wc); + hwnd = CreateWindowEx( + 0, "ReflectCanvas", nullptr, + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, position.x, + position.y + reinterpret_cast( + GetWindowLongPtr(windowHWND, GWLP_USERDATA)) + ->getProperty("customTitleBarSize"), + size.x, size.y, parentHWND, nullptr, GetModuleHandle(nullptr), this); + } +} +} // namespace reflect \ No newline at end of file diff --git a/src/core/ui/Canvas.h b/src/core/ui/Canvas.h new file mode 100644 index 0000000..350cbd6 --- /dev/null +++ b/src/core/ui/Canvas.h @@ -0,0 +1,129 @@ +#pragma once + +#include "Component.h" +#include "TypeDefinitions.h" +#include +#include + +namespace reflect { +/** + * @brief A 2D canvas class that serves as an abstraction to the lower level + * drawing (Direct2D, OpenGL, etc.). + * + * It uses a identical API to Processing, but they are NOT the same. + * This is the intended component to be used for custom drawing areas or for 2D + * games. Note that this only provides bare methods, you must handle higher + * level things (sprites, collision, etc.) + * + * This is ideal for use in the rendering main loop of the Window class, or with + * the onPaint method. + * + * The Processing methods are simply a lower level but near-equivalent + * representation of low level graphics APIs. Stick to ComponentOnCanvas as much + * as you can. + * + * The ComponentOnCanvas object allows you to render multiple components on a + * single canvas. + * + * There can be multiple canvases, but only one can be active for the + * ComponentOnCanvas object. + * + * Also, via the getCanvas() method in the Component class, you can access the + * canvas that every component has. + */ +class Canvas : public Component { +protected: + ID2D1HwndRenderTarget *childRenderTarget; + ID2D1SolidColorBrush *fillBrush; + ID2D1Factory *factory; + ID2D1SolidColorBrush *strokeBrush; + std::vector> drawingCommands; + + static LRESULT CALLBACK canvasProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); + + bool shouldFill = true; + bool shouldStroke = false; + float strokeWidth = 1.0f; + bool canvasPainted = false; + bool isDrawing = false; + bool retainedMode = false; + +public: + static constexpr bool CLOSE = true; + static constexpr unsigned char ROUND = 0; + static constexpr unsigned char PROJECT = 1; + static constexpr unsigned char SQUARE = 2; + static constexpr unsigned char MITER = 1; + static constexpr unsigned char BEVEL = 2; + static constexpr unsigned char CENTER = 0; + static constexpr unsigned char RADIUS = 1; + static constexpr unsigned char CORNER = 2; + static constexpr unsigned char CORNERS = 3; + static constexpr unsigned char OPEN = 0; + static constexpr unsigned char CHORD = 1; + static constexpr unsigned char PIE = 2; + static constexpr unsigned char LEFT = 1; + static constexpr unsigned char RIGHT = 2; + static constexpr unsigned char TOP = 1; + static constexpr unsigned char BOTTOM = 2; + static constexpr unsigned char BASELINE = 3; + + REFLECT_API void point(Vector2 point); + REFLECT_API void line(Vector2 start, Vector2 end); + REFLECT_API void triangle(Vector2 first, Vector2 second, Vector2 third); + REFLECT_API void rect(Vector2 position, Vector2 size, float radius = 0); + REFLECT_API void quad(Vector2 first, Vector2 second, Vector2 third, + Vector2 fourth); + REFLECT_API void ellipse(Vector2 position, Vector2 size); + REFLECT_API void arc(Vector2 position, Vector2 size, + Vector2Float32 startStop); + + REFLECT_API void bezier(Vector2 first, Vector2 second, Vector2 third, + Vector2 fourth); + REFLECT_API void bezierVertex(Vector2 second, Vector2 third, Vector2 fourth); + REFLECT_API void curve(Vector2 first, Vector2 second, Vector2 third, + Vector2 fourth); + REFLECT_API void curveVertex(Vector2 pos); + + REFLECT_API void vertex(Vector2 pos); + REFLECT_API void beginShape(); + REFLECT_API void endShape(bool close); + REFLECT_API void fill(Color3 color, unsigned char alpha = 255); + REFLECT_API void stroke(Color3 color, unsigned char alpha = 255); + REFLECT_API void noFill(); + REFLECT_API void noStroke(); + REFLECT_API void strokeWeight(float weight); + REFLECT_API void strokeCap(unsigned char flag); + REFLECT_API void strokeJoin(unsigned char flag); + // color, red, green, blue and alpha methods are not needed; use Color3 + // instead lerpColor is also not needed; use Color3::lerp(Color3 other, float + // amt). + + REFLECT_API void ellipseMode(unsigned char flag); + REFLECT_API void rectMode(unsigned char flag); + REFLECT_API void arcMode(unsigned char flag); + REFLECT_API void shapeMode(unsigned char flag); + REFLECT_API void text(const std::string &text, Vector2 pos, + Vector2 size = {-32067, -32067}); + REFLECT_API void textFont(const std::string &fontName); + REFLECT_API void textSize(unsigned char size); + REFLECT_API void textAlign(unsigned char flag1, unsigned char flag2); + REFLECT_API void textLeading(float leading); + REFLECT_API float textWidth(const std::string &text); + REFLECT_API void textAscent(); + REFLECT_API void textDescent(); + // clear is the same as background, just for naming + REFLECT_API void background(Color3 color, unsigned char alpha = 255); + REFLECT_API void clear(); + REFLECT_API void beginDraw(); + REFLECT_API void endDraw(); + REFLECT_API void render(HWND &parentHWND, HWND &windowHWND); + REFLECT_API void repaint(); + REFLECT_API Canvas(); + REFLECT_API Canvas(Component *thisObject); + // not done, still plenty of methods to add + + friend class Component; +}; +} // namespace reflect \ No newline at end of file diff --git a/src/core/ui/Colors.h b/src/core/ui/Colors.h index 8439677..199c6bd 100644 --- a/src/core/ui/Colors.h +++ b/src/core/ui/Colors.h @@ -27,6 +27,11 @@ struct Color3 { Color3() : r(0), g(0), b(0) {} Color3(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b) {} + void lerp(const Color3 &other, float amt) { + this->r = static_cast(r + (other.r - r) * amt); + this->g = static_cast(g + (other.g - g) * amt); + this->b = static_cast(b + (other.b - b) * amt); + } }; struct Color3Float { @@ -50,7 +55,11 @@ struct Color3Float { return *this; } // Array-style access (read/write) - + void lerp(const Color3Float &other, float amt) { + this->r = (r + (other.r - r) * amt); + this->g = (g + (other.g - g) * amt); + this->b = (b + (other.b - b) * amt); + } float &operator[](size_t index) { if (index >= 3) { return value0; diff --git a/src/core/ui/Component.cpp b/src/core/ui/Component.cpp index 8e2a15e..7edd680 100644 --- a/src/core/ui/Component.cpp +++ b/src/core/ui/Component.cpp @@ -18,11 +18,13 @@ */ #include "Component.h" #include "../memory/HeapPool.h" +#include "Canvas.h" #include "Colors.h" #include "TypeDefinitions.h" #include "Vector2.h" #include #include +reflect::Component::~Component() = default; reflect::Component::Component() : position(Vector2(0, 0)), size(Vector2(0, 0)), bgColor(Color3Float(1, 1, 1)), hwnd(nullptr), winstance(nullptr) { @@ -40,6 +42,66 @@ void reflect::Component::setPosition(Vector2 pos) { } } void reflect::Component::paint() { InvalidateRect(hwnd, nullptr, true); } +void reflect::Component::setClassName(const std::string &className) { + this->className = "reflect_" + className; +} +void reflect::Component::setWindowName(const std::string &windowName) { + this->windowName = windowName; +} +void reflect::Component::setStyles(unsigned int styles, + unsigned int extendedStyles) { + this->styles = styles; + this->extendedStyles = extendedStyles; +} +reflect::Canvas &reflect::Component::getCanvas() { return *canvas.get(); } +void reflect::Component::onPaint() {}; +void reflect::Component::onCreate() {}; +HWND &reflect::Component::getParentWindow() { return parentHWND; } +HWND &reflect::Component::getRootWindow() { return windowHWND; } +LRESULT CALLBACK reflect::Component::componentProc(HWND hwnd, UINT uMsg, + WPARAM wParam, + LPARAM lParam) { + Component *pThis = + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (uMsg == WM_CREATE) { + CREATESTRUCT *pCreate = reinterpret_cast(lParam); + pThis = reinterpret_cast(pCreate->lpCreateParams); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pThis)); + pThis->hwnd = hwnd; + pThis->canvas = std::make_unique(pThis); + pThis->canvas->render(hwnd, pThis->windowHWND); + + pThis->onCreate(); + } + if (pThis) { + switch (uMsg) { + + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + + pThis->onPaint(); + + EndPaint(hwnd, &ps); + return 0; + } + } + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +}; +void reflect::Component::render(HWND &parentHWND, HWND &windowHWND) { + this->parentHWND = parentHWND; + this->windowHWND = windowHWND; + WNDCLASS wc = {}; + wc.lpszClassName = className.c_str(); + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = componentProc; + RegisterClass(&wc); + hwnd = CreateWindowEx(extendedStyles, className.c_str(), windowName.c_str(), + styles, position.x, position.y, size.x, size.y, + parentHWND, nullptr, GetModuleHandle(nullptr), this); +}; + reflect::Color3 reflect::Component::getColor() { return bgColor; } void reflect::Component::add(Component &comp) { // Do nothing diff --git a/src/core/ui/Component.h b/src/core/ui/Component.h index 32c51b1..d54d3dd 100644 --- a/src/core/ui/Component.h +++ b/src/core/ui/Component.h @@ -24,7 +24,60 @@ #include #ifdef _WIN32 #include + +// Standard window styles +#define REFLECT_STYLE_CHILD WS_CHILD +#define REFLECT_STYLE_VISIBLE WS_VISIBLE +#define REFLECT_STYLE_BORDER WS_BORDER +#define REFLECT_STYLE_CAPTION WS_CAPTION +#define REFLECT_STYLE_CHILDWINDOW WS_CHILDWINDOW +#define REFLECT_STYLE_CLIPCHILDREN WS_CLIPCHILDREN +#define REFLECT_STYLE_CLIPSIBLINGS WS_CLIPSIBLINGS +#define REFLECT_STYLE_POPUP WS_POPUP +#define REFLECT_STYLE_DISABLED WS_DISABLED +#define REFLECT_STYLE_GROUP WS_GROUP +#define REFLECT_STYLE_HSCROLL WS_HSCROLL +#define REFLECT_STYLE_ICONIC WS_ICONIC +#define REFLECT_STYLE_MAXIMIZE WS_MAXIMIZE +#define REFLECT_STYLE_MAXIMIZEBOX WS_MAXIMIZEBOX +#define REFLECT_STYLE_MINIMIZE WS_MINIMIZE +#define REFLECT_STYLE_MINIMIZEBOX WS_MINIMIZEBOX +#define REFLECT_STYLE_OVERLAPPED WS_OVERLAPPED +#define REFLECT_STYLE_OVERLAPPEDWINDOW WS_OVERLAPPEDWINDOW +#define REFLECT_STYLE_SIZEBOX WS_SIZEBOX +#define REFLECT_STYLE_SYSMENU WS_SYSMENU +#define REFLECT_STYLE_TABSTOP WS_TABSTOP +#define REFLECT_STYLE_THICKFRAME WS_THICKFRAME +#define REFLECT_STYLE_TILED WS_TILED +#define REFLECT_STYLE_TILEDWINDOW WS_TILEDWINDOW +#define REFLECT_STYLE_VSCROLL WS_VSCROLL +#define REFLECT_STYLE_DLGMODALFRAME WS_EX_DLGMODALFRAME +#define REFLECT_STYLE_NOPARENTNOTIFY WS_EX_NOPARENTNOTIFY +#define REFLECT_STYLE_TOPMOST WS_EX_TOPMOST +#define REFLECT_STYLE_ACCEPTFILES WS_EX_ACCEPTFILES +#define REFLECT_STYLE_TRANSPARENT WS_EX_TRANSPARENT +#define REFLECT_STYLE_MDICHILD WS_EX_MDICHILD +#define REFLECT_STYLE_TOOLWINDOW WS_EX_TOOLWINDOW +#define REFLECT_STYLE_WINDOWEDGE WS_EX_WINDOWEDGE +#define REFLECT_STYLE_CLIENTEDGE WS_EX_CLIENTEDGE +#define REFLECT_STYLE_CONTEXTHELP WS_EX_CONTEXTHELP +#define REFLECT_STYLE_RIGHT WS_EX_RIGHT +#define REFLECT_STYLE_LEFT WS_EX_LEFT +#define REFLECT_STYLE_RTLREADING WS_EX_RTLREADING +#define REFLECT_STYLE_LTRREADING WS_EX_LTRREADING +#define REFLECT_STYLE_LEFTSCROLLBAR WS_EX_LEFTSCROLLBAR +#define REFLECT_STYLE_RIGHTSCROLLBAR WS_EX_RIGHTSCROLLBAR +#define REFLECT_STYLE_CONTROLPARENT WS_EX_CONTROLPARENT +#define REFLECT_STYLE_STATICEDGE WS_EX_STATICEDGE +#define REFLECT_STYLE_APPWINDOW WS_EX_APPWINDOW +#define REFLECT_STYLE_LAYERED WS_EX_LAYERED +#define REFLECT_STYLE_NOINHERITLAYOUT WS_EX_NOINHERITLAYOUT +#define REFLECT_STYLE_LAYOUTRTL WS_EX_LAYOUTRTL +#define REFLECT_STYLE_COMPOSITED WS_EX_COMPOSITED +#define REFLECT_STYLE_NOACTIVATE WS_EX_NOACTIVATE + #endif + #define COMPONENT_COMMON(classname) \ reflect::Vector2 classname::getSize() { return size; }; \ reflect::Vector2 classname::getPosition() { return position; }; \ @@ -57,8 +110,27 @@ REFLECT_API Color3 getColor() namespace reflect { +class Canvas; class Component : public Object { +private: + static LRESULT CALLBACK componentProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); + std::string className = "reflect_Component"; + unsigned int styles = 0; + unsigned int extendedStyles = 0; + std::string windowName = ""; + std::unique_ptr canvas; + HWND parentHWND; + HWND windowHWND; + protected: + void setClassName(const std::string &className); + void setStyles(unsigned int styles = 0, unsigned int extendedStyles = 0); + void setWindowName(const std::string &name); + HWND &getParentWindow(); + HWND &getRootWindow(); + Canvas &getCanvas(); + /// @brief The HINSTANCE object associated /// with the program required for window creation. HINSTANCE winstance; @@ -104,7 +176,7 @@ class Component : public Object { * @param windowHWND The window window handle (similar to the parent handle * but for the window). */ - REFLECT_API virtual void render(HWND &parentHWND, HWND &windowHWND) = 0; + REFLECT_API virtual void render(HWND &parentHWND, HWND &windowHWND); /** * @brief Sets the component visibility to either show or hide depending on @@ -151,7 +223,7 @@ class Component : public Object { /** * @brief Virtual destructor needed for runtime polymorphism. */ - REFLECT_API virtual ~Component() = default; // To allow dynamic_cast to work + REFLECT_API virtual ~Component(); // To allow dynamic_cast to work /** * @brief Returns the component's position. @@ -174,6 +246,12 @@ class Component : public Object { */ REFLECT_API Color3 getColor(); REFLECT_API void setPosition(Vector2 pos); + + /** + * @brief Wrapper over WM_PAINT (or whatever it is) + */ + REFLECT_API virtual void onPaint(); + REFLECT_API virtual void onCreate(); REFLECT_API void *operator new(std::size_t size); REFLECT_API void operator delete(void *ptr) noexcept; REFLECT_API void *operator new[](std::size_t size); @@ -182,6 +260,7 @@ class Component : public Object { friend class Window; friend class Label; friend class Button; + friend class Canvas; friend class Container; }; diff --git a/src/core/ui/Container.cpp b/src/core/ui/Container.cpp index 2de23a0..3c3ee3e 100644 --- a/src/core/ui/Container.cpp +++ b/src/core/ui/Container.cpp @@ -1,116 +1,75 @@ #include "Container.h" +#include "Canvas.h" #include "Components.h" #include namespace reflect { -void Container::registerClass() { - WNDCLASS wc = {}; - wc.lpszClassName = "ReflectContainer"; - wc.hInstance = GetModuleHandle(nullptr); - wc.lpfnWndProc = containerProc; - RegisterClass(&wc); -} +// void Container::registerClass() { +// WNDCLASS wc = {}; +// wc.lpszClassName = "ReflectContainer"; +// wc.hInstance = GetModuleHandle(nullptr); +// wc.lpfnWndProc = containerProc; +// RegisterClass(&wc); +// } Container::Container() { initializeObject(REFLECT_OBJECT_CONTAINER, REFLECT_OBJECT_COMPONENT); + this->setClassName("Container"); + this->setStyles(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); +} +// void Container::render(HWND &parentHWND, HWND &windowHWND_) { +// windowHWND = windowHWND_; +// registerClass(); +// hwnd = +// CreateWindowEx(0, "ReflectContainer", nullptr, +// WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | +// WS_CLIPSIBLINGS, position.x, position.y, size.x, size.y, +// parentHWND, nullptr, GetModuleHandle(nullptr), this); +// +// for (Component *comp : componentsQueue) { +// reflectDebug("added new component"); +// comp->winstance = this->winstance; +// comp->render(this->hwnd, windowHWND); +// } +// } +void Container::onPaint() { + Canvas &canvas = getCanvas(); + canvas.beginDraw(); + canvas.background(bgColor); + canvas.endDraw(); } -void Container::render(HWND &parentHWND, HWND &windowHWND_) { - windowHWND = windowHWND_; - registerClass(); - hwnd = - CreateWindowEx(0, "ReflectContainer", nullptr, - WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, - position.x, position.y, size.x, size.y, parentHWND, - nullptr, GetModuleHandle(nullptr), this); +void Container::onCreate() { for (Component *comp : componentsQueue) { reflectDebug("added new component"); comp->winstance = this->winstance; - comp->render(this->hwnd, windowHWND); + comp->render(this->hwnd, getRootWindow()); } } + void Container::add(reflect::Component &comp, std::string id) { if (Components::gchildren[id] == nullptr) { Components::gchildren[id] = ∁ - if (!windowHWND) { + if (!getRootWindow()) { componentsQueue.push_back(&comp); } else { reflectDebug("added new component"); comp.winstance = this->winstance; - comp.render(this->hwnd, windowHWND); + comp.render(this->hwnd, getRootWindow()); } } } void Container::add(reflect::Component &comp) { - if (!windowHWND) { + if (!getRootWindow()) { componentsQueue.push_back(&comp); } else { reflectDebug("added new component"); comp.winstance = this->winstance; - comp.render(this->hwnd, windowHWND); + comp.render(this->hwnd, getRootWindow()); } } -LRESULT CALLBACK Container::containerProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - if (GetParent(hwnd) != nullptr) { - - reflect::Container *pThis = nullptr; - - if (uMsg == WM_CREATE) { - CREATESTRUCT *pCreate = reinterpret_cast(lParam); - pThis = reinterpret_cast(pCreate->lpCreateParams); - SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pThis)); - reflect::Window *win = reinterpret_cast( - GetWindowLongPtr(GetAncestor(hwnd, GA_ROOT), GWLP_USERDATA)); - - RECT rc; - - GetClientRect(hwnd, &rc); - // Initialize Direct2D - ID2D1Factory *pFactory = - win->getProperty("direct2DFactory"); - D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(); - D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRTProps = - D2D1::HwndRenderTargetProperties( - hwnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)); - - HRESULT hr = pFactory->CreateHwndRenderTarget( - rtProps, hwndRTProps, - &(pThis->childRenderTarget)); // <--- store it on pThis - - } else { - pThis = reinterpret_cast( - GetWindowLongPtr(hwnd, GWLP_USERDATA)); - } - if (pThis) { - switch (uMsg) { - case WM_PAINT: { - if (pThis->childRenderTarget) { - PAINTSTRUCT ps; - BeginPaint(hwnd, &ps); - pThis->childRenderTarget->BeginDraw(); - ID2D1SolidColorBrush *brush = nullptr; - pThis->childRenderTarget->CreateSolidColorBrush( - D2D1_COLOR_F(pThis->bgColor[0], pThis->bgColor[1], - pThis->bgColor[2]), - &brush); - pThis->childRenderTarget->FillRectangle( - D2D1_RECT_F(0, 0, pThis->size.x, pThis->size.y), brush); - pThis->childRenderTarget->EndDraw(); - - brush->Release(); - EndPaint(hwnd, &ps); - return 0; - } - break; - } - } - } - } - return DefWindowProc(hwnd, uMsg, wParam, lParam); -}; } // namespace reflect \ No newline at end of file diff --git a/src/core/ui/Container.h b/src/core/ui/Container.h index 74913b0..0b7744c 100644 --- a/src/core/ui/Container.h +++ b/src/core/ui/Container.h @@ -6,17 +6,19 @@ namespace reflect { class Container : public Component { private: - static LRESULT CALLBACK containerProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam); - void registerClass(); + // static LRESULT CALLBACK containerProc(HWND hwnd, UINT uMsg, WPARAM wParam, + // LPARAM lParam); + // void registerClass(); HWND windowHWND = nullptr; std::vector componentsQueue; - ID2D1HwndRenderTarget *childRenderTarget = nullptr; + /// ID2D1HwndRenderTarget *childRenderTarget = nullptr; public: - REFLECT_API void render(HWND &parentHWND, HWND &windowHWND); + // REFLECT_API void render(HWND &parentHWND, HWND &windowHWND); REFLECT_API Container(); REFLECT_API void add(Component &comp, std::string id); REFLECT_API void add(Component &comp); + REFLECT_API void onPaint(); + REFLECT_API void onCreate(); }; } // namespace reflect \ No newline at end of file diff --git a/src/core/ui/TextComponent.cpp b/src/core/ui/TextComponent.cpp index 6ea520e..08712e2 100644 --- a/src/core/ui/TextComponent.cpp +++ b/src/core/ui/TextComponent.cpp @@ -26,6 +26,9 @@ void reflect::TextComponent::setText(std::string text_) { this->text = text_; } std::string reflect::TextComponent::getText() { return text; } void reflect::TextComponent::setFont(std::string f) { fontStr = f; } void reflect::TextComponent::setFontSize(int fs) { fontSize = fs; } +void reflect::TextComponent::setColor(uint8_t r, uint8_t g, uint8_t b) { + this->bgColor = Color3Float{r / 255.0f, g / 255.0f, b / 255.0f}; +} reflect::TextComponent::TextComponent() { initializeObject(REFLECT_OBJECT_TEXTCOMPONENT, REFLECT_OBJECT_COMPONENT); } diff --git a/src/core/ui/Vector2.cpp b/src/core/ui/Vector2.cpp index 4ad90c1..d7b05ac 100644 --- a/src/core/ui/Vector2.cpp +++ b/src/core/ui/Vector2.cpp @@ -83,4 +83,57 @@ Vector2 Vector2::operator|(const Vector2 &vec) const noexcept { Vector2::operator std::vector() const noexcept { return std::vector{x, y}; } + +// Vector2Float +Vector2Float32::Vector2Float32() = default; + +Vector2Float32::Vector2Float32(float x, float y) : x(x), y(y) {} + +Vector2Float32 +Vector2Float32::operator+(const Vector2Float32 &vec) const noexcept { + return Vector2Float32(this->x + vec.x, this->y + vec.y); +} + +Vector2Float32 +Vector2Float32::operator-(const Vector2Float32 &vec) const noexcept { + return Vector2Float32(this->x - vec.x, this->y - vec.y); +} + +Vector2Float32 +Vector2Float32::operator*(const Vector2Float32 &vec) const noexcept { + return Vector2Float32(this->x * vec.x, this->y * vec.y); +} + +Vector2Float32 +Vector2Float32::operator/(const Vector2Float32 &vec) const noexcept { + return Vector2Float32(this->x / vec.x, this->y / vec.y); +} + +Vector2Float32 &Vector2Float32::operator+=(const Vector2Float32 &vec) noexcept { + this->x += vec.x; + this->y += vec.y; + return *this; +} + +Vector2Float32 &Vector2Float32::operator-=(const Vector2Float32 &vec) noexcept { + this->x -= vec.x; + this->y -= vec.y; + return *this; +} + +Vector2Float32 &Vector2Float32::operator*=(const Vector2Float32 &vec) noexcept { + this->x *= vec.x; + this->y *= vec.y; + return *this; +} + +Vector2Float32 &Vector2Float32::operator/=(const Vector2Float32 &vec) noexcept { + this->x /= vec.x; + this->y /= vec.y; + return *this; +} + +Vector2Float32::operator std::vector() const noexcept { + return std::vector{x, y}; +} } // namespace reflect \ No newline at end of file diff --git a/src/core/ui/Vector2.h b/src/core/ui/Vector2.h index e7cd136..5168034 100644 --- a/src/core/ui/Vector2.h +++ b/src/core/ui/Vector2.h @@ -50,5 +50,27 @@ struct Vector2 { REFLECT_API explicit operator std::vector() const noexcept; int x, y; }; +struct Vector2Float32 { +public: + REFLECT_API Vector2Float32(); + REFLECT_API Vector2Float32(float x, float y); + + // Arithmetic operators + REFLECT_API Vector2Float32 + operator+(const Vector2Float32 &vec) const noexcept; + REFLECT_API Vector2Float32 + operator-(const Vector2Float32 &vec) const noexcept; + REFLECT_API Vector2Float32 + operator*(const Vector2Float32 &vec) const noexcept; + REFLECT_API Vector2Float32 + operator/(const Vector2Float32 &vec) const noexcept; + // Compound assignment + REFLECT_API Vector2Float32 &operator+=(const Vector2Float32 &vec) noexcept; + REFLECT_API Vector2Float32 &operator-=(const Vector2Float32 &vec) noexcept; + REFLECT_API Vector2Float32 &operator*=(const Vector2Float32 &vec) noexcept; + REFLECT_API Vector2Float32 &operator/=(const Vector2Float32 &vec) noexcept; + REFLECT_API explicit operator std::vector() const noexcept; + float x, y; +}; } // namespace reflect diff --git a/src/core/ui/Window.cpp b/src/core/ui/Window.cpp index 99997ca..a16195c 100644 --- a/src/core/ui/Window.cpp +++ b/src/core/ui/Window.cpp @@ -33,6 +33,7 @@ #include #ifdef _WIN32 #include "Notification.h" +#include #include #include #include @@ -42,6 +43,9 @@ #pragma comment(lib, "dwmapi.lib") using namespace reflect::console; +namespace { +ID2D1Factory *pFactory = nullptr; +} // Direct2D-specific members // Due to floating point operations, may not produce exact color void reflect::Window::addStyle(WindowStyle style) { @@ -100,28 +104,28 @@ void reflect::Window::setColor(Color3Array color) { this->bgColor[1] = color[1] / 255.0f; this->bgColor[2] = color[2] / 255.0f; } -void reflect::Window::setRenderLoop(void (*loop)(Window &)) { +void reflect::Window::setRenderLoop(std::function loop) { renderLoop = loop; } void reflect::Window::initializeDirect2D() { - if (!this->pFactory) { + + if (!::pFactory) { reflectDebug("initializing Direct2D..."); // Create the Direct2D factory reflectDebug("creating d2d1 factory..."); HRESULT hr = - D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &this->pFactory); - - int i = 0; + D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &::pFactory); } + this->pFactory = ::pFactory; if (!this->pRenderTarget) { RECT rc; GetClientRect(hwnd, &rc); // Create the render target reflectDebug("creating renderer target..."); - this->pFactory->CreateHwndRenderTarget( + ::pFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties( hwnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)), @@ -741,11 +745,11 @@ reflect::Window::~Window() { pRenderTarget->Release(); pRenderTarget = nullptr; } - if (pFactory) { - pFactory->Release(); - pFactory = nullptr; - } - // set to nullptr to avoid dangling pointers + // if (pFactory) { + // pFactory->Release(); + // pFactory = nullptr; + // } + // set to nullptr to avoid dangling pointers } void reflect::Window::setTitle(std::string title) { reflectDebug("window title set"); @@ -789,7 +793,8 @@ int reflect::Window::run(void (*func)(Window &win)) { //} } - if (customPipeline && openglRendering && !renderThread) + else if (customPipeline && openglRendering && !renderThread) { + renderThread = new std::thread([this, func, msg]() { while (msg.message != WM_QUIT) { if (!renderHdc) { @@ -813,6 +818,9 @@ int reflect::Window::run(void (*func)(Window &win)) { // } } }); + } else if (direct2dRendering && renderLoop) { + renderLoop(*this); + } /*if (openglRendering && currentContext != nullptr && customPipeline && renderLoop) { renderLoop(*this); @@ -829,7 +837,8 @@ int reflect::Window::run(void (*func)(Window &win)) { } return static_cast(msg.wParam); } -void reflect::Window::setBeforeRenderLoop(void (*callback)(Window &)) { +void reflect::Window::setBeforeRenderLoop( + std::function callback) { this->beforeRenderLoop = callback; } void reflect::Window::swapBuffers() { diff --git a/src/core/ui/Window.h b/src/core/ui/Window.h index 7063856..7c8c174 100644 --- a/src/core/ui/Window.h +++ b/src/core/ui/Window.h @@ -51,8 +51,8 @@ class Window : public Component { OpenGLContext *glCtx; bool closeHovering; bool customTitleBar; - void (*renderLoop)(Window &); - void (*beforeRenderLoop)(Window &); + std::function renderLoop; + std::function beforeRenderLoop; protected: HINSTANCE winstance; @@ -91,7 +91,7 @@ class Window : public Component { bool callInit; #ifdef _WIN32 ID2D1HwndRenderTarget *pRenderTarget = nullptr; - ID2D1Factory *pFactory = nullptr; + ID2D1Factory *pFactory; IDWriteFactory *pDWriteFactory = nullptr; static LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -114,13 +114,14 @@ class Window : public Component { std::any anyType = getPropertyMap()[property](); return std::any_cast(anyType); }; - REFLECT_API void setBeforeRenderLoop(void (*callback)(Window &)); + REFLECT_API void setBeforeRenderLoop(std::function callback); REFLECT_API void swapBuffers(); REFLECT_API bool isKeyPressed(char key); REFLECT_API void add(Component &comp, std::string id); REFLECT_API bool showNotification(reflect::Notification ¬if); REFLECT_API int run(void (*func)(Window &win)); - REFLECT_API void setRenderLoop(void (*loop)(Window &)); + REFLECT_API void + setRenderLoop(std::function beforeRenderLoop); REFLECT_API void close(); REFLECT_API operator HWND() const; REFLECT_API Window(const Window &) = delete; diff --git a/src/core/xml/ProcessorRegistry.cpp b/src/core/xml/ProcessorRegistry.cpp index 5bbac0e..356b416 100644 --- a/src/core/xml/ProcessorRegistry.cpp +++ b/src/core/xml/ProcessorRegistry.cpp @@ -1,5 +1,6 @@ #include "ProcessorRegistry.h" #include "../Main.h" +#include "Console.h" #include "core/ui/Button.h" #include "core/ui/Container.h" #include "core/ui/Image.h" @@ -93,6 +94,26 @@ std::unordered_map ProcessorRegistry::mapOfProcessors = Vector2 position(std::stoi(container->Attribute("x")), std::stoi(container->Attribute("y"))); Container *containerComp = new Container(); + + std::string bgColor = container->Attribute("bgColor"); + reflectDebug("setting window background color..."); + if (bgColor == "systemDefault") { + containerComp->setColor(255, 255, 255); + } else { + if (bgColor.at(0) == '#' && bgColor.length() == 7) { + + const std::string hex = bgColor.substr(1); + + const uint8_t r = stoi(hex.substr(0, 2), nullptr, 16); + const uint8_t g = stoi(hex.substr(2, 2), nullptr, 16); + const uint8_t b = stoi(hex.substr(4, 2), nullptr, 16); + const Color3 color = {r, g, b}; + containerComp->setColor(color); + } else { + reflectError("invalid hex color representation"); + std::exit(REFLECT_ERROR_HEX_COLOR_MALFORMED); + } + } containerComp->setSize( Vector2(std::stoi(container->Attribute("width")), std::stoi(container->Attribute("height")))); @@ -106,7 +127,8 @@ std::unordered_map ProcessorRegistry::mapOfProcessors = reflect::ProcessorRegistry::invokeProcessor(element->Name(), win, element); containerComp->add(comp.first, comp.second); - addToHeap(&comp); + Component *ptr = &comp.first; + addToHeap(ptr); } return {*containerComp, id}; }}}; diff --git a/src/net/HttpRequest.cpp b/src/net/HttpRequest.cpp deleted file mode 100644 index 980e7c3..0000000 --- a/src/net/HttpRequest.cpp +++ /dev/null @@ -1,464 +0,0 @@ -#include "HttpRequest.h" -#include "Console.h" -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -/** - * @brief Internal namespace, used for private functions and variables. - */ -namespace { -/** - * @brief Extracts the host and port from a URL. - * @param url The URL to extract from. - * @return A tuple containing the host and port. - */ -std::tuple extractHostAndPort(const std::string &url) { - // Regular expression to match the URL structure - std::regex urlRegex(R"(^(https?)://([^:/]+)(?::(\d+))?(/.*)?$)"); - std::smatch matches; - - if (std::regex_match(url, matches, urlRegex)) { - // Extract protocol (http or https) - std::string protocol = matches[1]; - // Extract hostname - std::string host = matches[2]; - // Extract port if available, otherwise use default port - int port = (matches[3].length() > 0) ? std::stoi(matches[3]) - : (protocol == "https" ? 443 : 80); - return std::make_tuple(host, port); - } else { - // If the URL doesn't have the protocol, treat it as "http" - return std::make_tuple(url, 80); - } -} -SSL_CTX *ctx; -SSL *ssl; - -/** - * @brief Initializes the socket and SSL library. - */ -class SocketInitializer { -public: - SocketInitializer() { - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - reflectError("failed to initialize winsock"); - exit(1); - } - SSL_library_init(); - ctx = SSL_CTX_new(TLS_client_method()); - ssl = SSL_new(ctx); - } - ~SocketInitializer() { - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ctx); - WSACleanup(); - } -}; -// Initialize the socket library and SSL context -SocketInitializer internalInitializer; -} // namespace -namespace reflect { -HttpRequest::HttpRequest(const std::string &url, HttpRequestMethod method, - const std::string &headers, const std::string &body) - : url(url), method(method), headers(headers), body(body), success(false) {} -void HttpRequest::setUrl(const std::string &newUrl) { url = newUrl; } -void HttpRequest::setMethod(HttpRequestMethod newMethod) { method = newMethod; } -void HttpRequest::setHeaders(const std::string &newHeaders) { - headers = newHeaders; -} -bool HttpRequest::isSuccess() const { return success; } -void HttpRequest::setBody(const std::string newBody) { body = newBody; } -std::string HttpRequest::getUrl() const { return url; } -HttpRequestMethod HttpRequest::getMethod() const { return method; } -std::string HttpRequest::getHeaders() const { return headers; } -std::string HttpRequest::getBody() const { return body; } - -std::string HttpRequest::getResponse() { return response; } -void HttpRequest::initiateRequest() { - success = true; - SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) { - success = false; - return; - } - - std::tuple values = extractHostAndPort(url); - std::string host = std::get<0>(values); - uint16_t port = std::get<1>(values); - - struct addrinfo hints, *result = nullptr; - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - int iResult = - getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &result); - if (iResult != 0) { - closesocket(sock); - success = false; - return; - } - - // Attempt to connect to an address until one succeeds - for (struct addrinfo *ptr = result; ptr != nullptr; ptr = ptr->ai_next) { - iResult = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen); - if (iResult == SOCKET_ERROR) { - closesocket(sock); - sock = INVALID_SOCKET; - continue; - } - break; - } - - freeaddrinfo(result); - - if (sock == INVALID_SOCKET) { - std::cerr << "Unable to connect to server." << std::endl; - WSACleanup(); - return; - } - - std::stringstream request; - // Prepare the GET request - request << "GET " + - std::regex_replace(url, std::regex("^[a-zA-Z]+://[^/]+"), "") + - " HTTP/1.1\r\n" - << "Host: " + host + "\r\n" - << "Connection: close\r\n" - << "\r\n"; - if (url.rfind("https://", 0) == 0) { - // If the URL starts with "https://", use SSL - if (SSL_set_fd(ssl, sock) == 0) { - reflectError("cannot set socket with SSL!"); - success = false; - closesocket(sock); - return; - } - if (SSL_connect(ssl) <= 0) { - reflectError("SSL connection failed!"); - success = false; - closesocket(sock); - return; - } - SSL_write(ssl, request.str().c_str(), strlen(request.str().c_str())); - char buffer[1024]; - int bytesReceived; - std::string data; - while ((bytesReceived = SSL_read(ssl, buffer, sizeof(buffer) - 1)) > 0) { - buffer[bytesReceived] = '\0'; // Null-terminate the string - data.append(buffer); - std::memset(buffer, 0, sizeof(buffer)); - } - this->response = data; - closesocket(sock); - this->response = data; - } else { - - // Send the GET request - if (send(sock, request.str().c_str(), request.str().length(), 0) == - SOCKET_ERROR) { - success = false; - closesocket(sock); - WSACleanup(); - return; - } - - int bytesReceived; - char buffer[1024]; - std::string data; - // Receive the response - while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) { - buffer[bytesReceived] = '\0'; // Null-terminate the string - data.append(buffer); - std::memset(buffer, 0, sizeof(buffer)); - } - - if (bytesReceived == SOCKET_ERROR) { - success = false; - } - - // Close the socket - closesocket(sock); - this->response = data; - } -} -// void HttpRequest::initiateRequest() { -// SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -// struct sockaddr_in serverAddr; -// struct hostent *host; -// if (sock == INVALID_SOCKET) { -// std::cerr << "Socket creation failed." << std::endl; -// WSACleanup(); -// return; -// } -// std::tuple values = extractHostAndPort(url); -// // Resolve the server address and port -// host = gethostbyname(std::get<0>(values).c_str()); -// -// if (host == NULL) { -// std::cerr << "Host not found." << std::endl; -// closesocket(sock); -// WSACleanup(); -// return; -// } -// -// // Fill in server address -// serverAddr.sin_family = AF_INET; -// serverAddr.sin_port = htons(std::get<1>(values)); // HTTP port -// serverAddr.sin_addr.s_addr = *(unsigned long *)host->h_addr_list[0]; -// -// // Connect to the server -// if (connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -// SOCKET_ERROR) { -// std::cerr << "Connection failed." << std::endl; -// closesocket(sock); -// WSACleanup(); -// return; -// } -// std::stringstream request; -// // Prepare the GET request -// request << "GET " + std::regex_replace(url, -// std::regex("^[a-zA-Z]+://[^/]+"), "") + " HTTP/1.1\r\n" -// << "Host: " + std::get<0>(values) + "\r\n" -// << "Connection: close\r\n" -// << "\r\n"; -// -// // Send the GET request -// if (send(sock, request.str().c_str(), request.str().length(), 0) == -// SOCKET_ERROR) { -// std::cerr << "Send failed." << std::endl; -// closesocket(sock); -// WSACleanup(); -// return; -// } -// int bytesReceived; -// char buffer[1024]; -// std::string data; -// // Receive the response -// while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) { -// buffer[bytesReceived] = '\0'; // Null-terminate the string -// data.append(buffer); -// std::memset(buffer, 0, sizeof(buffer)); -// } -// -// if (bytesReceived == SOCKET_ERROR) { -// std::cerr << "Receive failed." << std::endl; -// } -// -// // Close the socket -// closesocket(sock); -// this->response = data; -// } // namespace reflect -} // namespace reflect - -#elif defined(__linux__) -#include "Console.h" -#include "HttpRequest.h" -#include // For htons -#include // For memset -#include // For getaddrinfo -#include -#include -#include -#include -#include -#include // For socket functions -#include -#include // For close() -#include - -/** - * @brief Internal namespace, used for private functions and variables. - */ -namespace { -/** - * @brief Extracts the host and port from a URL. - * @param url The URL to extract from. - * @return A tuple containing the host and port. - */ -std::tuple extractHostAndPort(const std::string &url) { - // Regular expression to match the URL structure - std::regex urlRegex(R"(^(https?)://([^:/]+)(?::(\d+))?(/.*)?$)"); - std::smatch matches; - - if (std::regex_match(url, matches, urlRegex)) { - // Extract protocol (http or https) - std::string protocol = matches[1]; - // Extract hostname - std::string host = matches[2]; - // Extract port if available, otherwise use default port - int port = (matches[3].length() > 0) ? std::stoi(matches[3]) - : (protocol == "https" ? 443 : 80); - return std::make_tuple(host, port); - } else { - // If the URL doesn't have the protocol, treat it as "http" - return std::make_tuple(url, 80); - } -} - -SSL_CTX *ctx; -SSL *ssl; - -/** - * @brief Initializes the SSL library. - */ -class SocketInitializer { -public: - SocketInitializer() { - SSL_library_init(); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - ctx = SSL_CTX_new(TLS_client_method()); - if (!ctx) { - reflectError("Failed to initialize SSL context"); - exit(1); - } - ssl = SSL_new(ctx); - } - ~SocketInitializer() { - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ctx); - } -}; -// Initialize the SSL context -SocketInitializer internalInitializer; -} // namespace - -namespace reflect { -HttpRequest::HttpRequest(const std::string &url, HttpRequestMethod method, - const std::string &headers, const std::string &body) - : url(url), method(method), headers(headers), body(body), success(false) {} - -void HttpRequest::setUrl(const std::string &newUrl) { url = newUrl; } -void HttpRequest::setMethod(HttpRequestMethod newMethod) { method = newMethod; } -void HttpRequest::setHeaders(const std::string &newHeaders) { - headers = newHeaders; -} -bool HttpRequest::isSuccess() const { return success; } -void HttpRequest::setBody(const std::string newBody) { body = newBody; } -std::string HttpRequest::getUrl() const { return url; } -HttpRequestMethod HttpRequest::getMethod() const { return method; } -std::string HttpRequest::getHeaders() const { return headers; } -std::string HttpRequest::getBody() const { return body; } - -std::string HttpRequest::getResponse() { return response; } - -void HttpRequest::initiateRequest() { - success = true; - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - reflectError("Socket creation failed"); - success = false; - return; - } - - std::tuple values = extractHostAndPort(url); - std::string host = std::get<0>(values); - uint16_t port = std::get<1>(values); - - struct addrinfo hints, *result = nullptr; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - int iResult = - getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &result); - if (iResult != 0) { - reflectError("Failed to resolve host"); - close(sock); - success = false; - return; - } - - // Attempt to connect to an address until one succeeds - for (struct addrinfo *ptr = result; ptr != nullptr; ptr = ptr->ai_next) { - iResult = connect(sock, ptr->ai_addr, ptr->ai_addrlen); - if (iResult == -1) { - close(sock); - sock = -1; - continue; - } - break; - } - - freeaddrinfo(result); - - if (sock == -1) { - reflectError("Unable to connect to server"); - return; - } - - std::stringstream request; - // Prepare the GET request - request << "GET " + - std::regex_replace(url, std::regex("^[a-zA-Z]+://[^/]+"), "") + - " HTTP/1.1\r\n" - << "Host: " + host + "\r\n" - << "Connection: close\r\n" - << "\r\n"; - - if (url.rfind("https://", 0) == 0) { - // If the URL starts with "https://", use SSL - if (SSL_set_fd(ssl, sock) == 0) { - reflectError("Cannot set socket with SSL!"); - success = false; - close(sock); - return; - } - if (SSL_connect(ssl) <= 0) { - reflectError("SSL connection failed!"); - success = false; - close(sock); - return; - } - SSL_write(ssl, request.str().c_str(), request.str().length()); - char buffer[1024]; - int bytesReceived; - std::string data; - while ((bytesReceived = SSL_read(ssl, buffer, sizeof(buffer) - 1)) > 0) { - buffer[bytesReceived] = '\0'; // Null-terminate the string - data.append(buffer); - memset(buffer, 0, sizeof(buffer)); - } - this->response = data; - close(sock); - } else { - // Send the GET request - if (send(sock, request.str().c_str(), request.str().length(), 0) == -1) { - reflectError("Failed to send request"); - success = false; - close(sock); - return; - } - - int bytesReceived; - char buffer[1024]; - std::string data; - // Receive the response - while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) { - buffer[bytesReceived] = '\0'; // Null-terminate the string - data.append(buffer); - memset(buffer, 0, sizeof(buffer)); - } - - if (bytesReceived == -1) { - reflectError("Failed to receive response"); - success = false; - } - - // Close the socket - close(sock); - this->response = data; - } -} -} // namespace reflect -#endif \ No newline at end of file diff --git a/src/net/HttpRequest.h b/src/net/HttpRequest.h deleted file mode 100644 index 07e825a..0000000 --- a/src/net/HttpRequest.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "TypeDefinitions.h" -#ifdef _WIN32 -#include -#include -#endif -#include - -namespace reflect { -enum HttpRequestMethod { HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_DELETE }; -class HttpRequest { -private: - std::string url; - HttpRequestMethod method; - std::string headers; - std::string body; - bool success; - std::string response; - -public: - REFLECT_API - HttpRequest(const std::string &url, - HttpRequestMethod method = HttpRequestMethod::HTTP_GET, - const std::string &headers = "", const std::string &body = ""); - REFLECT_API void setUrl(const std::string &newUrl); - REFLECT_API void setMethod(HttpRequestMethod newMethod); - REFLECT_API void setHeaders(const std::string &newHeaders); - REFLECT_API void setBody(const std::string body); - REFLECT_API std::string getUrl() const; - REFLECT_API HttpRequestMethod getMethod() const; - REFLECT_API std::string getHeaders() const; - REFLECT_API std::string getBody() const; - REFLECT_API void initiateRequest(); - REFLECT_API std::string getResponse(); - REFLECT_API std::string getErrorMessage() const; - REFLECT_API int getStatusCode() const; - REFLECT_API bool isSuccess() const; - REFLECT_API void setTimeout(int seconds); - REFLECT_API void setProxy(const std::string &proxyUrl); - REFLECT_API void setProxyPort(int port); - REFLECT_API void setProxyCredentials(const std::string &username, - const std::string &password); - REFLECT_API void setFollowRedirects(bool follow); - REFLECT_API void setUserAgent(const std::string &userAgent); - REFLECT_API void setAccept(const std::string &accept); - REFLECT_API void setContentType(const std::string &contentType); - REFLECT_API void setAcceptEncoding(const std::string &acceptEncoding); - REFLECT_API void setAcceptLanguage(const std::string &acceptLanguage); - REFLECT_API void setKeepAlive(bool keepAlive); -}; -} // namespace reflect \ No newline at end of file diff --git a/test/ButtonTest.cpp b/test/ButtonTest.cpp new file mode 100644 index 0000000..67a8387 --- /dev/null +++ b/test/ButtonTest.cpp @@ -0,0 +1,82 @@ +#include "../core/memory/HeapPool.h" +#include "../core/ui/Button.h" +#include "../core/ui/Window.h" +#include +#include +#include +#include + +namespace { + +class ButtonTest : public ::testing::Test { +protected: + reflect::Button *button; + + void SetUp() override { + reflect::initializeHeapPool(4 * 1024 * 1024); + button = new reflect::Button("Hello", {10, 20}); + } + + void TearDown() override { delete button; } +}; + +TEST_F(ButtonTest, ConstructorSetsTextAndPosition) { + EXPECT_EQ(button->getText(), "Hello"); + EXPECT_EQ(button->getPosition().x, 10); + EXPECT_EQ(button->getPosition().y, 20); +} + +TEST_F(ButtonTest, SetTextChangesText) { + button->setText("World"); + EXPECT_EQ(button->getText(), "World"); +} + +TEST_F(ButtonTest, SetFontAndFontSize) { + button->setFont("Arial"); + button->setFontSize(18); + // No direct getter, but should not crash +} + +TEST_F(ButtonTest, SetAndGetColor) { + button->setColor(100, 150, 200); + auto color = button->getColor(); + EXPECT_EQ(color.r, 100); + EXPECT_EQ(color.g, 150); + EXPECT_EQ(color.b, 200); +} + +TEST_F(ButtonTest, SetAndGetPosition) { + button->setPosition({30, 40}); + auto pos = button->getPosition(); + EXPECT_EQ(pos.x, 30); + EXPECT_EQ(pos.y, 40); +} + +TEST_F(ButtonTest, SetAndGetSize) { + button->setSize({120, 30}); + auto size = button->getSize(); + EXPECT_EQ(size.x, 120); + EXPECT_EQ(size.y, 30); +} + +TEST_F(ButtonTest, SetAndGetVisibility) { + button->setVisible(true); + EXPECT_FALSE(button->getVisible()); + button->setVisible(false); + EXPECT_FALSE(button->getVisible()); +} + +// Instrumental: Render Button in a window and check for crash +TEST(ButtonInstrumental, RenderButtonInWindow) { + HINSTANCE instance = GetModuleHandle(nullptr); + reflect::Window window(instance, "Buttontest"); + reflect::Button Button("Instrumental", {50, 50}); + window.add(Button, "Button"); + window.setVisible(true); + // Run the window for a short time, then close + + int result = window.run([](reflect::Window &win) { win.close(); }); + EXPECT_EQ(result, 0); +} + +} // namespace diff --git a/test/HttpTest.cpp b/test/HttpTest.cpp deleted file mode 100644 index dacb768..0000000 --- a/test/HttpTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "../net/HttpRequest.h" -#include -#include - -class HttpTestFixture : public ::testing::Test { -protected: - reflect::HttpRequest *httpRequest; - - void SetUp() override { - // Common setup logic for all tests - httpRequest = nullptr; - } - - void TearDown() override { - // Clean up resources - delete httpRequest; - } - - void initializeRequest(const std::string &url) { - httpRequest = new reflect::HttpRequest(url); - } -}; - -TEST_F(HttpTestFixture, HttpGetTest) { - initializeRequest("http://example.com"); - httpRequest->initiateRequest(); - EXPECT_TRUE(httpRequest->isSuccess()); -} - -TEST_F(HttpTestFixture, HttpsGetTest) { - initializeRequest("https://example.com"); - httpRequest->initiateRequest(); - EXPECT_TRUE(httpRequest->isSuccess()); -} - -TEST_F(HttpTestFixture, InvalidUrlTest) { - initializeRequest("invalid-url"); - httpRequest->initiateRequest(); - EXPECT_FALSE(httpRequest->isSuccess()); -} From a6fcd89a846e7271abda67f6c31bd3076abccec2 Mon Sep 17 00:00:00 2001 From: thebest12lines Date: Sat, 31 May 2025 20:50:13 +0600 Subject: [PATCH 3/4] Finish 1.9.0 --- src/api/java/ReflectJava.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/ReflectJava.c b/src/api/java/ReflectJava.c index 633d89f..e66b6a6 100644 --- a/src/api/java/ReflectJava.c +++ b/src/api/java/ReflectJava.c @@ -77,7 +77,7 @@ JNIEXPORT void JNICALL Java_reflect4j_ReflectNative_invoke(JNIEnv *env, (*env)->ReleaseStringUTFChars(env, location, utfChars); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - //MessageBox(NULL, "attach now", "debug", MB_OK); + // MessageBox(NULL, "attach now", "debug", MB_OK); HMODULE hModule = NULL; // JVM loads "reflect.dll" via System.loadLibrary("reflect") From 1f019ffbfa0d227889891b8a39a09f7bd5ac618d Mon Sep 17 00:00:00 2001 From: thebest12lines Date: Sat, 31 May 2025 21:12:19 +0600 Subject: [PATCH 4/4] Finalize and release --- src/common/Definitions.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/Definitions.h b/src/common/Definitions.h index 6e578cd..53c80a4 100644 --- a/src/common/Definitions.h +++ b/src/common/Definitions.h @@ -32,12 +32,16 @@ /** * @brief Internal application version for compatibility checks. */ -#define APP_INTERNAL_VERSION 0x0000000BL +#define APP_INTERNAL_VERSION 0x0000000CL /** * @brief External application version to be shown to the end user. */ -#define APP_VERSION "1.8.0" +#define APP_VERSION "1.9.0" + +#define REFLECT_VERSION_MAJOR 1 +#define REFLECT_VERSION_MINOR 9 +#define REFLECT_VERSION_PATCH 0 /** * @brief Error codes used in the Reflect framework.