aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj5
-rw-r--r--CruUI.vcxproj.filters21
-rw-r--r--src/graph/graph.cpp367
-rw-r--r--src/graph/graph.hpp322
-rw-r--r--src/ui/render/flex_layout_render_object.cpp212
-rw-r--r--src/ui/render/flex_layout_render_object.hpp55
-rw-r--r--src/ui/render/linear_layout_render_object.cpp0
-rw-r--r--src/ui/render/linear_layout_render_object.hpp1
-rw-r--r--src/ui/render/render_object.cpp158
-rw-r--r--src/ui/render/render_object.hpp133
-rw-r--r--src/ui/ui_base.cpp6
-rw-r--r--src/ui/ui_base.hpp537
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