diff options
-rw-r--r-- | CruUI.vcxproj | 5 | ||||
-rw-r--r-- | CruUI.vcxproj.filters | 21 | ||||
-rw-r--r-- | src/graph/graph.cpp | 367 | ||||
-rw-r--r-- | src/graph/graph.hpp | 322 | ||||
-rw-r--r-- | src/ui/render/flex_layout_render_object.cpp | 212 | ||||
-rw-r--r-- | src/ui/render/flex_layout_render_object.hpp | 55 | ||||
-rw-r--r-- | src/ui/render/linear_layout_render_object.cpp | 0 | ||||
-rw-r--r-- | src/ui/render/linear_layout_render_object.hpp | 1 | ||||
-rw-r--r-- | src/ui/render/render_object.cpp | 158 | ||||
-rw-r--r-- | src/ui/render/render_object.hpp | 133 | ||||
-rw-r--r-- | src/ui/ui_base.cpp | 6 | ||||
-rw-r--r-- | src/ui/ui_base.hpp | 537 |
12 files changed, 967 insertions, 850 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 9bedd3f8..bd321715 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -152,10 +152,9 @@ <ClCompile Include="src\ui\events\ui_event.cpp" /> <ClCompile Include="src\ui\input_util.cpp" /> <ClCompile Include="src\ui\layout_base.cpp" /> - <ClCompile Include="src\ui\render\linear_layout_render_object.cpp" /> + <ClCompile Include="src\ui\render\flex_layout_render_object.cpp" /> <ClCompile Include="src\ui\render\render_object.cpp" /> <ClCompile Include="src\ui\ui_manager.cpp" /> - <ClCompile Include="src\ui\ui_base.cpp" /> <ClCompile Include="src\ui\window.cpp" /> <ClInclude Include="src\util\string_util.hpp" /> </ItemGroup> @@ -182,7 +181,7 @@ <ClInclude Include="src\ui\events\ui_event.hpp" /> <ClInclude Include="src\ui\input_util.hpp" /> <ClInclude Include="src\ui\layout_base.hpp" /> - <ClInclude Include="src\ui\render\linear_layout_render_object.hpp" /> + <ClInclude Include="src\ui\render\flex_layout_render_object.hpp" /> <ClInclude Include="src\ui\render\render_object.hpp" /> <ClInclude Include="src\ui\ui_manager.hpp" /> <ClInclude Include="src\ui\ui_base.hpp" /> diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 9a85da3f..25923c76 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -36,9 +36,6 @@ <ClCompile Include="src\ui\layout_base.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\ui\ui_base.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="src\ui\window.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -93,9 +90,6 @@ <ClCompile Include="src\ui\input_util.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\ui\render\linear_layout_render_object.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="src\util\string_util.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -105,6 +99,9 @@ <ClCompile Include="src\ui\window_class.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\ui\render\flex_layout_render_object.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="src\graph\graph.hpp"> @@ -200,9 +197,6 @@ <ClInclude Include="src\ui\input_util.hpp"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="src\ui\render\linear_layout_render_object.hpp"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="src\util\format.hpp"> <Filter>Header Files</Filter> </ClInclude> @@ -218,6 +212,9 @@ <ClInclude Include="src\ui\window_class.hpp"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\ui\render\flex_layout_render_object.hpp"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="src\application.cpp"> @@ -241,9 +238,6 @@ <ClCompile Include="src\timer.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\ui\ui_base.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="src\ui\events\ui_event.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -262,9 +256,6 @@ <ClCompile Include="src\ui\animations\animation.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="src\base.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="src\ui\controls\button.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/src/graph/graph.cpp b/src/graph/graph.cpp index eef95c8c..ed3fe5d5 100644 --- a/src/graph/graph.cpp +++ b/src/graph/graph.cpp @@ -3,224 +3,159 @@ #include "application.hpp" #include "exception.hpp" -namespace cru::graph -{ - using Microsoft::WRL::ComPtr; - - WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) - { - this->graph_manager_ = graph_manager; - - const auto d3d11_device = graph_manager->GetD3D11Device(); - const auto dxgi_factory = graph_manager->GetDxgiFactory(); - - // Allocate a descriptor. - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { 0 }; - swap_chain_desc.Width = 0; // use automatic sizing - swap_chain_desc.Height = 0; - swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format - swap_chain_desc.Stereo = false; - swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.BufferCount = 2; // use double buffering to enable flip - swap_chain_desc.Scaling = DXGI_SCALING_NONE; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect - swap_chain_desc.Flags = 0; - - - - // Get the final swap chain for this window from the DXGI factory. - ThrowIfFailed( - dxgi_factory->CreateSwapChainForHwnd( - d3d11_device.Get(), - hwnd, - &swap_chain_desc, - nullptr, - nullptr, - &dxgi_swap_chain_ - ) - ); - - CreateTargetBitmap(); - } - - WindowRenderTarget::~WindowRenderTarget() - { - - } - - void WindowRenderTarget::ResizeBuffer(const int width, const int height) - { - const auto graph_manager = graph_manager_; - const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); - - ComPtr<ID2D1Image> old_target; - d2d1_device_context->GetTarget(&old_target); - const auto target_this = old_target == this->target_bitmap_; - if (target_this) - d2d1_device_context->SetTarget(nullptr); - - old_target = nullptr; - target_bitmap_ = nullptr; - - ThrowIfFailed( - dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0) - ); - - CreateTargetBitmap(); - - if (target_this) - d2d1_device_context->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::SetAsTarget() - { - GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::Present() - { - ThrowIfFailed( - dxgi_swap_chain_->Present(1, 0) - ); - } - - void WindowRenderTarget::CreateTargetBitmap() - { - // Direct2D needs the dxgi version of the backbuffer surface pointer. - ComPtr<IDXGISurface> dxgiBackBuffer; - ThrowIfFailed( - dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer)) - ); - - const auto dpi = graph_manager_->GetDpi(); - - auto bitmap_properties = - D2D1::BitmapProperties1( - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, - D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), - dpi.x, - dpi.y - ); - - // Get a D2D surface from the DXGI back buffer to use as the D2D render target. - ThrowIfFailed( - graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( - dxgiBackBuffer.Get(), - &bitmap_properties, - &target_bitmap_ - ) - ); - } - - GraphManager* GraphManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<GraphManager>([](auto) - { - return new GraphManager{}; - }); - } - - GraphManager::GraphManager() - { - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +namespace cru::graph { +using Microsoft::WRL::ComPtr; + +WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) { + this->graph_manager_ = graph_manager; + + const auto d3d11_device = graph_manager->GetD3D11Device(); + const auto dxgi_factory = graph_manager->GetDxgiFactory(); + + // Allocate a descriptor. + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {0}; + swap_chain_desc.Width = 0; // use automatic sizing + swap_chain_desc.Height = 0; + swap_chain_desc.Format = + DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format + swap_chain_desc.Stereo = false; + swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = 2; // use double buffering to enable flip + swap_chain_desc.Scaling = DXGI_SCALING_NONE; + swap_chain_desc.SwapEffect = + DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect + swap_chain_desc.Flags = 0; + + // Get the final swap chain for this window from the DXGI factory. + ThrowIfFailed(dxgi_factory->CreateSwapChainForHwnd( + d3d11_device.Get(), hwnd, &swap_chain_desc, nullptr, nullptr, + &dxgi_swap_chain_)); + + CreateTargetBitmap(); +} + +WindowRenderTarget::~WindowRenderTarget() {} + +void WindowRenderTarget::ResizeBuffer(const int width, const int height) { + const auto graph_manager = graph_manager_; + const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); + + ComPtr<ID2D1Image> old_target; + d2d1_device_context->GetTarget(&old_target); + const auto target_this = old_target == this->target_bitmap_; + if (target_this) d2d1_device_context->SetTarget(nullptr); + + old_target = nullptr; + target_bitmap_ = nullptr; + + ThrowIfFailed(dxgi_swap_chain_->ResizeBuffers(0, width, height, + DXGI_FORMAT_UNKNOWN, 0)); + + CreateTargetBitmap(); + + if (target_this) d2d1_device_context->SetTarget(target_bitmap_.Get()); +} + +void WindowRenderTarget::SetAsTarget() { + GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); +} + +void WindowRenderTarget::Present() { + ThrowIfFailed(dxgi_swap_chain_->Present(1, 0)); +} + +void WindowRenderTarget::CreateTargetBitmap() { + // Direct2D needs the dxgi version of the backbuffer surface pointer. + ComPtr<IDXGISurface> dxgiBackBuffer; + ThrowIfFailed(dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))); + + const auto dpi = graph_manager_->GetDpi(); + + auto bitmap_properties = D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), + dpi.x, dpi.y); + + // Get a D2D surface from the DXGI back buffer to use as the D2D render + // target. + ThrowIfFailed( + graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( + dxgiBackBuffer.Get(), &bitmap_properties, &target_bitmap_)); +} + +GraphManager* GraphManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton<GraphManager>( + [](auto) { return new GraphManager{}; }); +} + +GraphManager::GraphManager() { + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #ifdef CRU_DEBUG - creation_flags |= D3D11_CREATE_DEVICE_DEBUG; + creation_flags |= D3D11_CREATE_DEVICE_DEBUG; #endif - const D3D_FEATURE_LEVEL feature_levels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - - ThrowIfFailed(D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - creation_flags, - feature_levels, - ARRAYSIZE(feature_levels), - D3D11_SDK_VERSION, - &d3d11_device_, - nullptr, - &d3d11_device_context_ - )); - - Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; - - ThrowIfFailed(d3d11_device_.As(&dxgi_device)); - - ThrowIfFailed(D2D1CreateFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory1), - &d2d1_factory_ - )); - - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); - - ThrowIfFailed(d2d1_device_->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - &d2d1_device_context_ - )); - - // Identify the physical adapter (GPU or card) this device is runs on. - ComPtr<IDXGIAdapter> dxgi_adapter; - ThrowIfFailed( - dxgi_device->GetAdapter(&dxgi_adapter) - ); - - // Get the factory object that created the DXGI device. - ThrowIfFailed( - dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_)) - ); - - - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(dwrite_factory_.GetAddressOf()) - )); - - dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); - } - - GraphManager::~GraphManager() - { - - } - - std::shared_ptr<WindowRenderTarget> GraphManager::CreateWindowRenderTarget(HWND hwnd) - { - return std::make_shared<WindowRenderTarget>(this, hwnd); - } - - Dpi GraphManager::GetDpi() const - { - Dpi dpi; - d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); - return dpi; - } - - void GraphManager::ReloadSystemMetrics() - { - ThrowIfFailed( - d2d1_factory_->ReloadSystemMetrics() - ); - } - - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush(const D2D1_COLOR_F& color) - { - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush; - ThrowIfFailed(GraphManager::GetInstance()->GetD2D1DeviceContext()->CreateSolidColorBrush(color, &brush)); - return brush; - } + const D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + + ThrowIfFailed(D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, + feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, + &d3d11_device_, nullptr, &d3d11_device_context_)); + + Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; + + ThrowIfFailed(d3d11_device_.As(&dxgi_device)); + + ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory1), &d2d1_factory_)); + + ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); + + ThrowIfFailed(d2d1_device_->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context_)); + + // Identify the physical adapter (GPU or card) this device is runs on. + ComPtr<IDXGIAdapter> dxgi_adapter; + ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); + + // Get the factory object that created the DXGI device. + ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); + + ThrowIfFailed(DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(dwrite_factory_.GetAddressOf()))); + + dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); +} + +GraphManager::~GraphManager() {} + +std::shared_ptr<WindowRenderTarget> GraphManager::CreateWindowRenderTarget( + HWND hwnd) { + return std::make_shared<WindowRenderTarget>(this, hwnd); +} + +Dpi GraphManager::GetDpi() const { + Dpi dpi; + d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); + return dpi; +} + +void GraphManager::ReloadSystemMetrics() { + ThrowIfFailed(d2d1_factory_->ReloadSystemMetrics()); +} + +Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush( + const D2D1_COLOR_F& color) { + Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush; + ThrowIfFailed(GraphManager::GetInstance() + ->GetD2D1DeviceContext() + ->CreateSolidColorBrush(color, &brush)); + return brush; } +} // namespace cru::graph diff --git a/src/graph/graph.hpp b/src/graph/graph.hpp index 440b0594..bad5b6d0 100644 --- a/src/graph/graph.hpp +++ b/src/graph/graph.hpp @@ -1,180 +1,160 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" -#include <memory> #include <functional> +#include <memory> +#include "system_headers.hpp" #include "base.hpp" +namespace cru::graph { +class GraphManager; + +// Represents a window render target. +class WindowRenderTarget : public Object { + public: + WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); + WindowRenderTarget(const WindowRenderTarget& other) = delete; + WindowRenderTarget(WindowRenderTarget&& other) = delete; + WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; + WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; + ~WindowRenderTarget() override; + + public: + // Get the graph manager that created the render target. + GraphManager* GetGraphManager() const { return graph_manager_; } + + // Get the d2d device context. + inline Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2DDeviceContext() const; + + // Get the target bitmap which can be set as the ID2D1DeviceContext's target. + Microsoft::WRL::ComPtr<ID2D1Bitmap1> GetTargetBitmap() const { + return target_bitmap_; + } + + // Resize the underlying buffer. + void ResizeBuffer(int width, int height); + + // Set this render target as the d2d device context's target. + void SetAsTarget(); + + // Present the data of the underlying buffer to the window. + void Present(); + + private: + void CreateTargetBitmap(); + + private: + GraphManager* graph_manager_; + Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgi_swap_chain_; + Microsoft::WRL::ComPtr<ID2D1Bitmap1> target_bitmap_; +}; + +struct Dpi { + float x; + float y; +}; + +class GraphManager final : public Object { + public: + static GraphManager* GetInstance(); + + private: + GraphManager(); + + public: + GraphManager(const GraphManager& other) = delete; + GraphManager(GraphManager&& other) = delete; + GraphManager& operator=(const GraphManager& other) = delete; + GraphManager& operator=(GraphManager&& other) = delete; + ~GraphManager() override; + + public: + Microsoft::WRL::ComPtr<ID2D1Factory1> GetD2D1Factory() const { + return d2d1_factory_; + } + + Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2D1DeviceContext() const { + return d2d1_device_context_; + } + + Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const { + return d3d11_device_; + } + + Microsoft::WRL::ComPtr<IDXGIFactory2> GetDxgiFactory() const { + return dxgi_factory_; + } + + Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory() const { + return dwrite_factory_; + } + + // Create a window render target with the HWND. + std::shared_ptr<WindowRenderTarget> CreateWindowRenderTarget(HWND hwnd); + + // Get the desktop dpi. + Dpi GetDpi() const; + + // Reload system metrics including desktop dpi. + void ReloadSystemMetrics(); + + Microsoft::WRL::ComPtr<IDWriteFontCollection> GetSystemFontCollection() + const { + return dwrite_system_font_collection_.Get(); + } + + private: + Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; + Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context_; + Microsoft::WRL::ComPtr<ID2D1Factory1> d2d1_factory_; + Microsoft::WRL::ComPtr<ID2D1Device> d2d1_device_; + Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1_device_context_; + Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory_; + + Microsoft::WRL::ComPtr<IDWriteFactory> dwrite_factory_; + Microsoft::WRL::ComPtr<IDWriteFontCollection> dwrite_system_font_collection_; +}; + +inline int DipToPixelInternal(const float dip, const float dpi) { + return static_cast<int>(dip * dpi / 96.0f); +} + +inline int DipToPixelX(const float dip_x) { + return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); +} + +inline int DipToPixelY(const float dip_y) { + return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); +} -namespace cru::graph -{ - class GraphManager; - - //Represents a window render target. - class WindowRenderTarget : public Object - { - public: - WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); - WindowRenderTarget(const WindowRenderTarget& other) = delete; - WindowRenderTarget(WindowRenderTarget&& other) = delete; - WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; - WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; - ~WindowRenderTarget() override; - - public: - //Get the graph manager that created the render target. - GraphManager* GetGraphManager() const - { - return graph_manager_; - } - - //Get the d2d device context. - inline Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2DDeviceContext() const; - - //Get the target bitmap which can be set as the ID2D1DeviceContext's target. - Microsoft::WRL::ComPtr<ID2D1Bitmap1> GetTargetBitmap() const - { - return target_bitmap_; - } - - //Resize the underlying buffer. - void ResizeBuffer(int width, int height); - - //Set this render target as the d2d device context's target. - void SetAsTarget(); - - //Present the data of the underlying buffer to the window. - void Present(); - - private: - void CreateTargetBitmap(); - - private: - GraphManager* graph_manager_; - Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgi_swap_chain_; - Microsoft::WRL::ComPtr<ID2D1Bitmap1> target_bitmap_; - }; - - struct Dpi - { - float x; - float y; - }; - - class GraphManager final : public Object - { - public: - static GraphManager* GetInstance(); - - private: - GraphManager(); - public: - GraphManager(const GraphManager& other) = delete; - GraphManager(GraphManager&& other) = delete; - GraphManager& operator=(const GraphManager& other) = delete; - GraphManager& operator=(GraphManager&& other) = delete; - ~GraphManager() override; - - public: - Microsoft::WRL::ComPtr<ID2D1Factory1> GetD2D1Factory() const - { - return d2d1_factory_; - } - - Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2D1DeviceContext() const - { - return d2d1_device_context_; - } - - Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const - { - return d3d11_device_; - } - - Microsoft::WRL::ComPtr<IDXGIFactory2> GetDxgiFactory() const - { - return dxgi_factory_; - } - - Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory() const - { - return dwrite_factory_; - } - - - //Create a window render target with the HWND. - std::shared_ptr<WindowRenderTarget> CreateWindowRenderTarget(HWND hwnd); - - //Get the desktop dpi. - Dpi GetDpi() const; - - //Reload system metrics including desktop dpi. - void ReloadSystemMetrics(); - - Microsoft::WRL::ComPtr<IDWriteFontCollection> GetSystemFontCollection() const - { - return dwrite_system_font_collection_.Get(); - } - - private: - Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; - Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context_; - Microsoft::WRL::ComPtr<ID2D1Factory1> d2d1_factory_; - Microsoft::WRL::ComPtr<ID2D1Device> d2d1_device_; - Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1_device_context_; - Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory_; - - Microsoft::WRL::ComPtr<IDWriteFactory> dwrite_factory_; - Microsoft::WRL::ComPtr<IDWriteFontCollection> dwrite_system_font_collection_; - }; - - inline int DipToPixelInternal(const float dip, const float dpi) - { - return static_cast<int>(dip * dpi / 96.0f); - } - - inline int DipToPixelX(const float dip_x) - { - return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline int DipToPixelY(const float dip_y) - { - return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); - } - - inline float DipToPixelInternal(const int pixel, const float dpi) - { - return static_cast<float>(pixel) * 96.0f / dpi; - } - - inline float PixelToDipX(const int pixel_x) - { - return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline float PixelToDipY(const int pixel_y) - { - return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); - } - - Microsoft::WRL::ComPtr<ID2D1DeviceContext> WindowRenderTarget::GetD2DDeviceContext() const - { - return graph_manager_->GetD2D1DeviceContext(); - } - - inline void WithTransform(ID2D1DeviceContext* device_context, const D2D1_MATRIX_3X2_F matrix, const std::function<void(ID2D1DeviceContext*)>& action) - { - D2D1_MATRIX_3X2_F old_transform; - device_context->GetTransform(&old_transform); - device_context->SetTransform(old_transform * matrix); - action(device_context); - device_context->SetTransform(old_transform); - } - - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush(const D2D1_COLOR_F& color); +inline float DipToPixelInternal(const int pixel, const float dpi) { + return static_cast<float>(pixel) * 96.0f / dpi; } + +inline float PixelToDipX(const int pixel_x) { + return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); +} + +inline float PixelToDipY(const int pixel_y) { + return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); +} + +Microsoft::WRL::ComPtr<ID2D1DeviceContext> +WindowRenderTarget::GetD2DDeviceContext() const { + return graph_manager_->GetD2D1DeviceContext(); +} + +inline void WithTransform( + ID2D1RenderTarget* device_context, const D2D1_MATRIX_3X2_F matrix, + const std::function<void(ID2D1RenderTarget*)>& action) { + D2D1_MATRIX_3X2_F old_transform; + device_context->GetTransform(&old_transform); + device_context->SetTransform(old_transform * matrix); + action(device_context); + device_context->SetTransform(old_transform); +} + +Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush( + const D2D1_COLOR_F& color); +} // namespace cru::graph diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp new file mode 100644 index 00000000..4e5171a2 --- /dev/null +++ b/src/ui/render/flex_layout_render_object.cpp @@ -0,0 +1,212 @@ +#include "flex_layout_render_object.hpp" + +#include <algorithm> +#include <functional> + +#include "cru_debug.hpp" +#include "graph/graph.hpp" + +namespace cru::ui::render { +FlexChildLayoutData* FlexLayoutRenderObject::GetChildLayoutData(int position) { + if (position < 0 || position >= child_layout_data_.size()) + throw std::invalid_argument("Position out of bound."); + + return &child_layout_data_[position]; +} + +void FlexLayoutRenderObject::Draw(ID2D1RenderTarget* render_target) { + for (const auto child : GetChildren()) { + auto offset = child->GetOffset(); + graph::WithTransform(render_target, + D2D1::Matrix3x2F::Translation(offset.x, offset.y), + [child](auto rt) { child->Draw(rt); }); + } +} + +RenderObject* FlexLayoutRenderObject::HitTest(const Point& point) { + const auto& children = GetChildren(); + for (auto i = children.crbegin(); i != children.crend(); ++i) { + auto offset = (*i)->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = (*i)->HitTest(point); + if (result != nullptr) { + return result; + } + } + return Rect{Point::Zero(), GetSize()}.IsPointInside(point) ? this : nullptr; +} + +void FlexLayoutRenderObject::OnAddChild(RenderObject* new_child, int position) { + child_layout_data_.emplace(child_layout_data_.cbegin() + position); +} + +void FlexLayoutRenderObject::OnRemoveChild(RenderObject* removed_child, + int position) { + child_layout_data_.erase(child_layout_data_.cbegin() + position); +} + +Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) { + std::vector<int> has_basis_children; + std::vector<int> no_basis_children; + std::vector<int> grow_children; + std::vector<int> shrink_chilren; + for (int i = 0; i < child_layout_data_.size(); i++) { + const auto& layout_data = child_layout_data_[i]; + if (layout_data.flex_basis.has_value()) + has_basis_children.push_back(i); + else + no_basis_children.push_back(i); + if (layout_data.flex_grow > 0) grow_children.push_back(i); + if (layout_data.flex_shrink > 0) shrink_chilren.push_back(i); + } + + std::function<float(const Size&)> get_main_length; + std::function<float(const Size&)> get_cross_length; + std::function<Size(float main, float cross)> create_size; + + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + get_main_length = [](const Size& size) { return size.width; }; + get_cross_length = [](const Size& size) { return size.height; }; + create_size = [](float main, float cross) { return Size(main, cross); }; + } else { + get_main_length = [](const Size& size) { return size.height; }; + get_cross_length = [](const Size& size) { return size.width; }; + create_size = [](float main, float cross) { return Size(cross, main); }; + } + + const auto& children = GetChildren(); + + float remain_main_length = get_main_length(available_size); + float max_cross_length = 0; + + for (const int i : has_basis_children) { + const auto child = children[i]; + const float basis = child_layout_data_[i].flex_basis.value(); + child->Measure(create_size(basis, get_cross_length(available_size))); + remain_main_length -= basis; + const float child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize(create_size(basis, child_preferred_cross_length)); + max_cross_length = std::max(max_cross_length, child_preferred_cross_length); + } + + for (const int i : no_basis_children) { + const auto child = children[i]; + child->Measure(create_size(remain_main_length > 0 ? remain_main_length : 0, + get_cross_length(available_size))); + remain_main_length -= get_main_length(child->GetPreferredSize()); + max_cross_length = + std::max(max_cross_length, get_cross_length(child->GetPreferredSize())); + } + + if (remain_main_length > 0) { + float total_grow = 0; + for (const int i : grow_children) + total_grow += child_layout_data_[i].flex_grow; + + for (const int i : grow_children) { + const float distributed_grow_length = + remain_main_length * (child_layout_data_[i].flex_grow / total_grow); + const auto child = children[i]; + const float new_main_length = + get_main_length(child->GetPreferredSize()) + distributed_grow_length; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + if (remain_main_length < 0) { + float total_shrink = 0; + for (const int i : shrink_chilren) + total_shrink += child_layout_data_[i].flex_shrink; + + for (const int i : shrink_chilren) { + const float distributed_shrink_length = // negative + remain_main_length * + (child_layout_data_[i].flex_shrink / total_shrink); + const auto child = children[i]; + float new_main_length = get_main_length(child->GetPreferredSize()) + + distributed_shrink_length; + new_main_length = new_main_length > 0 ? new_main_length : 0; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + return create_size(get_main_length(available_size) - + (remain_main_length > 0 ? remain_main_length : 0), + max_cross_length); +} + +void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { + auto calculate_cross_anchor = [](Alignment alignment, float start_point, + float content_length, + float preferred_length) -> float { + switch (alignment) { + case Alignment::Start: + return start_point; + case Alignment::Center: + return start_point + (content_length - preferred_length) / 2.0f; + case Alignment::End: + return start_point + content_length - preferred_length; + } + }; + + const auto& children = GetChildren(); + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + auto anchor_x = 0; + for (int i = 0; i < children.size(); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_x; + if (direction_ == FlexDirection::Horizontal) { + real_anchor_x = anchor_x + content_rect.left; + } else { + real_anchor_x = content_rect.GetRight() - anchor_x; + } + child->Layout(Rect{real_anchor_x, + calculate_cross_anchor( + child_layout_data_[i].alignment, content_rect.top, + content_rect.height, size.height), + size.width, size.height}); + + anchor_x += size.width; + } + } else { + auto anchor_y = 0; + for (int i = 0; i < children.size(); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_y; + if (direction_ == FlexDirection::Vertical) { + real_anchor_y = anchor_y + content_rect.top; + } else { + real_anchor_y = content_rect.GetBottom() - anchor_y; + } + child->Layout(Rect{real_anchor_y, + calculate_cross_anchor(child_layout_data_[i].alignment, + content_rect.left, + content_rect.width, size.width), + size.width, size.height}); + + anchor_y += size.height; + } + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/flex_layout_render_object.hpp b/src/ui/render/flex_layout_render_object.hpp new file mode 100644 index 00000000..7172f0c0 --- /dev/null +++ b/src/ui/render/flex_layout_render_object.hpp @@ -0,0 +1,55 @@ +#pragma once +#include "pre.hpp" + +#include <optional> + +#include "render_object.hpp" + +namespace cru::ui::render { +enum class FlexDirection { + Horizontal, + HorizontalReverse, + Vertical, + VertivalReverse +}; + +enum class Alignment { Start, End, Center }; + +struct FlexChildLayoutData { + std::optional<float> flex_basis; + float flex_grow = 0; + float flex_shrink = 0; + Alignment alignment = Alignment::Center; +}; + +class FlexLayoutRenderObject : public RenderObject { + public: + FlexLayoutRenderObject() = default; + FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; + FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = + delete; + FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; + FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; + ~FlexLayoutRenderObject() override = default; + + FlexDirection GetFlexDirection() const { return direction_; } + void SetFlexDirection(FlexDirection direction) { direction_ = direction; } + + FlexChildLayoutData* GetChildLayoutData(int position); + + void Draw(ID2D1RenderTarget* render_target) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnAddChild(RenderObject* new_child, int position) override; + void OnRemoveChild(RenderObject* removed_child, int position) override; + + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + FlexDirection direction_ = FlexDirection::Horizontal; + std::vector<FlexChildLayoutData> child_layout_data_{}; +}; +} // namespace cru::ui::render diff --git a/src/ui/render/linear_layout_render_object.cpp b/src/ui/render/linear_layout_render_object.cpp deleted file mode 100644 index e69de29b..00000000 --- a/src/ui/render/linear_layout_render_object.cpp +++ /dev/null diff --git a/src/ui/render/linear_layout_render_object.hpp b/src/ui/render/linear_layout_render_object.hpp deleted file mode 100644 index 6f70f09b..00000000 --- a/src/ui/render/linear_layout_render_object.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index c2aaeb62..0035d1be 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -1,90 +1,130 @@ #include "render_object.hpp" -#include <utility> -namespace cru::ui::render -{ -void RenderObject::SetRenderHost(IRenderHost* new_render_host) -{ - if (new_render_host == render_host_) return; +#include "cru_debug.hpp" - const auto old = render_host_; - render_host_ = new_render_host; - OnRenderHostChanged(old, new_render_host); +namespace cru::ui::render { +void RenderObject::SetRenderHost(IRenderHost* new_render_host) { + if (new_render_host == render_host_) return; + + const auto old = render_host_; + render_host_ = new_render_host; + OnRenderHostChanged(old, new_render_host); } -void RenderObject::AddChild(RenderObject* render_object, const int position) -{ - if (render_object->GetParent() != nullptr) - throw std::invalid_argument("Render object already has a parent."); +void RenderObject::AddChild(RenderObject* render_object, const int position) { + if (render_object->GetParent() != nullptr) + throw std::invalid_argument("Render object already has a parent."); - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); - if (static_cast<std::vector<RenderObject*>::size_type>(position) > - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + if (static_cast<std::vector<RenderObject*>::size_type>(position) > + children_.size()) + throw std::invalid_argument("Position index is out of bound."); - children_.insert(children_.cbegin() + position, render_object); - render_object->SetParent(this); - OnAddChild(render_object, position); + children_.insert(children_.cbegin() + position, render_object); + render_object->SetParent(this); + OnAddChild(render_object, position); } -void RenderObject::RemoveChild(const int position) -{ - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); +void RenderObject::RemoveChild(const int position) { + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); - if (static_cast<std::vector<RenderObject*>::size_type>(position) >= - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + if (static_cast<std::vector<RenderObject*>::size_type>(position) >= + children_.size()) + throw std::invalid_argument("Position index is out of bound."); - const auto i = children_.cbegin() + position; - const auto removed_child = *i; - children_.erase(i); - removed_child->SetParent(nullptr); - OnRemoveChild(removed_child, position); + const auto i = children_.cbegin() + position; + const auto removed_child = *i; + children_.erase(i); + removed_child->SetParent(nullptr); + OnRemoveChild(removed_child, position); } +void RenderObject::Measure(const Size& available_size) { + OnMeasureCore(available_size); +} -void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host) -{ +void RenderObject::Layout(const Rect& rect) { + SetOffset(rect.GetLeftTop()); + SetSize(rect.GetSize()); + OnLayoutCore(Rect{Point::Zero(), rect.GetSize()}); } -void RenderObject::InvalidateRenderHostPaint() const -{ - if (render_host_ != nullptr) render_host_->InvalidatePaint(); +void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host) {} + +void RenderObject::InvalidateRenderHostPaint() const { + if (render_host_ != nullptr) render_host_->InvalidatePaint(); } -void RenderObject::InvalidateRenderHostLayout() const -{ - if (render_host_ != nullptr) render_host_->InvalidateLayout(); +void RenderObject::InvalidateRenderHostLayout() const { + if (render_host_ != nullptr) render_host_->InvalidateLayout(); } void RenderObject::OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent) -{ -} + RenderObject* new_parent) {} +void RenderObject::OnAddChild(RenderObject* new_child, int position) {} -void RenderObject::OnAddChild(RenderObject* new_child, int position) -{ -} +void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) {} -void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) -{ +void RenderObject::SetParent(RenderObject* new_parent) { + const auto old_parent = parent_; + parent_ = new_parent; + OnParentChanged(old_parent, new_parent); } -void RenderObject::SetParent(RenderObject* new_parent) -{ - const auto old_parent = parent_; - parent_ = new_parent; - OnParentChanged(old_parent, new_parent); +void RenderObject::OnMeasureCore(const Size& available_size) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + const auto content_available_size = available_size - margin_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + debug::DebugMessage( + L"Measure: horizontal length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + debug::DebugMessage( + L"Measure: vertical length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.height = 0; + } + + const auto actual_content_size = + OnMeasureContent(coerced_content_available_size); + + SetPreferredSize(margin_padding_size + content_available_size + + actual_content_size); } - -void LinearLayoutRenderObject::Measure(const MeasureConstraint& constraint) -{ - +void RenderObject::OnLayoutCore(const Rect& rect) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + const auto content_available_size = rect.GetSize() - margin_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + debug::DebugMessage( + L"Layout: horizontal length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + debug::DebugMessage( + L"Layout: vertical length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.height = 0; + } + + OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top, + coerced_content_available_size.width, + coerced_content_available_size.height}); } } // namespace cru::ui::render diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 00f761d1..aeba1457 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -1,5 +1,4 @@ #pragma once - #include "pre.hpp" #include <optional> @@ -9,101 +8,89 @@ #include "base.hpp" #include "ui/ui_base.hpp" -namespace cru::ui::render -{ -struct MeasureConstraint -{ - std::optional<float> min_width; - std::optional<float> min_height; - std::optional<float> max_width; - std::optional<float> max_height; +namespace cru::ui::render { +struct IRenderHost : Interface { + virtual void InvalidatePaint() = 0; + virtual void InvalidateLayout() = 0; }; +class RenderObject : public Object { + protected: + RenderObject() = default; -struct LayoutConstraint -{ - float preferred_width; - float preferred_height; -}; + public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; + IRenderHost* GetRenderHost() const { return render_host_; } + void SetRenderHost(IRenderHost* new_render_host); -struct IRenderHost : Interface -{ - virtual void InvalidatePaint() = 0; - virtual void InvalidateLayout() = 0; -}; + RenderObject* GetParent() const { return parent_; } + const std::vector<RenderObject*>& GetChildren() const { return children_; } + void AddChild(RenderObject* render_object, int position); + void RemoveChild(int position); -// features: -// 1. tree -// 2. layout -// 3. paint -// 3. hit test -class RenderObject : public Object -{ -protected: - RenderObject() = default; + Point GetOffset() const { return offset_; } + void SetOffset(const Point& offset) { offset_ = offset; } + Size GetSize() const { return size_; } + void SetSize(const Size& size) { size_ = size; } -public: - RenderObject(const RenderObject& other) = delete; - RenderObject(RenderObject&& other) = delete; - RenderObject& operator=(const RenderObject& other) = delete; - RenderObject& operator=(RenderObject&& other) = delete; - ~RenderObject() override = default; + Thickness GetMargin() const { return margin_; } + void SetMargin(const Thickness& margin) { margin_ = margin; } - IRenderHost* GetRenderHost() const { return render_host_; } - void SetRenderHost(IRenderHost* new_render_host); + Thickness GetPadding() const { return padding_; } + void SetPadding(const Thickness& padding) { padding_ = padding; } - RenderObject* GetParent() const { return parent_; } + Size GetPreferredSize() const { return preferred_size_; } + void SetPreferredSize(const Size& preferred_size) { + preferred_size_ = preferred_size; + } - const std::vector<RenderObject*>& GetChildren() const { return children_; } - void AddChild(RenderObject* render_object, int position); - void RemoveChild(int position); + void Measure(const Size& available_size); + void Layout(const Rect& rect); - virtual void Measure(const MeasureConstraint& constraint) = 0; - virtual void Layout(const LayoutConstraint& constraint) = 0; + virtual void Draw(ID2D1RenderTarget* render_target) = 0; - virtual void Draw(ID2D1RenderTarget* render_target) = 0; + virtual RenderObject* HitTest(const Point& point) = 0; - virtual void HitTest(const Point& point) = 0; + protected: + virtual void OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host); -protected: - virtual void OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host); + void InvalidateRenderHostPaint() const; + void InvalidateRenderHostLayout() const; - void InvalidateRenderHostPaint() const; - void InvalidateRenderHostLayout() const; + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); - virtual void OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent); + virtual void OnAddChild(RenderObject* new_child, int position); + virtual void OnRemoveChild(RenderObject* removed_child, int position); - virtual void OnAddChild(RenderObject* new_child, int position); - virtual void OnRemoveChild(RenderObject* removed_child, int position); + virtual Size OnMeasureContent(const Size& available_size) = 0; + virtual void OnLayoutContent(const Rect& content_rect) = 0; -private: - void SetParent(RenderObject* new_parent); + private: + void SetParent(RenderObject* new_parent); -private: - IRenderHost* render_host_ = nullptr; - RenderObject* parent_ = nullptr; - std::vector<RenderObject*> children_; -}; + void OnMeasureCore(const Size& available_size); + void OnLayoutCore(const Rect& rect); + + private: + IRenderHost* render_host_ = nullptr; + RenderObject* parent_ = nullptr; + std::vector<RenderObject*> children_{}; -class LinearLayoutRenderObject : public RenderObject -{ -public: - LinearLayoutRenderObject() = default; - LinearLayoutRenderObject(const LinearLayoutRenderObject& other) = delete; - LinearLayoutRenderObject& operator=(const LinearLayoutRenderObject& other) = - delete; - LinearLayoutRenderObject(LinearLayoutRenderObject&& other) = delete; - LinearLayoutRenderObject& operator=(LinearLayoutRenderObject&& other) = - delete; - ~LinearLayoutRenderObject() = default; + Point offset_ = Point::Zero(); + Size size_ = Size::Zero(); - void Measure(const MeasureConstraint& constraint) override; + Thickness margin_ = Thickness::Zero(); + Thickness padding_ = Thickness::Zero(); -private: + Size preferred_size_ = Size::Zero(); }; } // namespace cru::ui::render diff --git a/src/ui/ui_base.cpp b/src/ui/ui_base.cpp deleted file mode 100644 index 2853011d..00000000 --- a/src/ui/ui_base.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "ui_base.hpp" - -namespace cru::ui -{ - -} diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index 17cb9acb..ba6c8b9a 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -1,313 +1,238 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include <optional> +namespace cru::ui { +struct Point final { + constexpr static Point Zero() { return Point(0, 0); } + + constexpr Point() = default; + constexpr Point(const float x, const float y) : x(x), y(y) {} + + float x = 0; + float y = 0; +}; + +constexpr bool operator==(const Point& left, const Point& right) { + return left.x == right.x && left.y == right.y; +} -namespace cru::ui -{ - struct Point final - { - constexpr static Point Zero() - { - return Point(0, 0); - } - - constexpr Point() = default; - constexpr Point(const float x, const float y) : x(x), y(y) { } - - float x = 0; - float y = 0; - }; - - constexpr bool operator==(const Point& left, const Point& right) - { - return left.x == right.x && left.y == right.y; - } - - constexpr bool operator!=(const Point& left, const Point& right) - { - return !(left == right); - } - - - struct Size final - { - constexpr static Size Zero() - { - return Size(0, 0); - } - - constexpr Size() = default; - constexpr Size(const float width, const float height) : width(width), height(height) { } - - float width = 0; - float height = 0; - }; - - constexpr Size operator + (const Size& left, const Size& right) - { - return Size(left.width + right.width, left.height + right.height); - } - - constexpr Size operator - (const Size& left, const Size& right) - { - return Size(left.width - right.width, left.height - right.height); - } - - constexpr bool operator==(const Size& left, const Size& right) - { - return left.width == right.width && left.height == right.height; - } - - constexpr bool operator!=(const Size& left, const Size& right) - { - return !(left == right); - } - - - struct Thickness final - { - constexpr static Thickness Zero() - { - return Thickness(0); - } - - constexpr Thickness() : Thickness(0) { } - - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } - - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } - - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } - - float GetHorizontalTotal() const - { - return left + right; - } - - float GetVerticalTotal() const - { - return top + bottom; - } - - void SetLeftRight(const float value) - { - left = right = value; - } - - void SetTopBottom(const float value) - { - top = bottom = value; - } - - void SetAll(const float value) - { - left = top = right = bottom = value; - } - - float Validate() const - { - return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; - } - - float left; - float top; - float right; - float bottom; - }; - - constexpr bool operator==(const Thickness& left, const Thickness& right) - { - return left.left == right.left && - left.top == right.top && - left.right == right.right && - left.bottom == right.bottom; - } - - constexpr bool operator!=(const Thickness& left, const Thickness& right) - { - return !(left == right); - } - - - struct Rect final - { - constexpr Rect() = default; - constexpr Rect(const float left, const float top, const float width, const float height) - : left(left), top(top), width(width), height(height) { } - constexpr Rect(const Point& lefttop, const Size& size) - : left(lefttop.x), top(lefttop.y), width(size.width), height(size.height) { } - - constexpr static Rect FromVertices(const float left, const float top, const float right, const float bottom) - { - return Rect(left, top, right - left, bottom - top); - } - - constexpr static Rect FromCenter(const Point& center, const float width, const float height) - { - return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, height); - } - - constexpr float GetRight() const - { - return left + width; - } - - constexpr float GetBottom() const - { - return top + height; - } - - constexpr Point GetLeftTop() const - { - return Point(left, top); - } - - constexpr Point GetRightBottom() const - { - return Point(left + width, top + height); - } - - constexpr Point GetLeftBottom() const - { - return Point(left, top + height); - } - - constexpr Point GetRightTop() const - { - return Point(left + width, top); - } - - constexpr Point GetCenter() const - { - return Point(left + width / 2.0f, top + height / 2.0f); - } - - constexpr Size GetSize() const - { - return Size(width, height); - } - - constexpr Rect Shrink(const Thickness& thickness) const - { - return Rect(left + thickness.left, top + thickness.top, width - thickness.GetHorizontalTotal(), height - thickness.GetVerticalTotal()); - } - - constexpr bool IsPointInside(const Point& point) const - { - return - point.x >= left && - point.x < GetRight() && - point.y >= top && - point.y < GetBottom(); - } - - float left = 0.0f; - float top = 0.0f; - float width = 0.0f; - float height = 0.0f; - }; - - constexpr bool operator==(const Rect& left, const Rect& right) - { - return left.left == right.left && - left.top == right.top && - left.width == right.width && - left.height == right.height; - } - - constexpr bool operator!=(const Rect& left, const Rect& right) - { - return !(left == right); - } - - - struct RoundedRect final - { - constexpr RoundedRect() = default; - constexpr RoundedRect(const Rect& rect, const float radius_x, const float radius_y) - : rect(rect), radius_x(radius_x), radius_y(radius_y) { } - - Rect rect{}; - float radius_x = 0.0f; - float radius_y = 0.0f; - }; - - constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) - { - return left.rect == right.rect && left.radius_x == right.radius_x && left.radius_y == right.radius_y; - } - - constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) - { - return !(left == right); - } - - - struct Ellipse final - { - constexpr Ellipse() = default; - constexpr Ellipse(const Point& center, const float radius_x, const float radius_y) - : center(center), radius_x(radius_x), radius_y(radius_y) { } - - constexpr static Ellipse FromRect(const Rect& rect) - { - return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f); - } - - constexpr Rect GetBoundRect() const - { - return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f); - } - - Point center{}; - float radius_x = 0.0f; - float radius_y = 0.0f; - }; - - constexpr bool operator==(const Ellipse& left, const Ellipse& right) - { - return left.center == right.center && left.radius_x == right.radius_x && left.radius_y == right.radius_y; - } - - constexpr bool operator!=(const Ellipse& left, const Ellipse& right) - { - return !(left == right); - } - - - struct TextRange final - { - constexpr static std::optional<TextRange> FromTwoSides(unsigned first, unsigned second) - { - if (first > second) - return std::make_optional<TextRange>(second, first - second); - if (first < second) - return std::make_optional<TextRange>(first, second - first); - return std::nullopt; - } - - constexpr static std::pair<unsigned, unsigned> ToTwoSides(std::optional<TextRange> text_range, unsigned default_position = 0) - { - if (text_range.has_value()) - return std::make_pair(text_range.value().position, text_range.value().position + text_range.value().count); - return std::make_pair(default_position, default_position); - } - - constexpr TextRange() = default; - constexpr TextRange(const unsigned position, const unsigned count) - : position(position), count(count) - { - - } - - unsigned position = 0; - unsigned count = 0; - }; +constexpr bool operator!=(const Point& left, const Point& right) { + return !(left == right); } + +struct Size final { + constexpr static Size Zero() { return Size(0, 0); } + + constexpr Size() = default; + constexpr Size(const float width, const float height) + : width(width), height(height) {} + + float width = 0; + float height = 0; +}; + +constexpr Size operator+(const Size& left, const Size& right) { + return Size(left.width + right.width, left.height + right.height); +} + +constexpr Size operator-(const Size& left, const Size& right) { + return Size(left.width - right.width, left.height - right.height); +} + +constexpr bool operator==(const Size& left, const Size& right) { + return left.width == right.width && left.height == right.height; +} + +constexpr bool operator!=(const Size& left, const Size& right) { + return !(left == right); +} + +struct Thickness final { + constexpr static Thickness Zero() { return Thickness(0); } + + constexpr Thickness() : Thickness(0) {} + + constexpr explicit Thickness(const float width) + : left(width), top(width), right(width), bottom(width) {} + + constexpr explicit Thickness(const float horizontal, const float vertical) + : left(horizontal), top(vertical), right(horizontal), bottom(vertical) {} + + constexpr Thickness(const float left, const float top, const float right, + const float bottom) + : left(left), top(top), right(right), bottom(bottom) {} + + float GetHorizontalTotal() const { return left + right; } + + float GetVerticalTotal() const { return top + bottom; } + + void SetLeftRight(const float value) { left = right = value; } + + void SetTopBottom(const float value) { top = bottom = value; } + + void SetAll(const float value) { left = top = right = bottom = value; } + + float Validate() const { + return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; + } + + float left; + float top; + float right; + float bottom; +}; + +constexpr bool operator==(const Thickness& left, const Thickness& right) { + return left.left == right.left && left.top == right.top && + left.right == right.right && left.bottom == right.bottom; +} + +constexpr bool operator!=(const Thickness& left, const Thickness& right) { + return !(left == right); +} + +struct Rect final { + constexpr Rect() = default; + constexpr Rect(const float left, const float top, const float width, + const float height) + : left(left), top(top), width(width), height(height) {} + constexpr Rect(const Point& lefttop, const Size& size) + : left(lefttop.x), + top(lefttop.y), + width(size.width), + height(size.height) {} + + constexpr static Rect FromVertices(const float left, const float top, + const float right, const float bottom) { + return Rect(left, top, right - left, bottom - top); + } + + constexpr static Rect FromCenter(const Point& center, const float width, + const float height) { + return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, + height); + } + + constexpr float GetRight() const { return left + width; } + + constexpr float GetBottom() const { return top + height; } + + constexpr Point GetLeftTop() const { return Point(left, top); } + + constexpr Point GetRightBottom() const { + return Point(left + width, top + height); + } + + constexpr Point GetLeftBottom() const { return Point(left, top + height); } + + constexpr Point GetRightTop() const { return Point(left + width, top); } + + constexpr Point GetCenter() const { + return Point(left + width / 2.0f, top + height / 2.0f); + } + + constexpr Size GetSize() const { return Size(width, height); } + + constexpr Rect Shrink(const Thickness& thickness) const { + return Rect(left + thickness.left, top + thickness.top, + width - thickness.GetHorizontalTotal(), + height - thickness.GetVerticalTotal()); + } + + constexpr bool IsPointInside(const Point& point) const { + return point.x >= left && point.x < GetRight() && point.y >= top && + point.y < GetBottom(); + } + + float left = 0.0f; + float top = 0.0f; + float width = 0.0f; + float height = 0.0f; +}; + +constexpr bool operator==(const Rect& left, const Rect& right) { + return left.left == right.left && left.top == right.top && + left.width == right.width && left.height == right.height; +} + +constexpr bool operator!=(const Rect& left, const Rect& right) { + return !(left == right); +} + +struct RoundedRect final { + constexpr RoundedRect() = default; + constexpr RoundedRect(const Rect& rect, const float radius_x, + const float radius_y) + : rect(rect), radius_x(radius_x), radius_y(radius_y) {} + + Rect rect{}; + float radius_x = 0.0f; + float radius_y = 0.0f; +}; + +constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) { + return left.rect == right.rect && left.radius_x == right.radius_x && + left.radius_y == right.radius_y; +} + +constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) { + return !(left == right); +} + +struct Ellipse final { + constexpr Ellipse() = default; + constexpr Ellipse(const Point& center, const float radius_x, + const float radius_y) + : center(center), radius_x(radius_x), radius_y(radius_y) {} + + constexpr static Ellipse FromRect(const Rect& rect) { + return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f); + } + + constexpr Rect GetBoundRect() const { + return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f); + } + + Point center{}; + float radius_x = 0.0f; + float radius_y = 0.0f; +}; + +constexpr bool operator==(const Ellipse& left, const Ellipse& right) { + return left.center == right.center && left.radius_x == right.radius_x && + left.radius_y == right.radius_y; +} + +constexpr bool operator!=(const Ellipse& left, const Ellipse& right) { + return !(left == right); +} + +struct TextRange final { + constexpr static std::optional<TextRange> FromTwoSides(unsigned first, + unsigned second) { + if (first > second) + return std::make_optional<TextRange>(second, first - second); + if (first < second) + return std::make_optional<TextRange>(first, second - first); + return std::nullopt; + } + + constexpr static std::pair<unsigned, unsigned> ToTwoSides( + std::optional<TextRange> text_range, unsigned default_position = 0) { + if (text_range.has_value()) + return std::make_pair( + text_range.value().position, + text_range.value().position + text_range.value().count); + return std::make_pair(default_position, default_position); + } + + constexpr TextRange() = default; + constexpr TextRange(const unsigned position, const unsigned count) + : position(position), count(count) {} + + unsigned position = 0; + unsigned count = 0; +}; +} // namespace cru::ui |