diff options
author | crupest <crupest@outlook.com> | 2018-11-07 20:20:26 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2018-11-07 20:20:26 +0800 |
commit | e2efeb22595b263f6eac5563b163a2574aa2f0dc (patch) | |
tree | e371ab858ce124c0d2070096e41314e44af87ca2 /src | |
parent | 2b5b89e9483063f3af05fb5485043868d447994b (diff) | |
download | cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.tar.gz cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.tar.bz2 cru-e2efeb22595b263f6eac5563b163a2574aa2f0dc.zip |
Reformat codes.
Diffstat (limited to 'src')
-rw-r--r-- | src/graph/graph.cpp | 416 | ||||
-rw-r--r-- | src/graph/graph.h | 283 | ||||
-rw-r--r-- | src/ui/control.cpp | 1535 | ||||
-rw-r--r-- | src/ui/control.h | 691 | ||||
-rw-r--r-- | src/ui/controls/text_block.cpp | 20 | ||||
-rw-r--r-- | src/ui/cursor.h | 1 | ||||
-rw-r--r-- | src/ui/events/ui_event.cpp | 16 | ||||
-rw-r--r-- | src/ui/events/ui_event.h | 603 | ||||
-rw-r--r-- | src/ui/layout_base.h | 301 | ||||
-rw-r--r-- | src/ui/ui_base.cpp | 23 | ||||
-rw-r--r-- | src/ui/ui_base.h | 243 | ||||
-rw-r--r-- | src/ui/window.cpp | 1037 | ||||
-rw-r--r-- | src/ui/window.h | 465 |
13 files changed, 2801 insertions, 2833 deletions
diff --git a/src/graph/graph.cpp b/src/graph/graph.cpp index 30b51413..e8d58a58 100644 --- a/src/graph/graph.cpp +++ b/src/graph/graph.cpp @@ -3,243 +3,243 @@ #include "application.h" #include "exception.h" -namespace cru { - namespace 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_ - ) - ); +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(); - } + CreateTargetBitmap(); + } - WindowRenderTarget::~WindowRenderTarget() - { + 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(); + 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); + 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; + old_target = nullptr; + target_bitmap_ = nullptr; - ThrowIfFailed( - dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0) - ); + ThrowIfFailed( + dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0) + ); - CreateTargetBitmap(); + CreateTargetBitmap(); - if (target_this) - d2d1_device_context->SetTarget(target_bitmap_.Get()); - } + if (target_this) + d2d1_device_context->SetTarget(target_bitmap_.Get()); + } - void WindowRenderTarget::SetAsTarget() - { - GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); - } + void WindowRenderTarget::SetAsTarget() + { + GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); + } - void WindowRenderTarget::Present() - { - ThrowIfFailed( - dxgi_swap_chain_->Present(1, 0) - ); - } + 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)) + 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 ); - 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_ - ) - ); - } + // 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()->GetGraphManager(); - } + GraphManager* GraphManager::GetInstance() + { + return Application::GetInstance()->GetGraphManager(); + } - GraphManager::GraphManager() - { - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + GraphManager::GraphManager() + { + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; -#ifdef _DEBUG +#ifdef CRU_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_)) - ); + 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() + { - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(dwrite_factory_.GetAddressOf()) - )); + } - dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); - } + std::shared_ptr<WindowRenderTarget> GraphManager::CreateWindowRenderTarget(HWND hwnd) + { + return std::make_shared<WindowRenderTarget>(this, hwnd); + } - GraphManager::~GraphManager() - { + Dpi GraphManager::GetDpi() const + { + Dpi dpi; + d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); + return dpi; + } - } + void GraphManager::ReloadSystemMetrics() + { + ThrowIfFailed( + d2d1_factory_->ReloadSystemMetrics() + ); + } - std::shared_ptr<WindowRenderTarget> GraphManager::CreateWindowRenderTarget(HWND hwnd) - { - return std::make_shared<WindowRenderTarget>(this, hwnd); - } + ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color) + { + const auto device_context = GraphManager::GetInstance()->GetD2D1DeviceContext(); + ComPtr<ID2D1SolidColorBrush> solid_color_brush; + device_context->CreateSolidColorBrush(color, &solid_color_brush); + return solid_color_brush; + } - Dpi GraphManager::GetDpi() const - { - Dpi dpi; - d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); - return dpi; - } + ComPtr<IDWriteTextFormat> CreateDefaultTextFormat() + { + const auto dwrite_factory = GraphManager::GetInstance()->GetDWriteFactory(); - void GraphManager::ReloadSystemMetrics() - { - ThrowIfFailed( - d2d1_factory_->ReloadSystemMetrics() - ); - } + ComPtr<IDWriteTextFormat> text_format; - ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color) - { - const auto device_context = GraphManager::GetInstance()->GetD2D1DeviceContext(); - ComPtr<ID2D1SolidColorBrush> solid_color_brush; - device_context->CreateSolidColorBrush(color, &solid_color_brush); - return solid_color_brush; - } + ThrowIfFailed(dwrite_factory->CreateTextFormat( + L"等线", nullptr, + DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 24.0, L"zh-cn", + &text_format + )); - ComPtr<IDWriteTextFormat> CreateDefaultTextFormat() - { - const auto dwrite_factory = GraphManager::GetInstance()->GetDWriteFactory(); - - ComPtr<IDWriteTextFormat> text_format; - - ThrowIfFailed(dwrite_factory->CreateTextFormat( - L"等线", nullptr, - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - 24.0, L"zh-cn", - &text_format - )); - - ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); - ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - return text_format; - } + ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); + ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); + return text_format; } } + diff --git a/src/graph/graph.h b/src/graph/graph.h index 19f060e2..ff8b88b8 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -7,174 +7,171 @@ #include "application.h" -namespace cru +namespace cru::graph { - namespace 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; - class GraphManager : public Object - { - public: - static GraphManager* GetInstance(); - - public: - GraphManager(); - 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) + //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 static_cast<int>(dip * dpi / 96.0f); + return graph_manager_; } - inline int DipToPixelX(const float dip_x) + //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 DipToPixelInternal(dip_x, Application::GetInstance()->GetGraphManager()->GetDpi().x); + return target_bitmap_; } - inline int DipToPixelY(const float dip_y) + //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 : public Object + { + public: + static GraphManager* GetInstance(); + + public: + GraphManager(); + 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 DipToPixelInternal(dip_y, Application::GetInstance()->GetGraphManager()->GetDpi().y); + return d2d1_factory_; } - inline float DipToPixelInternal(const int pixel, const float dpi) + Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2D1DeviceContext() const { - return static_cast<float>(pixel) * 96.0f / dpi; + return d2d1_device_context_; } - inline float PixelToDipX(const int pixel_x) + Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const { - return DipToPixelInternal(pixel_x, Application::GetInstance()->GetGraphManager()->GetDpi().x); + return d3d11_device_; } - inline float PixelToDipY(const int pixel_y) + Microsoft::WRL::ComPtr<IDXGIFactory2> GetDxgiFactory() const { - return DipToPixelInternal(pixel_y, Application::GetInstance()->GetGraphManager()->GetDpi().y); + return dxgi_factory_; } - Microsoft::WRL::ComPtr<ID2D1DeviceContext> WindowRenderTarget::GetD2DDeviceContext() const + Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory() const { - return graph_manager_->GetD2D1DeviceContext(); + return dwrite_factory_; } - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color); - Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat(); - inline void WithTransform(ID2D1DeviceContext* device_context, const D2D1_MATRIX_3X2_F matrix, const std::function<void(ID2D1DeviceContext*)>& action) + //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 { - 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); + 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, Application::GetInstance()->GetGraphManager()->GetDpi().x); + } + + inline int DipToPixelY(const float dip_y) + { + return DipToPixelInternal(dip_y, Application::GetInstance()->GetGraphManager()->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, Application::GetInstance()->GetGraphManager()->GetDpi().x); + } + + inline float PixelToDipY(const int pixel_y) + { + return DipToPixelInternal(pixel_y, Application::GetInstance()->GetGraphManager()->GetDpi().y); + } + + Microsoft::WRL::ComPtr<ID2D1DeviceContext> WindowRenderTarget::GetD2DDeviceContext() const + { + return graph_manager_->GetD2D1DeviceContext(); + } + + Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color); + Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat(); + + 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); } } diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 9416d48a..ec8420c1 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -7,880 +7,879 @@ #include "exception.h" #include "cru_debug.h" -namespace cru { - namespace ui { - using namespace events; +namespace cru::ui +{ + using namespace events; - Control::Control(const bool container) : - is_container_(container) - { - - } - - Control::Control(WindowConstructorTag, Window* window) : Control(true) - { - window_ = window; - } - - Control::~Control() - { - for (auto control: GetChildren()) - { - delete control; - } - } - - void AddChildCheck(Control* control) - { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast<Window*>(control)) - throw std::invalid_argument("Can't add a window as child."); - } - - const std::vector<Control*>& Control::GetChildren() const - { - return children_; - } - - void Control::AddChild(Control* control) - { - ThrowIfNotContainer(); - AddChildCheck(control); - - this->children_.push_back(control); - - control->parent_ = this; - - this->OnAddChild(control); - } - - void Control::AddChild(Control* control, int position) - { - ThrowIfNotContainer(); - AddChildCheck(control); - - if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - this->children_.insert(this->children_.cbegin() + position, control); - - control->parent_ = this; - - this->OnAddChild(this); - } - - void Control::RemoveChild(Control* child) - { - ThrowIfNotContainer(); - const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); - if (i == this->children_.cend()) - throw std::invalid_argument("The argument child is not a child of this control."); - - this->children_.erase(i); - - child->parent_ = nullptr; - - this->OnRemoveChild(this); - } - - void Control::RemoveChild(const int position) - { - ThrowIfNotContainer(); - if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto p = children_.cbegin() + position; - const auto child = *p; - children_.erase(p); - - child->parent_ = nullptr; - - this->OnRemoveChild(child); - } - - Control* Control::GetAncestor() - { - // if attached to window, the window is the ancestor. - if (window_) - return window_; - - // otherwise find the ancestor - auto ancestor = this; - while (const auto parent = ancestor->GetParent()) - ancestor = parent; - return ancestor; - } - - void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate) - { - predicate(control); - if (control->IsContainer()) - for (auto c: control->GetChildren()) - { - TraverseDescendantsInternal(c, predicate); - } - } - - void Control::TraverseDescendants(const std::function<void(Control*)>& predicate) - { - if (is_container_) - TraverseDescendantsInternal(this, predicate); - else - predicate(this); - } - - Point Control::GetPositionRelative() - { - return position_; - } - - void Control::SetPositionRelative(const Point & position) - { - if (position != position_) - { - if (old_position_ == position) // if cache has been refreshed and no pending notify - old_position_ = position_; - position_ = position; - LayoutManager::GetInstance()->InvalidateControlPositionCache(this); - if (auto window = GetWindow()) - { - window->Repaint(); - } - } - } - - Size Control::GetSize() - { - return size_; - } - - void Control::SetSize(const Size & size) - { - const auto old_size = size_; - size_ = size; - SizeChangedEventArgs args(this, this, old_size, size); - RaiseSizeChangedEvent(args); - if (auto window = GetWindow()) - window->Repaint(); - } - - Point Control::GetPositionAbsolute() const - { - return position_cache_.lefttop_position_absolute; - } - - Point Control::ControlToWindow(const Point& point) const - { - return Point(point.x + position_cache_.lefttop_position_absolute.x, - point.y + position_cache_.lefttop_position_absolute.y); - } - - Point Control::WindowToControl(const Point & point) const - { - return Point(point.x - position_cache_.lefttop_position_absolute.x, - point.y - position_cache_.lefttop_position_absolute.y); - } + Control::Control(const bool container) : + is_container_(container) + { - bool Control::IsPointInside(const Point & point) - { - const auto size = GetSize(); - return point.x >= 0.0f && point.x < size.width && point.y >= 0.0f && point.y < size.height; - } - - void Control::Draw(ID2D1DeviceContext* device_context) - { - D2D1::Matrix3x2F old_transform; - device_context->GetTransform(&old_transform); - - const auto position = GetPositionRelative(); - device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - - OnDraw(device_context); - - const auto rect = GetRect(RectRange::Content); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), - [this](ID2D1DeviceContext* device_context) - { - OnDrawContent(device_context); - DrawEventArgs args(this, this, device_context); - draw_event.Raise(args); - }); - - for (auto child : GetChildren()) - child->Draw(device_context); - - device_context->SetTransform(old_transform); - } + } - void Control::Repaint() - { - if (window_ != nullptr) - window_->Repaint(); - } + Control::Control(WindowConstructorTag, Window* window) : Control(true) + { + window_ = window; + } - bool Control::RequestFocus() + Control::~Control() + { + for (auto control: GetChildren()) { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->RequestFocusFor(this); + delete control; } + } - bool Control::HasFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->GetFocusControl() == this; - } + void AddChildCheck(Control* control) + { + if (control->GetParent() != nullptr) + throw std::invalid_argument("The control already has a parent."); - void Control::InvalidateLayout() - { - if (const auto window = GetWindow()) - LayoutManager::GetInstance()->InvalidateWindowLayout(window); - } + if (dynamic_cast<Window*>(control)) + throw std::invalid_argument("Can't add a window as child."); + } - void Control::Measure(const Size& available_size) - { - SetDesiredSize(OnMeasureCore(available_size)); - } + const std::vector<Control*>& Control::GetChildren() const + { + return children_; + } - void Control::Layout(const Rect& rect) - { - SetPositionRelative(rect.GetLeftTop()); - SetSize(rect.GetSize()); - OnLayoutCore(Rect(Point::Zero(), rect.GetSize())); - } + void Control::AddChild(Control* control) + { + ThrowIfNotContainer(); + AddChildCheck(control); - Size Control::GetDesiredSize() const - { - return desired_size_; - } + this->children_.push_back(control); - void Control::SetDesiredSize(const Size& desired_size) - { - desired_size_ = desired_size; - } + control->parent_ = this; - inline void Shrink(Rect& rect, const Thickness& thickness) - { - rect.left += thickness.left; - rect.top += thickness.top; - rect.width -= thickness.GetHorizontalTotal(); - rect.height -= thickness.GetVerticalTotal(); - } + this->OnAddChild(control); + } - Rect Control::GetRect(const RectRange range) - { - if (GetSize() == Size::Zero()) - return Rect(); + void Control::AddChild(Control* control, int position) + { + ThrowIfNotContainer(); + AddChildCheck(control); - const auto layout_params = GetLayoutParams(); + if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size()) + throw std::invalid_argument("The position is out of range."); - auto result = Rect(Point::Zero(), GetSize()); + this->children_.insert(this->children_.cbegin() + position, control); - if (range == RectRange::Margin) - return result; + control->parent_ = this; - Shrink(result, layout_params->margin); + this->OnAddChild(this); + } - if (range == RectRange::FullBorder) - return result; + void Control::RemoveChild(Control* child) + { + ThrowIfNotContainer(); + const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); + if (i == this->children_.cend()) + throw std::invalid_argument("The argument child is not a child of this control."); - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); + this->children_.erase(i); - if (range == RectRange::HalfBorder) - return result; + child->parent_ = nullptr; - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); + this->OnRemoveChild(this); + } - if (range == RectRange::Padding) - return result; + void Control::RemoveChild(const int position) + { + ThrowIfNotContainer(); + if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) + throw std::invalid_argument("The position is out of range."); - Shrink(result, layout_params->padding); + const auto p = children_.cbegin() + position; + const auto child = *p; + children_.erase(p); - return result; - } + child->parent_ = nullptr; - Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) - { - const auto rect_from = GetRect(from); - const auto rect_to = GetRect(to); - auto p = point; - p.x += rect_from.left; - p.y += rect_from.top; - p.x -= rect_to.left; - p.y -= rect_to.top; - return p; - } + this->OnRemoveChild(child); + } - void Control::InvalidateBorder() - { - InvalidateLayout(); - Repaint(); - } + Control* Control::GetAncestor() + { + // if attached to window, the window is the ancestor. + if (window_) + return window_; + + // otherwise find the ancestor + auto ancestor = this; + while (const auto parent = ancestor->GetParent()) + ancestor = parent; + return ancestor; + } - void Control::SetBordered(const bool bordered) - { - if (bordered != is_bordered_) + void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate) + { + predicate(control); + if (control->IsContainer()) + for (auto c: control->GetChildren()) { - is_bordered_ = bordered; - InvalidateBorder(); + TraverseDescendantsInternal(c, predicate); } - } + } - void Control::SetCursor(const Cursor::Ptr& cursor) - { - if (cursor != cursor_) - { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } - } + void Control::TraverseDescendants(const std::function<void(Control*)>& predicate) + { + if (is_container_) + TraverseDescendantsInternal(this, predicate); + else + predicate(this); + } - void Control::OnAddChild(Control* child) - { - if (auto window = GetWindow()) - { - child->TraverseDescendants([window](Control* control) { - control->OnAttachToWindow(window); - }); - window->RefreshControlList(); - InvalidateLayout(); - } - } + Point Control::GetPositionRelative() + { + return position_; + } - void Control::OnRemoveChild(Control* child) + void Control::SetPositionRelative(const Point & position) + { + if (position != position_) { + if (old_position_ == position) // if cache has been refreshed and no pending notify + old_position_ = position_; + position_ = position; + LayoutManager::GetInstance()->InvalidateControlPositionCache(this); if (auto window = GetWindow()) { - child->TraverseDescendants([window](Control* control) { - control->OnDetachToWindow(window); - }); - window->RefreshControlList(); - InvalidateLayout(); - } - } - - void Control::OnAttachToWindow(Window* window) - { - window_ = window; - } - - void Control::OnDetachToWindow(Window * window) - { - window_ = nullptr; - } - - inline D2D1_RECT_F Convert(const Rect& rect) - { - return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); - } - - void Control::OnDraw(ID2D1DeviceContext* device_context) - { -#ifdef CRU_DEBUG_LAYOUT - if (GetWindow()->IsDebugLayout()) - { - const auto resource = Application::GetInstance()->GetDebugLayoutResource(); - if (padding_geometry_ != nullptr) - device_context->FillGeometry(padding_geometry_.Get(), resource->padding_brush.Get()); - if (margin_geometry_ != nullptr) - device_context->FillGeometry(margin_geometry_.Get(), resource->margin_brush.Get()); - device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), resource->out_border_brush.Get()); - } -#endif - - if (is_bordered_) - { - const auto border_rect = GetRect(RectRange::HalfBorder); - device_context->DrawRoundedRectangle( - D2D1::RoundedRect( - Convert(border_rect), - GetBorderProperty().GetRadiusX(), - GetBorderProperty().GetRadiusY() - ), - GetBorderProperty().GetBrush().Get(), - GetBorderProperty().GetStrokeWidth(), - GetBorderProperty().GetStrokeStyle().Get() - ); - } - } - - void Control::OnDrawContent(ID2D1DeviceContext * device_context) - { - - } - - void Control::OnPositionChanged(PositionChangedEventArgs & args) - { - - } - - void Control::OnSizeChanged(SizeChangedEventArgs & args) - { - } - - void Control::OnPositionChangedCore(PositionChangedEventArgs & args) - { - - } - - namespace - { -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in) - { - const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); - Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry; - ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); - Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; - ThrowIfFailed(result_geometry->Open(&sink)); - ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); - ThrowIfFailed(sink->Close()); - return result_geometry; + window->Repaint(); } -#endif - } - - void Control::OnSizeChangedCore(SizeChangedEventArgs & args) - { -#ifdef CRU_DEBUG_LAYOUT - margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); - padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); -#endif - } - - void Control::RaisePositionChangedEvent(PositionChangedEventArgs& args) - { - OnPositionChangedCore(args); - OnPositionChanged(args); - position_changed_event.Raise(args); } + } - void Control::RaiseSizeChangedEvent(SizeChangedEventArgs& args) - { - OnSizeChangedCore(args); - OnSizeChanged(args); - size_changed_event.Raise(args); - } + Size Control::GetSize() + { + return size_; + } - void Control::OnMouseEnter(MouseEventArgs & args) - { - } + void Control::SetSize(const Size & size) + { + const auto old_size = size_; + size_ = size; + SizeChangedEventArgs args(this, this, old_size, size); + RaiseSizeChangedEvent(args); + if (auto window = GetWindow()) + window->Repaint(); + } - void Control::OnMouseLeave(MouseEventArgs & args) - { - } + Point Control::GetPositionAbsolute() const + { + return position_cache_.lefttop_position_absolute; + } - void Control::OnMouseMove(MouseEventArgs & args) - { - } + Point Control::ControlToWindow(const Point& point) const + { + return Point(point.x + position_cache_.lefttop_position_absolute.x, + point.y + position_cache_.lefttop_position_absolute.y); + } - void Control::OnMouseDown(MouseButtonEventArgs & args) - { - } + Point Control::WindowToControl(const Point & point) const + { + return Point(point.x - position_cache_.lefttop_position_absolute.x, + point.y - position_cache_.lefttop_position_absolute.y); + } - void Control::OnMouseUp(MouseButtonEventArgs & args) - { - } + bool Control::IsPointInside(const Point & point) + { + const auto size = GetSize(); + return point.x >= 0.0f && point.x < size.width && point.y >= 0.0f && point.y < size.height; + } - void Control::OnMouseClick(MouseButtonEventArgs& args) - { + void Control::Draw(ID2D1DeviceContext* device_context) + { + D2D1::Matrix3x2F old_transform; + device_context->GetTransform(&old_transform); - } + const auto position = GetPositionRelative(); + device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - void Control::OnMouseEnterCore(MouseEventArgs & args) - { - is_mouse_inside_ = true; - } + OnDraw(device_context); - void Control::OnMouseLeaveCore(MouseEventArgs & args) - { - is_mouse_inside_ = false; - for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) + const auto rect = GetRect(RectRange::Content); + graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), + [this](ID2D1DeviceContext* device_context) { - if (is_mouse_click_valid.second) - { - is_mouse_click_valid.second = false; - OnMouseClickEnd(is_mouse_click_valid.first); - } - } - } + OnDrawContent(device_context); + DrawEventArgs args(this, this, device_context); + draw_event.Raise(args); + }); - void Control::OnMouseMoveCore(MouseEventArgs & args) - { + for (auto child : GetChildren()) + child->Draw(device_context); - } + device_context->SetTransform(old_transform); + } - void Control::OnMouseDownCore(MouseButtonEventArgs & args) - { - if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) - RequestFocus(); - is_mouse_click_valid_map_[args.GetMouseButton()] = true; - OnMouseClickBegin(args.GetMouseButton()); - } + void Control::Repaint() + { + if (window_ != nullptr) + window_->Repaint(); + } - void Control::OnMouseUpCore(MouseButtonEventArgs & args) - { - if (is_mouse_click_valid_map_[args.GetMouseButton()]) - { - is_mouse_click_valid_map_[args.GetMouseButton()] = false; - RaiseMouseClickEvent(args); - OnMouseClickEnd(args.GetMouseButton()); - } - } + bool Control::RequestFocus() + { + auto window = GetWindow(); + if (window == nullptr) + return false; - void Control::OnMouseClickCore(MouseButtonEventArgs& args) - { + return window->RequestFocusFor(this); + } - } + bool Control::HasFocus() + { + auto window = GetWindow(); + if (window == nullptr) + return false; - void Control::RaiseMouseEnterEvent(MouseEventArgs& args) - { - OnMouseEnterCore(args); - OnMouseEnter(args); - mouse_enter_event.Raise(args); - } + return window->GetFocusControl() == this; + } - void Control::RaiseMouseLeaveEvent(MouseEventArgs& args) - { - OnMouseLeaveCore(args); - OnMouseLeave(args); - mouse_leave_event.Raise(args); - } + void Control::InvalidateLayout() + { + if (const auto window = GetWindow()) + LayoutManager::GetInstance()->InvalidateWindowLayout(window); + } - void Control::RaiseMouseMoveEvent(MouseEventArgs& args) - { - OnMouseMoveCore(args); - OnMouseMove(args); - mouse_move_event.Raise(args); - } + void Control::Measure(const Size& available_size) + { + SetDesiredSize(OnMeasureCore(available_size)); + } - void Control::RaiseMouseDownEvent(MouseButtonEventArgs& args) - { - OnMouseDownCore(args); - OnMouseDown(args); - mouse_down_event.Raise(args); - } + void Control::Layout(const Rect& rect) + { + SetPositionRelative(rect.GetLeftTop()); + SetSize(rect.GetSize()); + OnLayoutCore(Rect(Point::Zero(), rect.GetSize())); + } - void Control::RaiseMouseUpEvent(MouseButtonEventArgs& args) - { - OnMouseUpCore(args); - OnMouseUp(args); - mouse_up_event.Raise(args); - } + Size Control::GetDesiredSize() const + { + return desired_size_; + } - void Control::RaiseMouseClickEvent(MouseButtonEventArgs& args) - { - OnMouseClickCore(args); - OnMouseClick(args); - mouse_click_event.Raise(args); - } + void Control::SetDesiredSize(const Size& desired_size) + { + desired_size_ = desired_size; + } - void Control::OnMouseClickBegin(MouseButton button) - { + inline void Shrink(Rect& rect, const Thickness& thickness) + { + rect.left += thickness.left; + rect.top += thickness.top; + rect.width -= thickness.GetHorizontalTotal(); + rect.height -= thickness.GetVerticalTotal(); + } - } + Rect Control::GetRect(const RectRange range) + { + if (GetSize() == Size::Zero()) + return Rect(); - void Control::OnMouseClickEnd(MouseButton button) - { + const auto layout_params = GetLayoutParams(); - } + auto result = Rect(Point::Zero(), GetSize()); - void Control::OnKeyDown(KeyEventArgs& args) - { - } + if (range == RectRange::Margin) + return result; - void Control::OnKeyUp(KeyEventArgs& args) - { - } + Shrink(result, layout_params->margin); - void Control::OnChar(CharEventArgs& args) - { - } + if (range == RectRange::FullBorder) + return result; - void Control::OnKeyDownCore(KeyEventArgs& args) - { - } + if (is_bordered_) + Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - void Control::OnKeyUpCore(KeyEventArgs& args) - { - } + if (range == RectRange::HalfBorder) + return result; - void Control::OnCharCore(CharEventArgs& args) - { - } + if (is_bordered_) + Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - void Control::RaiseKeyDownEvent(KeyEventArgs& args) - { - OnKeyDownCore(args); - OnKeyDown(args); - key_down_event.Raise(args); - } + if (range == RectRange::Padding) + return result; - void Control::RaiseKeyUpEvent(KeyEventArgs& args) - { - OnKeyUpCore(args); - OnKeyUp(args); - key_up_event.Raise(args); - } + Shrink(result, layout_params->padding); - void Control::RaiseCharEvent(CharEventArgs& args) - { - OnCharCore(args); - OnChar(args); - char_event.Raise(args); - } + return result; + } - void Control::OnGetFocus(FocusChangeEventArgs& args) - { + Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) + { + const auto rect_from = GetRect(from); + const auto rect_to = GetRect(to); + auto p = point; + p.x += rect_from.left; + p.y += rect_from.top; + p.x -= rect_to.left; + p.y -= rect_to.top; + return p; + } - } + void Control::InvalidateBorder() + { + InvalidateLayout(); + Repaint(); + } - void Control::OnLoseFocus(FocusChangeEventArgs& args) + void Control::SetBordered(const bool bordered) + { + if (bordered != is_bordered_) { - + is_bordered_ = bordered; + InvalidateBorder(); } + } - void Control::OnGetFocusCore(FocusChangeEventArgs& args) + void Control::SetCursor(const Cursor::Ptr& cursor) + { + if (cursor != cursor_) { - + cursor_ = cursor; + const auto window = GetWindow(); + if (window && window->GetMouseHoverControl() == this) + window->UpdateCursor(); } + } - void Control::OnLoseFocusCore(FocusChangeEventArgs& args) + void Control::OnAddChild(Control* child) + { + if (auto window = GetWindow()) { - + child->TraverseDescendants([window](Control* control) { + control->OnAttachToWindow(window); + }); + window->RefreshControlList(); + InvalidateLayout(); } + } - void Control::RaiseGetFocusEvent(FocusChangeEventArgs& args) + void Control::OnRemoveChild(Control* child) + { + if (auto window = GetWindow()) { - OnGetFocusCore(args); - OnGetFocus(args); - get_focus_event.Raise(args); + child->TraverseDescendants([window](Control* control) { + control->OnDetachToWindow(window); + }); + window->RefreshControlList(); + InvalidateLayout(); } + } - void Control::RaiseLoseFocusEvent(FocusChangeEventArgs& args) - { - OnLoseFocusCore(args); - OnLoseFocus(args); - lose_focus_event.Raise(args); - } + void Control::OnAttachToWindow(Window* window) + { + window_ = window; + } - inline Size ThicknessToSize(const Thickness& thickness) - { - return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); - } + void Control::OnDetachToWindow(Window * window) + { + window_ = nullptr; + } - inline float AtLeast0(const float value) - { - return value < 0 ? 0 : value; - } + inline D2D1_RECT_F Convert(const Rect& rect) + { + return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); + } - Size Control::OnMeasureCore(const Size& available_size) + void Control::OnDraw(ID2D1DeviceContext* device_context) + { +#ifdef CRU_DEBUG_LAYOUT + if (GetWindow()->IsDebugLayout()) { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto border_size = Size::Zero(); - if (is_bordered_) - { - const auto border_width = GetBorderProperty().GetStrokeWidth(); - border_size = Size(border_width * 2.0f, border_width * 2.0f); - } - - // the total size of padding, border and margin - const auto outer_size = ThicknessToSize(layout_params->padding) + - ThicknessToSize(layout_params->margin) + border_size; - - - auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float - { - float length; - if (layout_length.mode == MeasureMode::Exactly) - length = layout_length.length; - else if (available_length > outer_length) - length = available_length - outer_length; - else - length = 0; - return Coerce(length, layout_length.min, layout_length.max); - }; - - // if padding, margin and border exceeded, then content size is 0. - const auto content_measure_size = Size( - get_content_measure_length(layout_params->width, available_size.width, outer_size.width), - get_content_measure_length(layout_params->height, available_size.height, outer_size.height) - ); - - const auto content_actual_size = OnMeasureContent(content_measure_size); - - auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float actual_length) -> float - { - // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" - if (layout_length.mode == MeasureMode::Stretch && actual_length < measure_length) - return measure_length; - return Coerce(actual_length, layout_length.min, std::nullopt); - }; - - const auto final_size = Size( - calculate_final_length(layout_params->width, content_measure_size.width, content_actual_size.width), - calculate_final_length(layout_params->height, content_measure_size.height, content_actual_size.height) - ) + outer_size; - - return final_size; + const auto resource = Application::GetInstance()->GetDebugLayoutResource(); + if (padding_geometry_ != nullptr) + device_context->FillGeometry(padding_geometry_.Get(), resource->padding_brush.Get()); + if (margin_geometry_ != nullptr) + device_context->FillGeometry(margin_geometry_.Get(), resource->margin_brush.Get()); + device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), resource->out_border_brush.Get()); } +#endif - void Control::OnLayoutCore(const Rect& rect) - { - const auto layout_params = GetLayoutParams(); - - auto border_width = 0.0f; - if (is_bordered_) - { - border_width = GetBorderProperty().GetStrokeWidth(); - } - - const Rect content_rect( - rect.left + layout_params->padding.left + layout_params->margin.right + border_width, - rect.top + layout_params->padding.top + layout_params->margin.top + border_width, - rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, - rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f + if (is_bordered_) + { + const auto border_rect = GetRect(RectRange::HalfBorder); + device_context->DrawRoundedRectangle( + D2D1::RoundedRect( + Convert(border_rect), + GetBorderProperty().GetRadiusX(), + GetBorderProperty().GetRadiusY() + ), + GetBorderProperty().GetBrush().Get(), + GetBorderProperty().GetStrokeWidth(), + GetBorderProperty().GetStrokeStyle().Get() ); - - if (content_rect.width < 0.0) - throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); - if (content_rect.height < 0.0) - throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); - - OnLayoutContent(content_rect); } + } - Size Control::OnMeasureContent(const Size& available_size) - { - auto max_child_size = Size::Zero(); - for (auto control: GetChildren()) - { - control->Measure(available_size); - const auto&& size = control->GetDesiredSize(); - if (max_child_size.width < size.width) - max_child_size.width = size.width; - if (max_child_size.height < size.height) - max_child_size.height = size.height; - } - return max_child_size; - } + void Control::OnDrawContent(ID2D1DeviceContext * device_context) + { - void Control::OnLayoutContent(const Rect& rect) - { - for (auto control: GetChildren()) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - control->Layout(Rect(Point( - calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), - calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) - ), size)); - } - } + } - void Control::CheckAndNotifyPositionChanged() - { - if (this->old_position_ != this->position_) - { - PositionChangedEventArgs args(this, this, this->old_position_, this->position_); - this->RaisePositionChangedEvent(args); - this->old_position_ = this->position_; - } - } + void Control::OnPositionChanged(PositionChangedEventArgs & args) + { - std::list<Control*> GetAncestorList(Control* control) - { - std::list<Control*> l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetParent(); - } - return l; - } + } - Control* FindLowestCommonAncestor(Control * left, Control * right) - { - if (left == nullptr || right == nullptr) - return nullptr; + void Control::OnSizeChanged(SizeChangedEventArgs & args) + { + } - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); + void Control::OnPositionChangedCore(PositionChangedEventArgs & args) + { - // the root is different - if (left_list.front() != right_list.front()) - return nullptr; + } - // find the last same control or the last control (one is ancestor of the other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) - { - if (left_i == left_list.cend()) - return *(--left_i); - if (right_i == right_list.cend()) - return *(--right_i); - if (*left_i != *right_i) - return *(--left_i); - ++left_i; - ++right_i; - } + namespace + { +#ifdef CRU_DEBUG_LAYOUT + Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in) + { + const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); + Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry; + ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); + Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry; + ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); + Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry; + ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); + Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; + ThrowIfFailed(result_geometry->Open(&sink)); + ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); + ThrowIfFailed(sink->Close()); + return result_geometry; } +#endif + } - Control * IsAncestorOrDescendant(Control * left, Control * right) - { - //Search up along the trunk from "left". Return if find "right". - auto control = left; - while (control != nullptr) - { - if (control == right) - return control; - control = control->GetParent(); - } - //Search up along the trunk from "right". Return if find "left". - control = right; - while (control != nullptr) - { - if (control == left) - return control; - control = control->GetParent(); - } - return nullptr; - } + void Control::OnSizeChangedCore(SizeChangedEventArgs & args) + { +#ifdef CRU_DEBUG_LAYOUT + margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); + padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); +#endif } + + void Control::RaisePositionChangedEvent(PositionChangedEventArgs& args) + { + OnPositionChangedCore(args); + OnPositionChanged(args); + position_changed_event.Raise(args); + } + + void Control::RaiseSizeChangedEvent(SizeChangedEventArgs& args) + { + OnSizeChangedCore(args); + OnSizeChanged(args); + size_changed_event.Raise(args); + } + + void Control::OnMouseEnter(MouseEventArgs & args) + { + } + + void Control::OnMouseLeave(MouseEventArgs & args) + { + } + + void Control::OnMouseMove(MouseEventArgs & args) + { + } + + void Control::OnMouseDown(MouseButtonEventArgs & args) + { + } + + void Control::OnMouseUp(MouseButtonEventArgs & args) + { + } + + void Control::OnMouseClick(MouseButtonEventArgs& args) + { + + } + + void Control::OnMouseEnterCore(MouseEventArgs & args) + { + is_mouse_inside_ = true; + } + + void Control::OnMouseLeaveCore(MouseEventArgs & args) + { + is_mouse_inside_ = false; + for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) + { + if (is_mouse_click_valid.second) + { + is_mouse_click_valid.second = false; + OnMouseClickEnd(is_mouse_click_valid.first); + } + } + } + + void Control::OnMouseMoveCore(MouseEventArgs & args) + { + + } + + void Control::OnMouseDownCore(MouseButtonEventArgs & args) + { + if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) + RequestFocus(); + is_mouse_click_valid_map_[args.GetMouseButton()] = true; + OnMouseClickBegin(args.GetMouseButton()); + } + + void Control::OnMouseUpCore(MouseButtonEventArgs & args) + { + if (is_mouse_click_valid_map_[args.GetMouseButton()]) + { + is_mouse_click_valid_map_[args.GetMouseButton()] = false; + RaiseMouseClickEvent(args); + OnMouseClickEnd(args.GetMouseButton()); + } + } + + void Control::OnMouseClickCore(MouseButtonEventArgs& args) + { + + } + + void Control::RaiseMouseEnterEvent(MouseEventArgs& args) + { + OnMouseEnterCore(args); + OnMouseEnter(args); + mouse_enter_event.Raise(args); + } + + void Control::RaiseMouseLeaveEvent(MouseEventArgs& args) + { + OnMouseLeaveCore(args); + OnMouseLeave(args); + mouse_leave_event.Raise(args); + } + + void Control::RaiseMouseMoveEvent(MouseEventArgs& args) + { + OnMouseMoveCore(args); + OnMouseMove(args); + mouse_move_event.Raise(args); + } + + void Control::RaiseMouseDownEvent(MouseButtonEventArgs& args) + { + OnMouseDownCore(args); + OnMouseDown(args); + mouse_down_event.Raise(args); + } + + void Control::RaiseMouseUpEvent(MouseButtonEventArgs& args) + { + OnMouseUpCore(args); + OnMouseUp(args); + mouse_up_event.Raise(args); + } + + void Control::RaiseMouseClickEvent(MouseButtonEventArgs& args) + { + OnMouseClickCore(args); + OnMouseClick(args); + mouse_click_event.Raise(args); + } + + void Control::OnMouseClickBegin(MouseButton button) + { + + } + + void Control::OnMouseClickEnd(MouseButton button) + { + + } + + void Control::OnKeyDown(KeyEventArgs& args) + { + } + + void Control::OnKeyUp(KeyEventArgs& args) + { + } + + void Control::OnChar(CharEventArgs& args) + { + } + + void Control::OnKeyDownCore(KeyEventArgs& args) + { + } + + void Control::OnKeyUpCore(KeyEventArgs& args) + { + } + + void Control::OnCharCore(CharEventArgs& args) + { + } + + void Control::RaiseKeyDownEvent(KeyEventArgs& args) + { + OnKeyDownCore(args); + OnKeyDown(args); + key_down_event.Raise(args); + } + + void Control::RaiseKeyUpEvent(KeyEventArgs& args) + { + OnKeyUpCore(args); + OnKeyUp(args); + key_up_event.Raise(args); + } + + void Control::RaiseCharEvent(CharEventArgs& args) + { + OnCharCore(args); + OnChar(args); + char_event.Raise(args); + } + + void Control::OnGetFocus(FocusChangeEventArgs& args) + { + + } + + void Control::OnLoseFocus(FocusChangeEventArgs& args) + { + + } + + void Control::OnGetFocusCore(FocusChangeEventArgs& args) + { + + } + + void Control::OnLoseFocusCore(FocusChangeEventArgs& args) + { + + } + + void Control::RaiseGetFocusEvent(FocusChangeEventArgs& args) + { + OnGetFocusCore(args); + OnGetFocus(args); + get_focus_event.Raise(args); + } + + void Control::RaiseLoseFocusEvent(FocusChangeEventArgs& args) + { + OnLoseFocusCore(args); + OnLoseFocus(args); + lose_focus_event.Raise(args); + } + + inline Size ThicknessToSize(const Thickness& thickness) + { + return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); + } + + inline float AtLeast0(const float value) + { + return value < 0 ? 0 : value; + } + + Size Control::OnMeasureCore(const Size& available_size) + { + const auto layout_params = GetLayoutParams(); + + if (!layout_params->Validate()) + throw std::runtime_error("LayoutParams is not valid. Please check it."); + + auto border_size = Size::Zero(); + if (is_bordered_) + { + const auto border_width = GetBorderProperty().GetStrokeWidth(); + border_size = Size(border_width * 2.0f, border_width * 2.0f); + } + + // the total size of padding, border and margin + const auto outer_size = ThicknessToSize(layout_params->padding) + + ThicknessToSize(layout_params->margin) + border_size; + + + auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float + { + float length; + if (layout_length.mode == MeasureMode::Exactly) + length = layout_length.length; + else if (available_length > outer_length) + length = available_length - outer_length; + else + length = 0; + return Coerce(length, layout_length.min, layout_length.max); + }; + + // if padding, margin and border exceeded, then content size is 0. + const auto content_measure_size = Size( + get_content_measure_length(layout_params->width, available_size.width, outer_size.width), + get_content_measure_length(layout_params->height, available_size.height, outer_size.height) + ); + + const auto content_actual_size = OnMeasureContent(content_measure_size); + + auto&& calculate_final_length = [](const LayoutSideParams& layout_length, const float measure_length, const float actual_length) -> float + { + // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" + if (layout_length.mode == MeasureMode::Stretch && actual_length < measure_length) + return measure_length; + return Coerce(actual_length, layout_length.min, std::nullopt); + }; + + const auto final_size = Size( + calculate_final_length(layout_params->width, content_measure_size.width, content_actual_size.width), + calculate_final_length(layout_params->height, content_measure_size.height, content_actual_size.height) + ) + outer_size; + + return final_size; + } + + void Control::OnLayoutCore(const Rect& rect) + { + const auto layout_params = GetLayoutParams(); + + auto border_width = 0.0f; + if (is_bordered_) + { + border_width = GetBorderProperty().GetStrokeWidth(); + } + + const Rect content_rect( + rect.left + layout_params->padding.left + layout_params->margin.right + border_width, + rect.top + layout_params->padding.top + layout_params->margin.top + border_width, + rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, + rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f + ); + + if (content_rect.width < 0.0) + throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); + if (content_rect.height < 0.0) + throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); + + OnLayoutContent(content_rect); + } + + Size Control::OnMeasureContent(const Size& available_size) + { + auto max_child_size = Size::Zero(); + for (auto control: GetChildren()) + { + control->Measure(available_size); + const auto&& size = control->GetDesiredSize(); + if (max_child_size.width < size.width) + max_child_size.width = size.width; + if (max_child_size.height < size.height) + max_child_size.height = size.height; + } + return max_child_size; + } + + void Control::OnLayoutContent(const Rect& rect) + { + for (auto control: GetChildren()) + { + const auto layout_params = control->GetLayoutParams(); + const auto size = control->GetDesiredSize(); + + auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float + { + switch (alignment) + { + case Alignment::Center: + return anchor + (layout_length - control_length) / 2; + case Alignment::Start: + return anchor; + case Alignment::End: + return anchor + layout_length - control_length; + default: + UnreachableCode(); + } + }; + + control->Layout(Rect(Point( + calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), + calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) + ), size)); + } + } + + void Control::CheckAndNotifyPositionChanged() + { + if (this->old_position_ != this->position_) + { + PositionChangedEventArgs args(this, this, this->old_position_, this->position_); + this->RaisePositionChangedEvent(args); + this->old_position_ = this->position_; + } + } + + std::list<Control*> GetAncestorList(Control* control) + { + std::list<Control*> l; + while (control != nullptr) + { + l.push_front(control); + control = control->GetParent(); + } + return l; + } + + Control* FindLowestCommonAncestor(Control * left, Control * right) + { + if (left == nullptr || right == nullptr) + return nullptr; + + auto&& left_list = GetAncestorList(left); + auto&& right_list = GetAncestorList(right); + + // the root is different + if (left_list.front() != right_list.front()) + return nullptr; + + // find the last same control or the last control (one is ancestor of the other) + auto left_i = left_list.cbegin(); + auto right_i = right_list.cbegin(); + while (true) + { + if (left_i == left_list.cend()) + return *(--left_i); + if (right_i == right_list.cend()) + return *(--right_i); + if (*left_i != *right_i) + return *(--left_i); + ++left_i; + ++right_i; + } + } + + Control * IsAncestorOrDescendant(Control * left, Control * right) + { + //Search up along the trunk from "left". Return if find "right". + auto control = left; + while (control != nullptr) + { + if (control == right) + return control; + control = control->GetParent(); + } + //Search up along the trunk from "right". Return if find "left". + control = right; + while (control != nullptr) + { + if (control == left) + return control; + control = control->GetParent(); + } + return nullptr; + } } diff --git a/src/ui/control.h b/src/ui/control.h index 9ee657f7..020f959e 100644 --- a/src/ui/control.h +++ b/src/ui/control.h @@ -14,435 +14,432 @@ #include "border_property.h" #include "cursor.h" -namespace cru +namespace cru::ui { - namespace ui - { - class Control; - class Window; + class Control; + class Window; - //the position cache - struct ControlPositionCache - { - //The lefttop relative to the ancestor. - Point lefttop_position_absolute; - }; + //the position cache + struct ControlPositionCache + { + //The lefttop relative to the ancestor. + Point lefttop_position_absolute; + }; - class Control : public Object - { - friend class Window; - friend class LayoutManager; + class Control : public Object + { + friend class Window; + friend class LayoutManager; - protected: - struct WindowConstructorTag {}; //Used for constructor for class Window. + protected: + struct WindowConstructorTag {}; //Used for constructor for class Window. - explicit Control(bool container = false); + explicit Control(bool container = false); - // Used only for creating Window. It will set window_ as window. - Control(WindowConstructorTag, Window* window); + // Used only for creating Window. It will set window_ as window. + Control(WindowConstructorTag, Window* window); - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override; + public: + Control(const Control& other) = delete; + Control(Control&& other) = delete; + Control& operator=(const Control& other) = delete; + Control& operator=(Control&& other) = delete; + ~Control() override; - public: + public: - //*************** region: tree *************** - virtual StringView GetControlType() const = 0; + //*************** region: tree *************** + virtual StringView GetControlType() const = 0; - bool IsContainer() const - { - return is_container_; - } + bool IsContainer() const + { + return is_container_; + } - //Get parent of control, return nullptr if it has no parent. - Control* GetParent() const - { - return parent_; - } + //Get parent of control, return nullptr if it has no parent. + Control* GetParent() const + { + return parent_; + } - //Return a immutable vector of all children. - const std::vector<Control*>& GetChildren() const; + //Return a immutable vector of all children. + const std::vector<Control*>& GetChildren() const; - //Add a child at tail. - void AddChild(Control* control); + //Add a child at tail. + void AddChild(Control* control); - //Add a child before the position. - void AddChild(Control* control, int position); + //Add a child before the position. + void AddChild(Control* control, int position); - //Remove a child. - void RemoveChild(Control* child); + //Remove a child. + void RemoveChild(Control* child); - //Remove a child at specified position. - void RemoveChild(int position); + //Remove a child at specified position. + void RemoveChild(int position); - //Get the ancestor of the control. - Control* GetAncestor(); + //Get the ancestor of the control. + Control* GetAncestor(); - //Get the window if attached, otherwise, return nullptr. - Window* GetWindow() const - { - return window_; - } + //Get the window if attached, otherwise, return nullptr. + Window* GetWindow() const + { + return window_; + } - //Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function<void(Control*)>& predicate); + //Traverse the tree rooted the control including itself. + void TraverseDescendants(const std::function<void(Control*)>& predicate); - //*************** region: position and size *************** - // Position and size part must be isolated from layout part. - // All the operations in this part must be done independently. - // And layout part must use api of this part. + //*************** region: position and size *************** + // Position and size part must be isolated from layout part. + // All the operations in this part must be done independently. + // And layout part must use api of this part. - //Get the lefttop relative to its parent. - virtual Point GetPositionRelative(); + //Get the lefttop relative to its parent. + virtual Point GetPositionRelative(); - //Set the lefttop relative to its parent. - virtual void SetPositionRelative(const Point& position); + //Set the lefttop relative to its parent. + virtual void SetPositionRelative(const Point& position); - //Get the actual size. - virtual Size GetSize(); + //Get the actual size. + virtual Size GetSize(); - //Set the actual size directly without re-layout. - virtual void SetSize(const Size& size); + //Set the actual size directly without re-layout. + virtual void SetSize(const Size& size); - //Get lefttop relative to ancestor. This is only valid when - //attached to window. Notice that the value is cached. - //You can invalidate and recalculate it by calling "InvalidatePositionCache". - Point GetPositionAbsolute() const; + //Get lefttop relative to ancestor. This is only valid when + //attached to window. Notice that the value is cached. + //You can invalidate and recalculate it by calling "InvalidatePositionCache". + Point GetPositionAbsolute() const; - //Local point to absolute point. - Point ControlToWindow(const Point& point) const; + //Local point to absolute point. + Point ControlToWindow(const Point& point) const; - //Absolute point to local point. - Point WindowToControl(const Point& point) const; + //Absolute point to local point. + Point WindowToControl(const Point& point) const; - virtual bool IsPointInside(const Point& point); + virtual bool IsPointInside(const Point& point); - //*************** region: graphic *************** + //*************** region: graphic *************** - //Draw this control and its child controls. - void Draw(ID2D1DeviceContext* device_context); + //Draw this control and its child controls. + void Draw(ID2D1DeviceContext* device_context); - virtual void Repaint(); + virtual void Repaint(); - //*************** region: focus *************** + //*************** region: focus *************** - bool RequestFocus(); + bool RequestFocus(); - bool HasFocus(); + bool HasFocus(); - bool IsFocusOnPressed() const - { - return is_focus_on_pressed_; - } + bool IsFocusOnPressed() const + { + return is_focus_on_pressed_; + } - void SetFocusOnPressed(const bool value) - { - is_focus_on_pressed_ = value; - } + void SetFocusOnPressed(const bool value) + { + is_focus_on_pressed_ = value; + } - //*************** region: layout *************** + //*************** region: layout *************** - void InvalidateLayout(); + void InvalidateLayout(); - void Measure(const Size& available_size); + void Measure(const Size& available_size); - void Layout(const Rect& rect); + void Layout(const Rect& rect); - Size GetDesiredSize() const; + Size GetDesiredSize() const; - void SetDesiredSize(const Size& desired_size); + void SetDesiredSize(const Size& desired_size); - BasicLayoutParams* GetLayoutParams() - { - return &layout_params_; - } + BasicLayoutParams* GetLayoutParams() + { + return &layout_params_; + } - Rect GetRect(RectRange range); + Rect GetRect(RectRange range); - Point TransformPoint(const Point& point, RectRange from = RectRange::Margin, RectRange to = RectRange::Content); + Point TransformPoint(const Point& point, RectRange from = RectRange::Margin, RectRange to = RectRange::Content); - //*************** region: border *************** + //*************** region: border *************** - BorderProperty& GetBorderProperty() - { - return border_property_; - } + BorderProperty& GetBorderProperty() + { + return border_property_; + } - void InvalidateBorder(); + void InvalidateBorder(); - bool IsBordered() const - { - return is_bordered_; - } + bool IsBordered() const + { + return is_bordered_; + } - void SetBordered(bool bordered); + void SetBordered(bool bordered); - //*************** region: additional properties *************** - template <typename T> - std::optional<T> GetAdditionalProperty(const String& key) + //*************** region: additional properties *************** + template <typename T> + std::optional<T> GetAdditionalProperty(const String& key) + { + try { - try - { - const auto find_result = additional_properties_.find(key); - if (find_result != additional_properties_.cend()) - return std::any_cast<T>(find_result->second); - else - return std::nullopt; - } - catch (const std::bad_any_cast&) - { - throw std::runtime_error(Format("Key \"{}\" is not of the type {}.", ToUtf8String(key), typeid(T).name())); - } + const auto find_result = additional_properties_.find(key); + if (find_result != additional_properties_.cend()) + return std::any_cast<T>(find_result->second); + else + return std::nullopt; } - - template <typename T> - void SetAdditionalProperty(const String& key, const T& value) + catch (const std::bad_any_cast&) { - additional_properties_[key] = std::make_any<T>(value); + throw std::runtime_error(Format("Key \"{}\" is not of the type {}.", ToUtf8String(key), typeid(T).name())); } + } - template <typename T> - void SetAdditionalProperty(const String& key, T&& value) - { - additional_properties_[key] = std::make_any<T>(std::move(value)); - } + template <typename T> + void SetAdditionalProperty(const String& key, const T& value) + { + additional_properties_[key] = std::make_any<T>(value); + } - - //*************** region: cursor *************** - // If cursor is set to null, then it uses parent's cursor. - // Window's cursor can't be null. + template <typename T> + void SetAdditionalProperty(const String& key, T&& value) + { + additional_properties_[key] = std::make_any<T>(std::move(value)); + } - Cursor::Ptr GetCursor() const - { - return cursor_; - } + + //*************** region: cursor *************** + // If cursor is set to null, then it uses parent's cursor. + // Window's cursor can't be null. - void SetCursor(const Cursor::Ptr& cursor); - - - //*************** region: events *************** - //Raised when mouse enter the control. - events::MouseEvent mouse_enter_event; - //Raised when mouse is leave the control. - events::MouseEvent mouse_leave_event; - //Raised when mouse is move in the control. - events::MouseEvent mouse_move_event; - //Raised when a mouse button is pressed in the control. - events::MouseButtonEvent mouse_down_event; - //Raised when a mouse button is released in the control. - events::MouseButtonEvent mouse_up_event; - //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations. - events::MouseButtonEvent mouse_click_event; - - events::KeyEvent key_down_event; - events::KeyEvent key_up_event; - events::CharEvent char_event; - - events::FocusChangeEvent get_focus_event; - events::FocusChangeEvent lose_focus_event; - - events::DrawEvent draw_event; - - events::PositionChangedEvent position_changed_event; - events::SizeChangedEvent size_changed_event; - - protected: - //Invoked when a child is added. Overrides should invoke base. - virtual void OnAddChild(Control* child); - //Invoked when a child is removed. Overrides should invoke base. - virtual void OnRemoveChild(Control* child); - - //Invoked when the control is attached to a window. Overrides should invoke base. - virtual void OnAttachToWindow(Window* window); - //Invoked when the control is detached to a window. Overrides should invoke base. - virtual void OnDetachToWindow(Window* window); - - private: - void OnDraw(ID2D1DeviceContext* device_context); - - protected: - virtual void OnDrawContent(ID2D1DeviceContext* device_context); - - // For a event, the window event system will first dispatch event to core functions. - // Therefore for particular controls, you should do essential actions in core functions, - // and override version should invoke base version. The base core function - // in "Control" class will call corresponding non-core function and call "Raise" on - // event objects. So user custom actions should be done by overriding non-core function - // and calling the base version is optional. - - //*************** region: position and size event *************** - virtual void OnPositionChanged(events::PositionChangedEventArgs& args); - virtual void OnSizeChanged(events::SizeChangedEventArgs& args); - - virtual void OnPositionChangedCore(events::PositionChangedEventArgs& args); - virtual void OnSizeChangedCore(events::SizeChangedEventArgs& args); - - void RaisePositionChangedEvent(events::PositionChangedEventArgs& args); - void RaiseSizeChangedEvent(events::SizeChangedEventArgs& args); - - //*************** region: mouse event *************** - virtual void OnMouseEnter(events::MouseEventArgs& args); - virtual void OnMouseLeave(events::MouseEventArgs& args); - virtual void OnMouseMove(events::MouseEventArgs& args); - virtual void OnMouseDown(events::MouseButtonEventArgs& args); - virtual void OnMouseUp(events::MouseButtonEventArgs& args); - virtual void OnMouseClick(events::MouseButtonEventArgs& args); - - virtual void OnMouseEnterCore(events::MouseEventArgs& args); - virtual void OnMouseLeaveCore(events::MouseEventArgs& args); - virtual void OnMouseMoveCore(events::MouseEventArgs& args); - virtual void OnMouseDownCore(events::MouseButtonEventArgs& args); - virtual void OnMouseUpCore(events::MouseButtonEventArgs& args); - virtual void OnMouseClickCore(events::MouseButtonEventArgs& args); - - void RaiseMouseEnterEvent(events::MouseEventArgs& args); - void RaiseMouseLeaveEvent(events::MouseEventArgs& args); - void RaiseMouseMoveEvent(events::MouseEventArgs& args); - void RaiseMouseDownEvent(events::MouseButtonEventArgs& args); - void RaiseMouseUpEvent(events::MouseButtonEventArgs& args); - void RaiseMouseClickEvent(events::MouseButtonEventArgs& args); - - virtual void OnMouseClickBegin(MouseButton button); - virtual void OnMouseClickEnd(MouseButton button); - - //*************** region: keyboard event *************** - virtual void OnKeyDown(events::KeyEventArgs& args); - virtual void OnKeyUp(events::KeyEventArgs& args); - virtual void OnChar(events::CharEventArgs& args); - - virtual void OnKeyDownCore(events::KeyEventArgs& args); - virtual void OnKeyUpCore(events::KeyEventArgs& args); - virtual void OnCharCore(events::CharEventArgs& args); - - void RaiseKeyDownEvent(events::KeyEventArgs& args); - void RaiseKeyUpEvent(events::KeyEventArgs& args); - void RaiseCharEvent(events::CharEventArgs& args); - - //*************** region: focus event *************** - virtual void OnGetFocus(events::FocusChangeEventArgs& args); - virtual void OnLoseFocus(events::FocusChangeEventArgs& args); - - virtual void OnGetFocusCore(events::FocusChangeEventArgs& args); - virtual void OnLoseFocusCore(events::FocusChangeEventArgs& args); - - void RaiseGetFocusEvent(events::FocusChangeEventArgs& args); - void RaiseLoseFocusEvent(events::FocusChangeEventArgs& args); - - //*************** region: layout *************** - Size OnMeasureCore(const Size& available_size); - void OnLayoutCore(const Rect& rect); - - virtual Size OnMeasureContent(const Size& available_size); - virtual void OnLayoutContent(const Rect& rect); - - private: - // Only for layout manager to use. - // Check if the old position is updated to current position. - // If not, then a notify of position change and update will - // be done. - void CheckAndNotifyPositionChanged(); - - void ThrowIfNotContainer() const - { - if (!is_container_) - throw std::runtime_error("You can't perform such operation on a non-container control."); - } + Cursor::Ptr GetCursor() const + { + return cursor_; + } - private: - bool is_container_; + void SetCursor(const Cursor::Ptr& cursor); + + + //*************** region: events *************** + //Raised when mouse enter the control. + events::MouseEvent mouse_enter_event; + //Raised when mouse is leave the control. + events::MouseEvent mouse_leave_event; + //Raised when mouse is move in the control. + events::MouseEvent mouse_move_event; + //Raised when a mouse button is pressed in the control. + events::MouseButtonEvent mouse_down_event; + //Raised when a mouse button is released in the control. + events::MouseButtonEvent mouse_up_event; + //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations. + events::MouseButtonEvent mouse_click_event; + + events::KeyEvent key_down_event; + events::KeyEvent key_up_event; + events::CharEvent char_event; + + events::FocusChangeEvent get_focus_event; + events::FocusChangeEvent lose_focus_event; + + events::DrawEvent draw_event; + + events::PositionChangedEvent position_changed_event; + events::SizeChangedEvent size_changed_event; + + protected: + //Invoked when a child is added. Overrides should invoke base. + virtual void OnAddChild(Control* child); + //Invoked when a child is removed. Overrides should invoke base. + virtual void OnRemoveChild(Control* child); + + //Invoked when the control is attached to a window. Overrides should invoke base. + virtual void OnAttachToWindow(Window* window); + //Invoked when the control is detached to a window. Overrides should invoke base. + virtual void OnDetachToWindow(Window* window); + + private: + void OnDraw(ID2D1DeviceContext* device_context); + + protected: + virtual void OnDrawContent(ID2D1DeviceContext* device_context); + + // For a event, the window event system will first dispatch event to core functions. + // Therefore for particular controls, you should do essential actions in core functions, + // and override version should invoke base version. The base core function + // in "Control" class will call corresponding non-core function and call "Raise" on + // event objects. So user custom actions should be done by overriding non-core function + // and calling the base version is optional. + + //*************** region: position and size event *************** + virtual void OnPositionChanged(events::PositionChangedEventArgs& args); + virtual void OnSizeChanged(events::SizeChangedEventArgs& args); + + virtual void OnPositionChangedCore(events::PositionChangedEventArgs& args); + virtual void OnSizeChangedCore(events::SizeChangedEventArgs& args); + + void RaisePositionChangedEvent(events::PositionChangedEventArgs& args); + void RaiseSizeChangedEvent(events::SizeChangedEventArgs& args); + + //*************** region: mouse event *************** + virtual void OnMouseEnter(events::MouseEventArgs& args); + virtual void OnMouseLeave(events::MouseEventArgs& args); + virtual void OnMouseMove(events::MouseEventArgs& args); + virtual void OnMouseDown(events::MouseButtonEventArgs& args); + virtual void OnMouseUp(events::MouseButtonEventArgs& args); + virtual void OnMouseClick(events::MouseButtonEventArgs& args); + + virtual void OnMouseEnterCore(events::MouseEventArgs& args); + virtual void OnMouseLeaveCore(events::MouseEventArgs& args); + virtual void OnMouseMoveCore(events::MouseEventArgs& args); + virtual void OnMouseDownCore(events::MouseButtonEventArgs& args); + virtual void OnMouseUpCore(events::MouseButtonEventArgs& args); + virtual void OnMouseClickCore(events::MouseButtonEventArgs& args); + + void RaiseMouseEnterEvent(events::MouseEventArgs& args); + void RaiseMouseLeaveEvent(events::MouseEventArgs& args); + void RaiseMouseMoveEvent(events::MouseEventArgs& args); + void RaiseMouseDownEvent(events::MouseButtonEventArgs& args); + void RaiseMouseUpEvent(events::MouseButtonEventArgs& args); + void RaiseMouseClickEvent(events::MouseButtonEventArgs& args); + + virtual void OnMouseClickBegin(MouseButton button); + virtual void OnMouseClickEnd(MouseButton button); + + //*************** region: keyboard event *************** + virtual void OnKeyDown(events::KeyEventArgs& args); + virtual void OnKeyUp(events::KeyEventArgs& args); + virtual void OnChar(events::CharEventArgs& args); + + virtual void OnKeyDownCore(events::KeyEventArgs& args); + virtual void OnKeyUpCore(events::KeyEventArgs& args); + virtual void OnCharCore(events::CharEventArgs& args); + + void RaiseKeyDownEvent(events::KeyEventArgs& args); + void RaiseKeyUpEvent(events::KeyEventArgs& args); + void RaiseCharEvent(events::CharEventArgs& args); + + //*************** region: focus event *************** + virtual void OnGetFocus(events::FocusChangeEventArgs& args); + virtual void OnLoseFocus(events::FocusChangeEventArgs& args); + + virtual void OnGetFocusCore(events::FocusChangeEventArgs& args); + virtual void OnLoseFocusCore(events::FocusChangeEventArgs& args); + + void RaiseGetFocusEvent(events::FocusChangeEventArgs& args); + void RaiseLoseFocusEvent(events::FocusChangeEventArgs& args); + + //*************** region: layout *************** + Size OnMeasureCore(const Size& available_size); + void OnLayoutCore(const Rect& rect); + + virtual Size OnMeasureContent(const Size& available_size); + virtual void OnLayoutContent(const Rect& rect); + + private: + // Only for layout manager to use. + // Check if the old position is updated to current position. + // If not, then a notify of position change and update will + // be done. + void CheckAndNotifyPositionChanged(); + + void ThrowIfNotContainer() const + { + if (!is_container_) + throw std::runtime_error("You can't perform such operation on a non-container control."); + } - protected: - Window * window_ = nullptr; // protected for Window class to write it as itself in constructor. + private: + bool is_container_; - private: - Control * parent_ = nullptr; - std::vector<Control*> children_{}; + protected: + Window * window_ = nullptr; // protected for Window class to write it as itself in constructor. - // When position is changed and notification hasn't been - // sent, it will be the old position. When position is changed - // more than once, it will be the oldest position since last - // notification. If notification has been sent, it will be updated - // to position_. - Point old_position_ = Point::Zero(); - Point position_ = Point::Zero(); - Size size_ = Size::Zero(); + private: + Control * parent_ = nullptr; + std::vector<Control*> children_{}; - ControlPositionCache position_cache_{}; + // When position is changed and notification hasn't been + // sent, it will be the old position. When position is changed + // more than once, it will be the oldest position since last + // notification. If notification has been sent, it will be updated + // to position_. + Point old_position_ = Point::Zero(); + Point position_ = Point::Zero(); + Size size_ = Size::Zero(); - bool is_mouse_inside_ = false; + ControlPositionCache position_cache_{}; - std::unordered_map<MouseButton, bool> is_mouse_click_valid_map_ - { - { MouseButton::Left, true }, - { MouseButton::Middle, true }, - { MouseButton::Right, true } - }; // used for clicking determination + bool is_mouse_inside_ = false; - BasicLayoutParams layout_params_{}; - Size desired_size_ = Size::Zero(); + std::unordered_map<MouseButton, bool> is_mouse_click_valid_map_ + { + { MouseButton::Left, true }, + { MouseButton::Middle, true }, + { MouseButton::Right, true } + }; // used for clicking determination - bool is_bordered_ = false; - BorderProperty border_property_; + BasicLayoutParams layout_params_{}; + Size desired_size_ = Size::Zero(); - std::unordered_map<String, std::any> additional_properties_{}; + bool is_bordered_ = false; + BorderProperty border_property_; - bool is_focus_on_pressed_ = true; + std::unordered_map<String, std::any> additional_properties_{}; + + bool is_focus_on_pressed_ = true; #ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr<ID2D1Geometry> margin_geometry_; - Microsoft::WRL::ComPtr<ID2D1Geometry> padding_geometry_; + Microsoft::WRL::ComPtr<ID2D1Geometry> margin_geometry_; + Microsoft::WRL::ComPtr<ID2D1Geometry> padding_geometry_; #endif - Cursor::Ptr cursor_{}; - }; + Cursor::Ptr cursor_{}; + }; - // Find the lowest common ancestor. - // Return nullptr if "left" and "right" are not in the same tree. - Control* FindLowestCommonAncestor(Control* left, Control* right); + // Find the lowest common ancestor. + // Return nullptr if "left" and "right" are not in the same tree. + Control* FindLowestCommonAncestor(Control* left, Control* right); - // Return the ancestor if one control is the ancestor of the other one, otherwise nullptr. - Control* IsAncestorOrDescendant(Control* left, Control* right); + // Return the ancestor if one control is the ancestor of the other one, otherwise nullptr. + Control* IsAncestorOrDescendant(Control* left, Control* right); - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - return control; - } + template <typename TControl, typename... Args> + TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args) + { + static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); + TControl* control = TControl::Create(std::forward<Args>(args)...); + control->GetLayoutParams()->width = width; + control->GetLayoutParams()->height = height; + return control; + } - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } - - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } + template <typename TControl, typename... Args> + TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) + { + static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); + TControl* control = TControl::Create(std::forward<Args>(args)...); + control->GetLayoutParams()->padding = padding; + control->GetLayoutParams()->margin = margin; + return control; + } - using ControlList = std::initializer_list<Control*>; + template <typename TControl, typename... Args> + TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) + { + static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); + TControl* control = TControl::Create(std::forward<Args>(args)...); + control->GetLayoutParams()->width = width; + control->GetLayoutParams()->height = height; + control->GetLayoutParams()->padding = padding; + control->GetLayoutParams()->margin = margin; + return control; } + + using ControlList = std::initializer_list<Control*>; } diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp index 7c52684d..e6c7fd7e 100644 --- a/src/ui/controls/text_block.cpp +++ b/src/ui/controls/text_block.cpp @@ -2,22 +2,16 @@ #include "ui/window.h" -namespace cru +namespace cru::ui::controls { - namespace ui + TextBlock::TextBlock(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format, + const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : TextControl(init_text_format, init_brush) { - namespace controls - { - TextBlock::TextBlock(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format, - const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : TextControl(init_text_format, init_brush) - { - } + } - StringView TextBlock::GetControlType() const - { - return control_type; - } - } + StringView TextBlock::GetControlType() const + { + return control_type; } } diff --git a/src/ui/cursor.h b/src/ui/cursor.h index 3c09b35e..ccb21d32 100644 --- a/src/ui/cursor.h +++ b/src/ui/cursor.h @@ -2,7 +2,6 @@ #include "system_headers.h" #include <memory> -#include <unordered_map> #include "base.h" diff --git a/src/ui/events/ui_event.cpp b/src/ui/events/ui_event.cpp index 5e9ca452..9f5185ce 100644 --- a/src/ui/events/ui_event.cpp +++ b/src/ui/events/ui_event.cpp @@ -2,18 +2,12 @@ #include "ui/control.h" -namespace cru +namespace cru::ui::events { - namespace ui + Point MouseEventArgs::GetPoint(Control* control, const RectRange range) const { - namespace events - { - Point MouseEventArgs::GetPoint(Control* control, const RectRange range) const - { - if (point_.has_value()) - return control->TransformPoint(control->WindowToControl(point_.value()), RectRange::Margin, range); - return Point(); - } - } + if (point_.has_value()) + return control->TransformPoint(control->WindowToControl(point_.value()), RectRange::Margin, range); + return Point(); } } diff --git a/src/ui/events/ui_event.h b/src/ui/events/ui_event.h index 1a6f4046..24429f04 100644 --- a/src/ui/events/ui_event.h +++ b/src/ui/events/ui_event.h @@ -8,311 +8,308 @@ #include "ui/ui_base.h" #include "ui/layout_base.h" -namespace cru +namespace cru::ui { - namespace ui + class Control; +} + +namespace cru::ui::events +{ + class UiEventArgs : public BasicEventArgs + { + public: + UiEventArgs(Object* sender, Object* original_sender) + : BasicEventArgs(sender), original_sender_(original_sender) + { + + } + + UiEventArgs(const UiEventArgs& other) = default; + UiEventArgs(UiEventArgs&& other) = default; + UiEventArgs& operator=(const UiEventArgs& other) = default; + UiEventArgs& operator=(UiEventArgs&& other) = default; + ~UiEventArgs() override = default; + + Object* GetOriginalSender() const + { + return original_sender_; + } + + private: + Object* original_sender_; + }; + + + class MouseEventArgs : public UiEventArgs + { + public: + MouseEventArgs(Object* sender, Object* original_sender, const std::optional<Point>& point = std::nullopt) + : UiEventArgs(sender, original_sender), point_(point) + { + + } + MouseEventArgs(const MouseEventArgs& other) = default; + MouseEventArgs(MouseEventArgs&& other) = default; + MouseEventArgs& operator=(const MouseEventArgs& other) = default; + MouseEventArgs& operator=(MouseEventArgs&& other) = default; + ~MouseEventArgs() override = default; + + Point GetPoint(Control* control, RectRange range = RectRange::Content) const; + + private: + std::optional<Point> point_; + }; + + + class MouseButtonEventArgs : public MouseEventArgs + { + public: + MouseButtonEventArgs(Object* sender, Object* original_sender, const Point& point, const MouseButton button) + : MouseEventArgs(sender, original_sender, point), button_(button) + { + + } + MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; + MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; + ~MouseButtonEventArgs() override = default; + + MouseButton GetMouseButton() const + { + return button_; + } + + private: + MouseButton button_; + }; + + + class DrawEventArgs : public UiEventArgs + { + public: + DrawEventArgs(Object* sender, Object* original_sender, ID2D1DeviceContext* device_context) + : UiEventArgs(sender, original_sender), device_context_(device_context) + { + + } + DrawEventArgs(const DrawEventArgs& other) = default; + DrawEventArgs(DrawEventArgs&& other) = default; + DrawEventArgs& operator=(const DrawEventArgs& other) = default; + DrawEventArgs& operator=(DrawEventArgs&& other) = default; + ~DrawEventArgs() = default; + + ID2D1DeviceContext* GetDeviceContext() const + { + return device_context_; + } + + private: + ID2D1DeviceContext * device_context_; + }; + + + class PositionChangedEventArgs : public UiEventArgs { - class Control; + public: + PositionChangedEventArgs(Object* sender, Object* original_sender, const Point& old_position, const Point& new_position) + : UiEventArgs(sender, original_sender), old_position_(old_position), new_position_(new_position) + { - namespace events + } + PositionChangedEventArgs(const PositionChangedEventArgs& other) = default; + PositionChangedEventArgs(PositionChangedEventArgs&& other) = default; + PositionChangedEventArgs& operator=(const PositionChangedEventArgs& other) = default; + PositionChangedEventArgs& operator=(PositionChangedEventArgs&& other) = default; + ~PositionChangedEventArgs() override = default; + + Point GetOldPosition() const + { + return old_position_; + } + + Point GetNewPosition() const + { + return new_position_; + } + + private: + Point old_position_; + Point new_position_; + }; + + + class SizeChangedEventArgs : public UiEventArgs + { + public: + SizeChangedEventArgs(Object* sender, Object* original_sender, const Size& old_size, const Size& new_size) + : UiEventArgs(sender, original_sender), old_size_(old_size), new_size_(new_size) { - class UiEventArgs : public BasicEventArgs - { - public: - UiEventArgs(Object* sender, Object* original_sender) - : BasicEventArgs(sender), original_sender_(original_sender) - { - - } - - UiEventArgs(const UiEventArgs& other) = default; - UiEventArgs(UiEventArgs&& other) = default; - UiEventArgs& operator=(const UiEventArgs& other) = default; - UiEventArgs& operator=(UiEventArgs&& other) = default; - ~UiEventArgs() override = default; - - Object* GetOriginalSender() const - { - return original_sender_; - } - - private: - Object* original_sender_; - }; - - - class MouseEventArgs : public UiEventArgs - { - public: - MouseEventArgs(Object* sender, Object* original_sender, const std::optional<Point>& point = std::nullopt) - : UiEventArgs(sender, original_sender), point_(point) - { - - } - MouseEventArgs(const MouseEventArgs& other) = default; - MouseEventArgs(MouseEventArgs&& other) = default; - MouseEventArgs& operator=(const MouseEventArgs& other) = default; - MouseEventArgs& operator=(MouseEventArgs&& other) = default; - ~MouseEventArgs() override = default; - - Point GetPoint(Control* control, RectRange range = RectRange::Content) const; - - private: - std::optional<Point> point_; - }; - - - class MouseButtonEventArgs : public MouseEventArgs - { - public: - MouseButtonEventArgs(Object* sender, Object* original_sender, const Point& point, const MouseButton button) - : MouseEventArgs(sender, original_sender, point), button_(button) - { - - } - MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; - MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; - ~MouseButtonEventArgs() override = default; - - MouseButton GetMouseButton() const - { - return button_; - } - - private: - MouseButton button_; - }; - - - class DrawEventArgs : public UiEventArgs - { - public: - DrawEventArgs(Object* sender, Object* original_sender, ID2D1DeviceContext* device_context) - : UiEventArgs(sender, original_sender), device_context_(device_context) - { - - } - DrawEventArgs(const DrawEventArgs& other) = default; - DrawEventArgs(DrawEventArgs&& other) = default; - DrawEventArgs& operator=(const DrawEventArgs& other) = default; - DrawEventArgs& operator=(DrawEventArgs&& other) = default; - ~DrawEventArgs() = default; - - ID2D1DeviceContext* GetDeviceContext() const - { - return device_context_; - } - - private: - ID2D1DeviceContext * device_context_; - }; - - - class PositionChangedEventArgs : public UiEventArgs - { - public: - PositionChangedEventArgs(Object* sender, Object* original_sender, const Point& old_position, const Point& new_position) - : UiEventArgs(sender, original_sender), old_position_(old_position), new_position_(new_position) - { - - } - PositionChangedEventArgs(const PositionChangedEventArgs& other) = default; - PositionChangedEventArgs(PositionChangedEventArgs&& other) = default; - PositionChangedEventArgs& operator=(const PositionChangedEventArgs& other) = default; - PositionChangedEventArgs& operator=(PositionChangedEventArgs&& other) = default; - ~PositionChangedEventArgs() override = default; - - Point GetOldPosition() const - { - return old_position_; - } - - Point GetNewPosition() const - { - return new_position_; - } - - private: - Point old_position_; - Point new_position_; - }; - - - class SizeChangedEventArgs : public UiEventArgs - { - public: - SizeChangedEventArgs(Object* sender, Object* original_sender, const Size& old_size, const Size& new_size) - : UiEventArgs(sender, original_sender), old_size_(old_size), new_size_(new_size) - { - - } - SizeChangedEventArgs(const SizeChangedEventArgs& other) = default; - SizeChangedEventArgs(SizeChangedEventArgs&& other) = default; - SizeChangedEventArgs& operator=(const SizeChangedEventArgs& other) = default; - SizeChangedEventArgs& operator=(SizeChangedEventArgs&& other) = default; - ~SizeChangedEventArgs() override = default; - - Size GetOldSize() const - { - return old_size_; - } - - Size GetNewSize() const - { - return new_size_; - } - - private: - Size old_size_; - Size new_size_; - }; - - class FocusChangeEventArgs : public UiEventArgs - { - public: - FocusChangeEventArgs(Object* sender, Object* original_sender, const bool is_window = false) - : UiEventArgs(sender, original_sender), is_window_(is_window) - { - - } - FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; - FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; - ~FocusChangeEventArgs() override = default; - - // Return whether the focus change is caused by the window-wide focus change. - bool IsWindow() const - { - return is_window_; - } - - private: - bool is_window_; - }; - - class ToggleEventArgs : public UiEventArgs - { - public: - ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) - : UiEventArgs(sender, original_sender), new_state_(new_state) - { - - } - ToggleEventArgs(const ToggleEventArgs& other) = default; - ToggleEventArgs(ToggleEventArgs&& other) = default; - ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; - ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; - ~ToggleEventArgs() override = default; - - bool GetNewState() const - { - return new_state_; - } - - private: - bool new_state_; - }; - - struct WindowNativeMessage - { - HWND hwnd; - int msg; - WPARAM w_param; - LPARAM l_param; - }; - - class WindowNativeMessageEventArgs : public UiEventArgs - { - public: - WindowNativeMessageEventArgs(Object* sender, Object* original_sender, const WindowNativeMessage& message) - : UiEventArgs(sender, original_sender), message_(message), result_(std::nullopt) - { - - } - WindowNativeMessageEventArgs(const WindowNativeMessageEventArgs& other) = default; - WindowNativeMessageEventArgs(WindowNativeMessageEventArgs&& other) = default; - WindowNativeMessageEventArgs& operator=(const WindowNativeMessageEventArgs& other) = default; - WindowNativeMessageEventArgs& operator=(WindowNativeMessageEventArgs&& other) = default; - ~WindowNativeMessageEventArgs() override = default; - - WindowNativeMessage GetWindowMessage() const - { - return message_; - } - - std::optional<LRESULT> GetResult() const - { - return result_; - } - - void SetResult(const std::optional<LRESULT> result) - { - result_ = result; - } - - private: - WindowNativeMessage message_; - std::optional<LRESULT> result_; - }; - - class KeyEventArgs : public UiEventArgs - { - public: - KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) - : UiEventArgs(sender, original_sender), virtual_code_(virtual_code) - { - } - KeyEventArgs(const KeyEventArgs& other) = default; - KeyEventArgs(KeyEventArgs&& other) = default; - KeyEventArgs& operator=(const KeyEventArgs& other) = default; - KeyEventArgs& operator=(KeyEventArgs&& other) = default; - ~KeyEventArgs() override = default; - - int GetVirtualCode() const - { - return virtual_code_; - } - - private: - int virtual_code_; - }; - - class CharEventArgs : public UiEventArgs - { - public: - CharEventArgs(Object* sender, Object* original_sender, wchar_t c) - : UiEventArgs(sender, original_sender), c_(c) - { - } - CharEventArgs(const CharEventArgs& other) = default; - CharEventArgs(CharEventArgs&& other) = default; - CharEventArgs& operator=(const CharEventArgs& other) = default; - CharEventArgs& operator=(CharEventArgs&& other) = default; - ~CharEventArgs() override = default; - - wchar_t GetChar() const - { - return c_; - } - - private: - wchar_t c_; - }; - - using UiEvent = Event<UiEventArgs>; - using MouseEvent = Event<MouseEventArgs>; - using MouseButtonEvent = Event<MouseButtonEventArgs>; - using DrawEvent = Event<DrawEventArgs>; - using PositionChangedEvent = Event<PositionChangedEventArgs>; - using SizeChangedEvent = Event<SizeChangedEventArgs>; - using FocusChangeEvent = Event<FocusChangeEventArgs>; - using ToggleEvent = Event<ToggleEventArgs>; - using WindowNativeMessageEvent = Event<WindowNativeMessageEventArgs>; - using KeyEvent = Event<KeyEventArgs>; - using CharEvent = Event<CharEventArgs>; + } - } -}
\ No newline at end of file + SizeChangedEventArgs(const SizeChangedEventArgs& other) = default; + SizeChangedEventArgs(SizeChangedEventArgs&& other) = default; + SizeChangedEventArgs& operator=(const SizeChangedEventArgs& other) = default; + SizeChangedEventArgs& operator=(SizeChangedEventArgs&& other) = default; + ~SizeChangedEventArgs() override = default; + + Size GetOldSize() const + { + return old_size_; + } + + Size GetNewSize() const + { + return new_size_; + } + + private: + Size old_size_; + Size new_size_; + }; + + class FocusChangeEventArgs : public UiEventArgs + { + public: + FocusChangeEventArgs(Object* sender, Object* original_sender, const bool is_window = false) + : UiEventArgs(sender, original_sender), is_window_(is_window) + { + + } + FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; + FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; + ~FocusChangeEventArgs() override = default; + + // Return whether the focus change is caused by the window-wide focus change. + bool IsWindow() const + { + return is_window_; + } + + private: + bool is_window_; + }; + + class ToggleEventArgs : public UiEventArgs + { + public: + ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) + : UiEventArgs(sender, original_sender), new_state_(new_state) + { + + } + ToggleEventArgs(const ToggleEventArgs& other) = default; + ToggleEventArgs(ToggleEventArgs&& other) = default; + ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; + ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; + ~ToggleEventArgs() override = default; + + bool GetNewState() const + { + return new_state_; + } + + private: + bool new_state_; + }; + + struct WindowNativeMessage + { + HWND hwnd; + int msg; + WPARAM w_param; + LPARAM l_param; + }; + + class WindowNativeMessageEventArgs : public UiEventArgs + { + public: + WindowNativeMessageEventArgs(Object* sender, Object* original_sender, const WindowNativeMessage& message) + : UiEventArgs(sender, original_sender), message_(message), result_(std::nullopt) + { + + } + WindowNativeMessageEventArgs(const WindowNativeMessageEventArgs& other) = default; + WindowNativeMessageEventArgs(WindowNativeMessageEventArgs&& other) = default; + WindowNativeMessageEventArgs& operator=(const WindowNativeMessageEventArgs& other) = default; + WindowNativeMessageEventArgs& operator=(WindowNativeMessageEventArgs&& other) = default; + ~WindowNativeMessageEventArgs() override = default; + + WindowNativeMessage GetWindowMessage() const + { + return message_; + } + + std::optional<LRESULT> GetResult() const + { + return result_; + } + + void SetResult(const std::optional<LRESULT> result) + { + result_ = result; + } + + private: + WindowNativeMessage message_; + std::optional<LRESULT> result_; + }; + + class KeyEventArgs : public UiEventArgs + { + public: + KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) + : UiEventArgs(sender, original_sender), virtual_code_(virtual_code) + { + } + KeyEventArgs(const KeyEventArgs& other) = default; + KeyEventArgs(KeyEventArgs&& other) = default; + KeyEventArgs& operator=(const KeyEventArgs& other) = default; + KeyEventArgs& operator=(KeyEventArgs&& other) = default; + ~KeyEventArgs() override = default; + + int GetVirtualCode() const + { + return virtual_code_; + } + + private: + int virtual_code_; + }; + + class CharEventArgs : public UiEventArgs + { + public: + CharEventArgs(Object* sender, Object* original_sender, wchar_t c) + : UiEventArgs(sender, original_sender), c_(c) + { + } + CharEventArgs(const CharEventArgs& other) = default; + CharEventArgs(CharEventArgs&& other) = default; + CharEventArgs& operator=(const CharEventArgs& other) = default; + CharEventArgs& operator=(CharEventArgs&& other) = default; + ~CharEventArgs() override = default; + + wchar_t GetChar() const + { + return c_; + } + + private: + wchar_t c_; + }; + + using UiEvent = Event<UiEventArgs>; + using MouseEvent = Event<MouseEventArgs>; + using MouseButtonEvent = Event<MouseButtonEventArgs>; + using DrawEvent = Event<DrawEventArgs>; + using PositionChangedEvent = Event<PositionChangedEventArgs>; + using SizeChangedEvent = Event<SizeChangedEventArgs>; + using FocusChangeEvent = Event<FocusChangeEventArgs>; + using ToggleEvent = Event<ToggleEventArgs>; + using WindowNativeMessageEvent = Event<WindowNativeMessageEventArgs>; + using KeyEvent = Event<KeyEventArgs>; + using CharEvent = Event<CharEventArgs>; +} diff --git a/src/ui/layout_base.h b/src/ui/layout_base.h index 4a4c09ea..b3001268 100644 --- a/src/ui/layout_base.h +++ b/src/ui/layout_base.h @@ -5,187 +5,184 @@ #include "base.h" #include "ui_base.h" -namespace cru +namespace cru::ui { - namespace ui + class Control; + class Window; + + enum class Alignment { - class Control; - class Window; + Center, + Start, + End + }; - enum class Alignment - { - Center, - Start, - End - }; + enum class MeasureMode + { + Exactly, + Content, + Stretch + }; - enum class MeasureMode + enum class RectRange + { + Content, // content excluding padding, border and margin + Padding, // only including content and padding + HalfBorder, // including content, padding and half border + FullBorder, // including content, padding and full border + Margin // including content, padding, border and margin + }; + + struct Thickness + { + constexpr static Thickness Zero() { - Exactly, - Content, - Stretch - }; + return Thickness(0); + } + + constexpr Thickness() : Thickness(0) { } - enum class RectRange + 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 { - Content, // content excluding padding, border and margin - Padding, // only including content and padding - HalfBorder, // including content, padding and half border - FullBorder, // including content, padding and full border - Margin // including content, padding, border and margin - }; - - struct Thickness + return left + right; + } + + float GetVerticalTotal() const { - constexpr static Thickness Zero() - { - return Thickness(0); - } + return top + bottom; + } - constexpr Thickness() : Thickness(0) { } + float Validate() const + { + return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; + } - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } + float left; + float top; + float right; + float bottom; + }; - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } + struct LayoutSideParams final + { + constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) + { + return LayoutSideParams(MeasureMode::Exactly, length, alignment); + } - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } + constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center) + { + return LayoutSideParams(MeasureMode::Content, 0, alignment); + } - float GetHorizontalTotal() const - { - return left + right; - } + constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center) + { + return LayoutSideParams(MeasureMode::Stretch, 0, alignment); + } - float GetVerticalTotal() const - { - return top + bottom; - } + constexpr LayoutSideParams() = default; - float Validate() const - { - return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; - } + constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment) + : length(length), mode(mode), alignment(alignment) + { - float left; - float top; - float right; - float bottom; - }; + } - struct LayoutSideParams final - { - constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Exactly, length, alignment); - } - - constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Content, 0, alignment); - } - - constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Stretch, 0, alignment); - } - - constexpr LayoutSideParams() = default; - - constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment) - : length(length), mode(mode), alignment(alignment) - { - - } - - constexpr bool Validate() const - { - if (length < 0.0) - return false; - if (min.has_value() && min.value() < 0.0) - return false; - if (max.has_value() && max.value() < 0.0) - return false; - if (min.has_value() && max.has_value() && min.value() > max.value()) - return false; - return true; - } - - // only used in exactly mode, specify the exactly side length of content. - float length = 0.0; - MeasureMode mode = MeasureMode::Content; - Alignment alignment = Alignment::Center; - - // min and max specify the min/max side length of content. - // they are used as hint and respect the actual size that content needs. - // when mode is exactly, length is coerced into the min-max range. - std::optional<float> min = std::nullopt; - std::optional<float> max = std::nullopt; - }; - - struct BasicLayoutParams final + constexpr bool Validate() const { - BasicLayoutParams() = default; - BasicLayoutParams(const BasicLayoutParams&) = default; - BasicLayoutParams(BasicLayoutParams&&) = default; - BasicLayoutParams& operator = (const BasicLayoutParams&) = default; - BasicLayoutParams& operator = (BasicLayoutParams&&) = default; - ~BasicLayoutParams() = default; - - bool Validate() const - { - return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); - } - - LayoutSideParams width; - LayoutSideParams height; - Thickness padding; - Thickness margin; - }; - - - class LayoutManager : public Object + if (length < 0.0) + return false; + if (min.has_value() && min.value() < 0.0) + return false; + if (max.has_value() && max.value() < 0.0) + return false; + if (min.has_value() && max.has_value() && min.value() > max.value()) + return false; + return true; + } + + // only used in exactly mode, specify the exactly side length of content. + float length = 0.0; + MeasureMode mode = MeasureMode::Content; + Alignment alignment = Alignment::Center; + + // min and max specify the min/max side length of content. + // they are used as hint and respect the actual size that content needs. + // when mode is exactly, length is coerced into the min-max range. + std::optional<float> min = std::nullopt; + std::optional<float> max = std::nullopt; + }; + + struct BasicLayoutParams final + { + BasicLayoutParams() = default; + BasicLayoutParams(const BasicLayoutParams&) = default; + BasicLayoutParams(BasicLayoutParams&&) = default; + BasicLayoutParams& operator = (const BasicLayoutParams&) = default; + BasicLayoutParams& operator = (BasicLayoutParams&&) = default; + ~BasicLayoutParams() = default; + + bool Validate() const { - public: - static LayoutManager* GetInstance(); + return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); + } + + LayoutSideParams width; + LayoutSideParams height; + Thickness padding; + Thickness margin; + }; + + + class LayoutManager : public Object + { + public: + static LayoutManager* GetInstance(); - public: - LayoutManager() = default; - LayoutManager(const LayoutManager& other) = delete; - LayoutManager(LayoutManager&& other) = delete; - LayoutManager& operator=(const LayoutManager& other) = delete; - LayoutManager& operator=(LayoutManager&& other) = delete; - ~LayoutManager() override = default; + public: + LayoutManager() = default; + LayoutManager(const LayoutManager& other) = delete; + LayoutManager(LayoutManager&& other) = delete; + LayoutManager& operator=(const LayoutManager& other) = delete; + LayoutManager& operator=(LayoutManager&& other) = delete; + ~LayoutManager() override = default; - //*************** region: position cache *************** + //*************** region: position cache *************** - //Mark position cache of the control and its descendants invalid, - //(which is saved as an auto-managed list internal) - //and send a message to refresh them. - void InvalidateControlPositionCache(Control* control); + //Mark position cache of the control and its descendants invalid, + //(which is saved as an auto-managed list internal) + //and send a message to refresh them. + void InvalidateControlPositionCache(Control* control); - //Refresh position cache of the control and its descendants whose cache - //has been marked as invalid. - void RefreshInvalidControlPositionCache(); + //Refresh position cache of the control and its descendants whose cache + //has been marked as invalid. + void RefreshInvalidControlPositionCache(); - //Refresh position cache of the control and its descendants immediately. - static void RefreshControlPositionCache(Control* control); + //Refresh position cache of the control and its descendants immediately. + static void RefreshControlPositionCache(Control* control); - //*************** region: layout *************** + //*************** region: layout *************** - void InvalidateWindowLayout(Window* window); + void InvalidateWindowLayout(Window* window); - void RefreshInvalidWindowLayout(); + void RefreshInvalidWindowLayout(); - private: - static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute); + private: + static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute); - private: - std::unordered_set<Control*> cache_invalid_controls_; - std::unordered_set<Window*> layout_invalid_windows_; - }; - } + private: + std::unordered_set<Control*> cache_invalid_controls_; + std::unordered_set<Window*> layout_invalid_windows_; + }; } diff --git a/src/ui/ui_base.cpp b/src/ui/ui_base.cpp index 3bee2269..0195c588 100644 --- a/src/ui/ui_base.cpp +++ b/src/ui/ui_base.cpp @@ -2,18 +2,17 @@ #include "system_headers.h" -namespace cru { - namespace ui { - bool IsKeyDown(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 0x8000) != 0; - } +namespace cru::ui +{ + bool IsKeyDown(const int virtual_code) + { + const auto result = ::GetKeyState(virtual_code); + return (static_cast<unsigned short>(result) & 0x8000) != 0; + } - bool IsKeyToggled(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 1) != 0; - } + bool IsKeyToggled(const int virtual_code) + { + const auto result = ::GetKeyState(virtual_code); + return (static_cast<unsigned short>(result) & 1) != 0; } } diff --git a/src/ui/ui_base.h b/src/ui/ui_base.h index bff30bfd..8daa43d7 100644 --- a/src/ui/ui_base.h +++ b/src/ui/ui_base.h @@ -3,158 +3,155 @@ #include <optional> -namespace cru +namespace cru::ui { - namespace ui + struct Point { - struct Point + constexpr static Point Zero() { - constexpr static Point Zero() - { - return Point(0, 0); - } + 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 Point() = default; - constexpr Point(const float x, const float y) : x(x), y(y) { } + constexpr bool operator==(const Point& left, const Point& right) + { + return left.x == right.x && left.y == right.y; + } - float x = 0; - float y = 0; - }; + constexpr bool operator!=(const Point& left, const Point& right) + { + return !(left == right); + } - constexpr bool operator==(const Point& left, const Point& right) + struct Size + { + constexpr static Size Zero() { - return left.x == right.x && left.y == right.y; + return Size(0, 0); } - constexpr bool operator!=(const Point& left, const Point& right) + 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 Rect + { + 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 !(left == right); + return Rect(left, top, right - left, bottom - top); } - struct Size + constexpr float GetRight() const { - constexpr static Size Zero() - { - return Size(0, 0); - } - - constexpr Size() = default; - constexpr Size(const float width, const float height) : width(width), height(height) { } + return left + width; + } - float width = 0; - float height = 0; - }; + constexpr float GetBottom() const + { + return top + height; + } - constexpr Size operator + (const Size& left, const Size& right) + constexpr Point GetLeftTop() const { - return Size(left.width + right.width, left.height + right.height); + return Point(left, top); } - constexpr Size operator - (const Size& left, const Size& right) + constexpr Point GetRightBottom() const { - return Size(left.width - right.width, left.height - right.height); + return Point(left + width, top + height); } - constexpr bool operator==(const Size& left, const Size& right) + constexpr Size GetSize() const { - return left.width == right.width && left.height == right.height; + return Size(width, height); } - constexpr bool operator!=(const Size& left, const Size& right) + constexpr bool IsPointInside(const Point& point) const { - return !(left == right); + return + point.x >= left && + point.x < GetRight() && + point.y >= top && + point.y < GetBottom(); } - struct Rect + float left = 0.0f; + float top = 0.0f; + float width = 0.0f; + float height = 0.0f; + }; + + enum class MouseButton + { + Left, + Right, + Middle + }; + + struct TextRange + { + constexpr static std::optional<TextRange> FromTwoSides(unsigned first, unsigned second) { - 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 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 Size GetSize() const - { - return Size(width, height); - } - - 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; - }; - - enum class MouseButton + 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) { - Left, - Right, - Middle - }; + 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); + } - struct TextRange + constexpr TextRange() = default; + constexpr TextRange(const unsigned position, const unsigned count) + : position(position), count(count) { - 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; - }; - - bool IsKeyDown(int virtual_code); - bool IsKeyToggled(int virtual_code); - } + + } + + unsigned position = 0; + unsigned count = 0; + }; + + bool IsKeyDown(int virtual_code); + bool IsKeyToggled(int virtual_code); } diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 5ca5ad3f..cc08810b 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -5,661 +5,658 @@ #include "exception.h" #include "cursor.h" -namespace cru +namespace cru::ui { - namespace ui + WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) + : name_(name) { - WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) - : name_(name) - { - WNDCLASSEX window_class; - window_class.cbSize = sizeof(WNDCLASSEX); - - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.lpfnWndProc = window_proc; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = h_instance; - window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); - window_class.hCursor = LoadCursor(NULL, IDC_ARROW); - window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); - window_class.lpszMenuName = NULL; - window_class.lpszClassName = name.c_str(); - window_class.hIconSm = NULL; - - atom_ = RegisterClassEx(&window_class); - if (atom_ == 0) - throw std::runtime_error("Failed to create window class."); - } + WNDCLASSEX window_class; + window_class.cbSize = sizeof(WNDCLASSEX); + + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = window_proc; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = h_instance; + window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + window_class.hCursor = LoadCursor(NULL, IDC_ARROW); + window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); + window_class.lpszMenuName = NULL; + window_class.lpszClassName = name.c_str(); + window_class.hIconSm = NULL; + + atom_ = RegisterClassEx(&window_class); + if (atom_ == 0) + throw std::runtime_error("Failed to create window class."); + } - LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { - auto window = Application::GetInstance()->GetWindowManager()->FromHandle(hWnd); + LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + auto window = Application::GetInstance()->GetWindowManager()->FromHandle(hWnd); - LRESULT result; - if (window != nullptr && window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result)) - return result; + LRESULT result; + if (window != nullptr && window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result)) + return result; - return DefWindowProc(hWnd, Msg, wParam, lParam); - } + return DefWindowProc(hWnd, Msg, wParam, lParam); + } - WindowManager::WindowManager() { - general_window_class_ = std::make_unique<WindowClass>( - L"CruUIWindowClass", - GeneralWndProc, - Application::GetInstance()->GetInstanceHandle() - ); - } + WindowManager::WindowManager() { + general_window_class_ = std::make_unique<WindowClass>( + L"CruUIWindowClass", + GeneralWndProc, + Application::GetInstance()->GetInstanceHandle() + ); + } - void WindowManager::RegisterWindow(HWND hwnd, Window * window) { - const auto find_result = window_map_.find(hwnd); - if (find_result != window_map_.end()) - throw std::runtime_error("The hwnd is already in the map."); + void WindowManager::RegisterWindow(HWND hwnd, Window * window) { + const auto find_result = window_map_.find(hwnd); + if (find_result != window_map_.end()) + throw std::runtime_error("The hwnd is already in the map."); - window_map_.emplace(hwnd, window); - } + window_map_.emplace(hwnd, window); + } - void WindowManager::UnregisterWindow(HWND hwnd) { - const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - throw std::runtime_error("The hwnd is not in the map."); - window_map_.erase(find_result); + void WindowManager::UnregisterWindow(HWND hwnd) { + const auto find_result = window_map_.find(hwnd); + if (find_result == window_map_.end()) + throw std::runtime_error("The hwnd is not in the map."); + window_map_.erase(find_result); - if (window_map_.empty()) - Application::GetInstance()->Quit(0); - } + if (window_map_.empty()) + Application::GetInstance()->Quit(0); + } - Window* WindowManager::FromHandle(HWND hwnd) { - const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - return nullptr; - else - return find_result->second; - } + Window* WindowManager::FromHandle(HWND hwnd) { + const auto find_result = window_map_.find(hwnd); + if (find_result == window_map_.end()) + return nullptr; + else + return find_result->second; + } - std::vector<Window*> WindowManager::GetAllWindows() const - { - std::vector<Window*> windows; - for (auto [key, value] : window_map_) - windows.push_back(value); - return windows; - } + std::vector<Window*> WindowManager::GetAllWindows() const + { + std::vector<Window*> windows; + for (auto [key, value] : window_map_) + windows.push_back(value); + return windows; + } - inline Point PiToDip(const POINT& pi_point) - { - return Point( - graph::PixelToDipX(pi_point.x), - graph::PixelToDipY(pi_point.y) - ); - } + inline Point PiToDip(const POINT& pi_point) + { + return Point( + graph::PixelToDipX(pi_point.x), + graph::PixelToDipY(pi_point.y) + ); + } - namespace + namespace + { + Cursor::Ptr GetCursorInherit(Control* control) { - Cursor::Ptr GetCursorInherit(Control* control) + while (control != nullptr) { - while (control != nullptr) - { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) - return cursor; - control = control->GetParent(); - } - return cursors::arrow; + const auto cursor = control->GetCursor(); + if (cursor != nullptr) + return cursor; + control = control->GetParent(); } + return cursors::arrow; } + } - Window::Window() : Control(WindowConstructorTag{}, this), control_list_({ this }) { - const auto app = Application::GetInstance(); - hwnd_ = CreateWindowEx(0, - app->GetWindowManager()->GetGeneralWindowClass()->GetName(), - L"", WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - nullptr, nullptr, app->GetInstanceHandle(), nullptr - ); + Window::Window() : Control(WindowConstructorTag{}, this), control_list_({ this }) { + const auto app = Application::GetInstance(); + hwnd_ = CreateWindowEx(0, + app->GetWindowManager()->GetGeneralWindowClass()->GetName(), + L"", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, nullptr, app->GetInstanceHandle(), nullptr + ); - if (hwnd_ == nullptr) - throw std::runtime_error("Failed to create window."); + if (hwnd_ == nullptr) + throw std::runtime_error("Failed to create window."); - app->GetWindowManager()->RegisterWindow(hwnd_, this); + app->GetWindowManager()->RegisterWindow(hwnd_, this); - render_target_ = app->GetGraphManager()->CreateWindowRenderTarget(hwnd_); + render_target_ = app->GetGraphManager()->CreateWindowRenderTarget(hwnd_); - SetCursor(cursors::arrow); - } + SetCursor(cursors::arrow); + } - Window::~Window() { - Close(); - TraverseDescendants([this](Control* control) { - control->OnDetachToWindow(this); - }); - } + Window::~Window() { + Close(); + TraverseDescendants([this](Control* control) { + control->OnDetachToWindow(this); + }); + } - StringView Window::GetControlType() const - { - return control_type; - } + StringView Window::GetControlType() const + { + return control_type; + } - void Window::Close() { - if (IsWindowValid()) - DestroyWindow(hwnd_); - } + void Window::Close() { + if (IsWindowValid()) + DestroyWindow(hwnd_); + } - void Window::Repaint() { - if (IsWindowValid()) { - InvalidateRect(hwnd_, nullptr, false); - } + void Window::Repaint() { + if (IsWindowValid()) { + InvalidateRect(hwnd_, nullptr, false); } + } - void Window::Show() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_SHOWNORMAL); - } + void Window::Show() { + if (IsWindowValid()) { + ShowWindow(hwnd_, SW_SHOWNORMAL); } + } - void Window::Hide() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_HIDE); - } + void Window::Hide() { + if (IsWindowValid()) { + ShowWindow(hwnd_, SW_HIDE); } + } + + Size Window::GetClientSize() { + if (!IsWindowValid()) + return Size(); + + const auto pixel_rect = GetClientRectPixel(); + return Size( + graph::PixelToDipX(pixel_rect.right), + graph::PixelToDipY(pixel_rect.bottom) + ); + } - Size Window::GetClientSize() { - if (!IsWindowValid()) - return Size(); + void Window::SetClientSize(const Size & size) { + if (IsWindowValid()) { + const auto window_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); + const auto window_ex_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); - const auto pixel_rect = GetClientRectPixel(); - return Size( - graph::PixelToDipX(pixel_rect.right), - graph::PixelToDipY(pixel_rect.bottom) + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = graph::DipToPixelX(size.width); + rect.bottom = graph::DipToPixelY(size.height); + AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style); + + SetWindowPos( + hwnd_, nullptr, 0, 0, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOZORDER | SWP_NOMOVE ); } + } - void Window::SetClientSize(const Size & size) { - if (IsWindowValid()) { - const auto window_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = graph::DipToPixelX(size.width); - rect.bottom = graph::DipToPixelY(size.height); - AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style); - - SetWindowPos( - hwnd_, nullptr, 0, 0, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOZORDER | SWP_NOMOVE - ); - } - } + Rect Window::GetWindowRect() { + if (!IsWindowValid()) + return Rect(); - Rect Window::GetWindowRect() { - if (!IsWindowValid()) - return Rect(); + RECT rect; + ::GetWindowRect(hwnd_, &rect); - RECT rect; - ::GetWindowRect(hwnd_, &rect); + return Rect::FromVertices( + graph::PixelToDipX(rect.left), + graph::PixelToDipY(rect.top), + graph::PixelToDipX(rect.right), + graph::PixelToDipY(rect.bottom) + ); + } - return Rect::FromVertices( - graph::PixelToDipX(rect.left), - graph::PixelToDipY(rect.top), - graph::PixelToDipX(rect.right), - graph::PixelToDipY(rect.bottom) + void Window::SetWindowRect(const Rect & rect) { + if (IsWindowValid()) { + SetWindowPos( + hwnd_, nullptr, + graph::DipToPixelX(rect.left), + graph::DipToPixelY(rect.top), + graph::DipToPixelX(rect.GetRight()), + graph::DipToPixelY(rect.GetBottom()), + SWP_NOZORDER ); } + } - void Window::SetWindowRect(const Rect & rect) { - if (IsWindowValid()) { - SetWindowPos( - hwnd_, nullptr, - graph::DipToPixelX(rect.left), - graph::DipToPixelY(rect.top), - graph::DipToPixelX(rect.GetRight()), - graph::DipToPixelY(rect.GetBottom()), - SWP_NOZORDER - ); - } - } - - bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) { - - if (!native_message_event.IsNoHandler()) - { - events::WindowNativeMessageEventArgs args(this, this, {hwnd, msg, w_param, l_param}); - native_message_event.Raise(args); - if (args.GetResult().has_value()) - { - result = args.GetResult().value(); - return true; - } - } + bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) { - switch (msg) { - case WM_PAINT: - OnPaintInternal(); - result = 0; - return true; - case WM_ERASEBKGND: - result = 1; - return true; - case WM_SETFOCUS: - OnSetFocusInternal(); - result = 0; - return true; - case WM_KILLFOCUS: - OnKillFocusInternal(); - result = 0; - return true; - case WM_MOUSEMOVE: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseMoveInternal(point); - result = 0; - return true; - } - case WM_LBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_LBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_RBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_RBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_MBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Middle, point); - result = 0; - return true; - } - case WM_MBUTTONUP: + if (!native_message_event.IsNoHandler()) + { + events::WindowNativeMessageEventArgs args(this, this, {hwnd, msg, w_param, l_param}); + native_message_event.Raise(args); + if (args.GetResult().has_value()) { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Middle, point); - result = 0; + result = args.GetResult().value(); return true; } - case WM_KEYDOWN: - OnKeyDownInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_KEYUP: - OnKeyUpInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_CHAR: - OnCharInternal(static_cast<wchar_t>(w_param)); - result = 0; - return true; - case WM_SIZE: - OnResizeInternal(LOWORD(l_param), HIWORD(l_param)); - result = 0; - return true; - case WM_ACTIVATE: - if (w_param == WA_ACTIVE || w_param == WA_CLICKACTIVE) - OnActivatedInternal(); - else if (w_param == WA_INACTIVE) - OnDeactivatedInternal(); - result = 0; - return true; - case WM_DESTROY: - OnDestroyInternal(); - result = 0; - return true; - default: - return false; - } } - Point Window::GetMousePosition() + switch (msg) { + case WM_PAINT: + OnPaintInternal(); + result = 0; + return true; + case WM_ERASEBKGND: + result = 1; + return true; + case WM_SETFOCUS: + OnSetFocusInternal(); + result = 0; + return true; + case WM_KILLFOCUS: + OnKillFocusInternal(); + result = 0; + return true; + case WM_MOUSEMOVE: { - if (!IsWindowValid()) - return Point::Zero(); POINT point; - ::GetCursorPos(&point); - ::ScreenToClient(hwnd_, &point); - return PiToDip(point); + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseMoveInternal(point); + result = 0; + return true; } - - Point Window::GetPositionRelative() + case WM_LBUTTONDOWN: { - return Point(); + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Left, point); + result = 0; + return true; } - - void Window::SetPositionRelative(const Point & position) + case WM_LBUTTONUP: { - + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Left, point); + result = 0; + return true; } - - Size Window::GetSize() + case WM_RBUTTONDOWN: { - return GetClientSize(); + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Right, point); + result = 0; + return true; } - - void Window::SetSize(const Size & size) + case WM_RBUTTONUP: { - + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Right, point); + result = 0; + return true; } - - void Window::Relayout() + case WM_MBUTTONDOWN: { - OnMeasureCore(GetSize()); - OnLayoutCore(Rect(Point::Zero(), GetSize())); + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Middle, point); + result = 0; + return true; } - - void Window::RefreshControlList() { - control_list_.clear(); - TraverseDescendants([this](Control* control) { - this->control_list_.push_back(control); - }); + case WM_MBUTTONUP: + { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Middle, point); + result = 0; + return true; } + case WM_KEYDOWN: + OnKeyDownInternal(static_cast<int>(w_param)); + result = 0; + return true; + case WM_KEYUP: + OnKeyUpInternal(static_cast<int>(w_param)); + result = 0; + return true; + case WM_CHAR: + OnCharInternal(static_cast<wchar_t>(w_param)); + result = 0; + return true; + case WM_SIZE: + OnResizeInternal(LOWORD(l_param), HIWORD(l_param)); + result = 0; + return true; + case WM_ACTIVATE: + if (w_param == WA_ACTIVE || w_param == WA_CLICKACTIVE) + OnActivatedInternal(); + else if (w_param == WA_INACTIVE) + OnDeactivatedInternal(); + result = 0; + return true; + case WM_DESTROY: + OnDestroyInternal(); + result = 0; + return true; + default: + return false; + } + } - Control * Window::HitTest(const Point & point) - { - for (auto i = control_list_.crbegin(); i != control_list_.crend(); ++i) { - auto control = *i; - if (control->IsPointInside(control->WindowToControl(point))) { - return control; - } + Point Window::GetMousePosition() + { + if (!IsWindowValid()) + return Point::Zero(); + POINT point; + ::GetCursorPos(&point); + ::ScreenToClient(hwnd_, &point); + return PiToDip(point); + } + + Point Window::GetPositionRelative() + { + return Point(); + } + + void Window::SetPositionRelative(const Point & position) + { + + } + + Size Window::GetSize() + { + return GetClientSize(); + } + + void Window::SetSize(const Size & size) + { + + } + + void Window::Relayout() + { + OnMeasureCore(GetSize()); + OnLayoutCore(Rect(Point::Zero(), GetSize())); + } + + void Window::RefreshControlList() { + control_list_.clear(); + TraverseDescendants([this](Control* control) { + this->control_list_.push_back(control); + }); + } + + Control * Window::HitTest(const Point & point) + { + for (auto i = control_list_.crbegin(); i != control_list_.crend(); ++i) { + auto control = *i; + if (control->IsPointInside(control->WindowToControl(point))) { + return control; } - return nullptr; } + return nullptr; + } + + bool Window::RequestFocusFor(Control * control) + { + if (control == nullptr) + throw std::invalid_argument("The control to request focus can't be null. You can set it as the window."); - bool Window::RequestFocusFor(Control * control) + if (!IsWindowValid()) + return false; + + if (!window_focus_) { - if (control == nullptr) - throw std::invalid_argument("The control to request focus can't be null. You can set it as the window."); + focus_control_ = control; + ::SetFocus(hwnd_); + return true; // event dispatch will be done in window message handling function "OnSetFocusInternal". + } - if (!IsWindowValid()) - return false; + if (focus_control_ == control) + return true; - if (!window_focus_) - { - focus_control_ = control; - ::SetFocus(hwnd_); - return true; // event dispatch will be done in window message handling function "OnSetFocusInternal". - } + DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, false); - if (focus_control_ == control) - return true; + focus_control_ = control; - DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, false); + DispatchEvent(control, &Control::RaiseGetFocusEvent, nullptr, false); - focus_control_ = control; + return true; + } - DispatchEvent(control, &Control::RaiseGetFocusEvent, nullptr, false); + Control* Window::GetFocusControl() + { + return focus_control_; + } - return true; + Control* Window::CaptureMouseFor(Control* control) + { + if (control != nullptr) + { + ::SetCapture(hwnd_); + std::swap(mouse_capture_control_, control); + DispatchMouseHoverControlChangeEvent(control ? control : mouse_hover_control_, mouse_capture_control_, GetMousePosition()); + return control; } - - Control* Window::GetFocusControl() + else { - return focus_control_; + return ReleaseCurrentMouseCapture(); } + } - Control* Window::CaptureMouseFor(Control* control) + Control* Window::ReleaseCurrentMouseCapture() + { + if (mouse_capture_control_) { - if (control != nullptr) - { - ::SetCapture(hwnd_); - std::swap(mouse_capture_control_, control); - DispatchMouseHoverControlChangeEvent(control ? control : mouse_hover_control_, mouse_capture_control_, GetMousePosition()); - return control; - } - else - { - return ReleaseCurrentMouseCapture(); - } + const auto previous = mouse_capture_control_; + mouse_capture_control_ = nullptr; + ::ReleaseCapture(); + DispatchMouseHoverControlChangeEvent(previous, mouse_hover_control_, GetMousePosition()); + return previous; } - - Control* Window::ReleaseCurrentMouseCapture() + else { - if (mouse_capture_control_) - { - const auto previous = mouse_capture_control_; - mouse_capture_control_ = nullptr; - ::ReleaseCapture(); - DispatchMouseHoverControlChangeEvent(previous, mouse_hover_control_, GetMousePosition()); - return previous; - } - else - { - return nullptr; - } + return nullptr; } + } - void Window::UpdateCursor() + void Window::UpdateCursor() + { + if (IsWindowValid() && mouse_hover_control_ != nullptr) { - if (IsWindowValid() && mouse_hover_control_ != nullptr) - { - SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle()); - } + SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle()); } + } #ifdef CRU_DEBUG_LAYOUT - void Window::SetDebugLayout(const bool value) + void Window::SetDebugLayout(const bool value) + { + if (debug_layout_ != value) { - if (debug_layout_ != value) - { - debug_layout_ = value; - Repaint(); - } + debug_layout_ = value; + Repaint(); } + } #endif - RECT Window::GetClientRectPixel() { - RECT rect{ }; - GetClientRect(hwnd_, &rect); - return rect; - } + RECT Window::GetClientRectPixel() { + RECT rect{ }; + GetClientRect(hwnd_, &rect); + return rect; + } - bool Window::IsMessageInQueue(UINT message) - { - MSG msg; - return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; - } + bool Window::IsMessageInQueue(UINT message) + { + MSG msg; + return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; + } - void Window::SetCursorInternal(HCURSOR cursor) + void Window::SetCursorInternal(HCURSOR cursor) + { + if (IsWindowValid()) { - if (IsWindowValid()) - { - ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor)); - if (mouse_hover_control_ != nullptr) - ::SetCursor(cursor); - } + ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor)); + if (mouse_hover_control_ != nullptr) + ::SetCursor(cursor); } + } - Size Window::OnMeasureContent(const Size& available_size) + Size Window::OnMeasureContent(const Size& available_size) + { + for (auto control: GetChildren()) { - for (auto control: GetChildren()) - { - control->Measure(available_size); - } - return available_size; + control->Measure(available_size); } + return available_size; + } - void Window::OnDestroyInternal() { - Application::GetInstance()->GetWindowManager()->UnregisterWindow(hwnd_); - hwnd_ = nullptr; - } + void Window::OnDestroyInternal() { + Application::GetInstance()->GetWindowManager()->UnregisterWindow(hwnd_); + hwnd_ = nullptr; + } - void Window::OnPaintInternal() { - render_target_->SetAsTarget(); + void Window::OnPaintInternal() { + render_target_->SetAsTarget(); - auto device_context = render_target_->GetD2DDeviceContext(); + auto device_context = render_target_->GetD2DDeviceContext(); - device_context->BeginDraw(); + device_context->BeginDraw(); - //Clear the background. - device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); + //Clear the background. + device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); - Draw(device_context.Get()); + Draw(device_context.Get()); - ThrowIfFailed( - device_context->EndDraw(), "Failed to draw window." - ); + ThrowIfFailed( + device_context->EndDraw(), "Failed to draw window." + ); - render_target_->Present(); + render_target_->Present(); - ValidateRect(hwnd_, nullptr); - } + ValidateRect(hwnd_, nullptr); + } - void Window::OnResizeInternal(const int new_width, const int new_height) { - render_target_->ResizeBuffer(new_width, new_height); - if (!(new_width == 0 && new_height == 0)) - InvalidateLayout(); - } + void Window::OnResizeInternal(const int new_width, const int new_height) { + render_target_->ResizeBuffer(new_width, new_height); + if (!(new_width == 0 && new_height == 0)) + InvalidateLayout(); + } - void Window::OnSetFocusInternal() - { - window_focus_ = true; - DispatchEvent(focus_control_, &Control::RaiseGetFocusEvent, nullptr, true); - } + void Window::OnSetFocusInternal() + { + window_focus_ = true; + DispatchEvent(focus_control_, &Control::RaiseGetFocusEvent, nullptr, true); + } - void Window::OnKillFocusInternal() - { - window_focus_ = false; - DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, true); - } + void Window::OnKillFocusInternal() + { + window_focus_ = false; + DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, true); + } - void Window::OnMouseMoveInternal(const POINT point) - { - const auto dip_point = PiToDip(point); + void Window::OnMouseMoveInternal(const POINT point) + { + const auto dip_point = PiToDip(point); - //when mouse was previous outside the window - if (mouse_hover_control_ == nullptr) { - //invoke TrackMouseEvent to have WM_MOUSELEAVE sent. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof tme; - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hwnd_; + //when mouse was previous outside the window + if (mouse_hover_control_ == nullptr) { + //invoke TrackMouseEvent to have WM_MOUSELEAVE sent. + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof tme; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd_; - TrackMouseEvent(&tme); - } + TrackMouseEvent(&tme); + } - //Find the first control that hit test succeed. - const auto new_control_mouse_hover = HitTest(dip_point); - const auto old_control_mouse_hover = mouse_hover_control_; - mouse_hover_control_ = new_control_mouse_hover; + //Find the first control that hit test succeed. + const auto new_control_mouse_hover = HitTest(dip_point); + const auto old_control_mouse_hover = mouse_hover_control_; + mouse_hover_control_ = new_control_mouse_hover; - if (mouse_capture_control_) // if mouse is captured - { - DispatchEvent(mouse_capture_control_, &Control::RaiseMouseMoveEvent, nullptr, dip_point); - } - else - { - DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point); - DispatchEvent(new_control_mouse_hover, &Control::RaiseMouseMoveEvent, nullptr, dip_point); - } + if (mouse_capture_control_) // if mouse is captured + { + DispatchEvent(mouse_capture_control_, &Control::RaiseMouseMoveEvent, nullptr, dip_point); } - - void Window::OnMouseLeaveInternal() + else { - DispatchEvent(mouse_hover_control_, &Control::RaiseMouseLeaveEvent, nullptr); - mouse_hover_control_ = nullptr; + DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point); + DispatchEvent(new_control_mouse_hover, &Control::RaiseMouseMoveEvent, nullptr, dip_point); } + } - void Window::OnMouseDownInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); + void Window::OnMouseLeaveInternal() + { + DispatchEvent(mouse_hover_control_, &Control::RaiseMouseLeaveEvent, nullptr); + mouse_hover_control_ = nullptr; + } - Control* control; + void Window::OnMouseDownInternal(MouseButton button, POINT point) + { + const auto dip_point = PiToDip(point); - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); + Control* control; - DispatchEvent(control, &Control::RaiseMouseDownEvent, nullptr, dip_point, button); - } + if (mouse_capture_control_) + control = mouse_capture_control_; + else + control = HitTest(dip_point); - void Window::OnMouseUpInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); + DispatchEvent(control, &Control::RaiseMouseDownEvent, nullptr, dip_point, button); + } - Control* control; + void Window::OnMouseUpInternal(MouseButton button, POINT point) + { + const auto dip_point = PiToDip(point); - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); + Control* control; - DispatchEvent(control, &Control::RaiseMouseUpEvent, nullptr, dip_point, button); - } + if (mouse_capture_control_) + control = mouse_capture_control_; + else + control = HitTest(dip_point); - void Window::OnKeyDownInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::RaiseKeyDownEvent, nullptr, virtual_code); - } + DispatchEvent(control, &Control::RaiseMouseUpEvent, nullptr, dip_point, button); + } - void Window::OnKeyUpInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::RaiseKeyUpEvent, nullptr, virtual_code); - } + void Window::OnKeyDownInternal(int virtual_code) + { + DispatchEvent(focus_control_, &Control::RaiseKeyDownEvent, nullptr, virtual_code); + } - void Window::OnCharInternal(wchar_t c) - { - DispatchEvent(focus_control_, &Control::RaiseCharEvent, nullptr, c); - } + void Window::OnKeyUpInternal(int virtual_code) + { + DispatchEvent(focus_control_, &Control::RaiseKeyUpEvent, nullptr, virtual_code); + } - void Window::OnActivatedInternal() - { - events::UiEventArgs args(this, this); - activated_event.Raise(args); - } + void Window::OnCharInternal(wchar_t c) + { + DispatchEvent(focus_control_, &Control::RaiseCharEvent, nullptr, c); + } - void Window::OnDeactivatedInternal() - { - events::UiEventArgs args(this, this); - deactivated_event.Raise(args); - } + void Window::OnActivatedInternal() + { + events::UiEventArgs args(this, this); + activated_event.Raise(args); + } - void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, const Point& point) + void Window::OnDeactivatedInternal() + { + events::UiEventArgs args(this, this); + deactivated_event.Raise(args); + } + + void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, const Point& point) + { + if (new_control != old_control) //if the mouse-hover-on control changed { - if (new_control != old_control) //if the mouse-hover-on control changed + const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control); + if (old_control != nullptr) // if last mouse-hover-on control exists + DispatchEvent(old_control, &Control::RaiseMouseLeaveEvent, lowest_common_ancestor); // dispatch mouse leave event. + if (new_control != nullptr) { - const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control); - if (old_control != nullptr) // if last mouse-hover-on control exists - DispatchEvent(old_control, &Control::RaiseMouseLeaveEvent, lowest_common_ancestor); // dispatch mouse leave event. - if (new_control != nullptr) - { - DispatchEvent(new_control, &Control::RaiseMouseEnterEvent, lowest_common_ancestor, point); // dispatch mouse enter event. - UpdateCursor(); - } + DispatchEvent(new_control, &Control::RaiseMouseEnterEvent, lowest_common_ancestor, point); // dispatch mouse enter event. + UpdateCursor(); } } } diff --git a/src/ui/window.h b/src/ui/window.h index 1d188a05..822bf32d 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -8,294 +8,295 @@ #include "control.h" #include "events/ui_event.h" -namespace cru { - namespace graph { - class WindowRenderTarget; - } - - namespace ui { - class WindowClass : public Object - { - public: - WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); - WindowClass(const WindowClass& other) = delete; - WindowClass(WindowClass&& other) = delete; - WindowClass& operator=(const WindowClass& other) = delete; - WindowClass& operator=(WindowClass&& other) = delete; - ~WindowClass() override = default; - - - const wchar_t* GetName() const - { - return name_.c_str(); - } +namespace cru::graph +{ + class WindowRenderTarget; +} - ATOM GetAtom() const - { - return atom_; - } +namespace cru::ui +{ + class WindowClass : public Object + { + public: + WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); + WindowClass(const WindowClass& other) = delete; + WindowClass(WindowClass&& other) = delete; + WindowClass& operator=(const WindowClass& other) = delete; + WindowClass& operator=(WindowClass&& other) = delete; + ~WindowClass() override = default; - private: - String name_; - ATOM atom_; - }; - class WindowManager : public Object + const wchar_t* GetName() const { - public: - WindowManager(); - WindowManager(const WindowManager& other) = delete; - WindowManager(WindowManager&& other) = delete; - WindowManager& operator=(const WindowManager& other) = delete; - WindowManager& operator=(WindowManager&& other) = delete; - ~WindowManager() override = default; + return name_.c_str(); + } + ATOM GetAtom() const + { + return atom_; + } + + private: + String name_; + ATOM atom_; + }; + + class WindowManager : public Object + { + public: + WindowManager(); + WindowManager(const WindowManager& other) = delete; + WindowManager(WindowManager&& other) = delete; + WindowManager& operator=(const WindowManager& other) = delete; + WindowManager& operator=(WindowManager&& other) = delete; + ~WindowManager() override = default; + + + //Get the general window class for creating ordinary window. + WindowClass* GetGeneralWindowClass() const + { + return general_window_class_.get(); + } - //Get the general window class for creating ordinary window. - WindowClass* GetGeneralWindowClass() const - { - return general_window_class_.get(); - } - - //Register a window newly created. - //This function adds the hwnd to hwnd-window map. - //It should be called immediately after a window was created. - void RegisterWindow(HWND hwnd, Window* window); + //Register a window newly created. + //This function adds the hwnd to hwnd-window map. + //It should be called immediately after a window was created. + void RegisterWindow(HWND hwnd, Window* window); - //Unregister a window that is going to be destroyed. - //This function removes the hwnd from the hwnd-window map. - //It should be called immediately before a window is going to be destroyed, - void UnregisterWindow(HWND hwnd); + //Unregister a window that is going to be destroyed. + //This function removes the hwnd from the hwnd-window map. + //It should be called immediately before a window is going to be destroyed, + void UnregisterWindow(HWND hwnd); - //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. - Window* FromHandle(HWND hwnd); + //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. + Window* FromHandle(HWND hwnd); - std::vector<Window*> GetAllWindows() const; + std::vector<Window*> GetAllWindows() const; - private: - std::unique_ptr<WindowClass> general_window_class_; - std::map<HWND, Window*> window_map_; - }; + private: + std::unique_ptr<WindowClass> general_window_class_; + std::map<HWND, Window*> window_map_; + }; - class Window : public Control - { - friend class WindowManager; - public: - static constexpr auto control_type = L"Window"; + class Window : public Control + { + friend class WindowManager; + public: + static constexpr auto control_type = L"Window"; - Window(); - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; + Window(); + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; - public: - StringView GetControlType() const override final; + public: + StringView GetControlType() const override final; - //*************** region: handle *************** + //*************** region: handle *************** - //Get the handle of the window. Return null if window is invalid. - HWND GetWindowHandle() const - { - return hwnd_; - } + //Get the handle of the window. Return null if window is invalid. + HWND GetWindowHandle() const + { + return hwnd_; + } - //Return if the window is still valid, that is, hasn't been closed or destroyed. - bool IsWindowValid() const - { - return hwnd_ != nullptr; - } + //Return if the window is still valid, that is, hasn't been closed or destroyed. + bool IsWindowValid() const + { + return hwnd_ != nullptr; + } - //*************** region: window operations *************** + //*************** region: window operations *************** - //Close and destroy the window if the window is valid. - void Close(); + //Close and destroy the window if the window is valid. + void Close(); - //Send a repaint message to the window's message queue which may make the window repaint. - void Repaint() override; + //Send a repaint message to the window's message queue which may make the window repaint. + void Repaint() override; - //Show the window. - void Show(); + //Show the window. + void Show(); - //Hide thw window. - void Hide(); + //Hide thw window. + void Hide(); - //Get the client size. - Size GetClientSize(); + //Get the client size. + Size GetClientSize(); - //Set the client size and repaint. - void SetClientSize(const Size& size); + //Set the client size and repaint. + void SetClientSize(const Size& size); - //Get the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - Rect GetWindowRect(); + //Get the rect of the window containing frame. + //The lefttop of the rect is relative to screen lefttop. + Rect GetWindowRect(); - //Set the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - void SetWindowRect(const Rect& rect); + //Set the rect of the window containing frame. + //The lefttop of the rect is relative to screen lefttop. + void SetWindowRect(const Rect& rect); - //Handle the raw window message. - //Return true if the message is handled and get the result through "result" argument. - //Return false if the message is not handled. - bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); + //Handle the raw window message. + //Return true if the message is handled and get the result through "result" argument. + //Return false if the message is not handled. + bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); - //*************** region: mouse *************** + //*************** region: mouse *************** - Point GetMousePosition(); + Point GetMousePosition(); - Control* GetMouseHoverControl() const - { - return mouse_hover_control_; - } + Control* GetMouseHoverControl() const + { + return mouse_hover_control_; + } - //*************** region: position and size *************** + //*************** region: position and size *************** - //Always return (0, 0) for a window. - Point GetPositionRelative() override final; + //Always return (0, 0) for a window. + Point GetPositionRelative() override final; - //This method has no effect for a window. - void SetPositionRelative(const Point& position) override final; + //This method has no effect for a window. + void SetPositionRelative(const Point& position) override final; - //Get the size of client area for a window. - Size GetSize() override final; + //Get the size of client area for a window. + Size GetSize() override final; - //This method has no effect for a window. Use SetClientSize instead. - void SetSize(const Size& size) override final; + //This method has no effect for a window. Use SetClientSize instead. + void SetSize(const Size& size) override final; - //*************** region: layout *************** + //*************** region: layout *************** - void Relayout(); + void Relayout(); - //*************** region: functions *************** + //*************** region: functions *************** - //Refresh control list. - //It should be invoked every time a control is added or removed from the tree. - void RefreshControlList(); + //Refresh control list. + //It should be invoked every time a control is added or removed from the tree. + void RefreshControlList(); - //Get the most top control at "point". - Control* HitTest(const Point& point); + //Get the most top control at "point". + Control* HitTest(const Point& point); - //*************** region: focus *************** + //*************** region: focus *************** - //Request focus for specified control. - bool RequestFocusFor(Control* control); + //Request focus for specified control. + bool RequestFocusFor(Control* control); - //Get the control that has focus. - Control* GetFocusControl(); + //Get the control that has focus. + Control* GetFocusControl(); - //*************** region: mouse capture *************** + //*************** region: mouse capture *************** - Control* CaptureMouseFor(Control* control); - Control* ReleaseCurrentMouseCapture(); + Control* CaptureMouseFor(Control* control); + Control* ReleaseCurrentMouseCapture(); - - //*************** region: cursor *************** - void UpdateCursor(); + + //*************** region: cursor *************** + void UpdateCursor(); - //*************** region: debug *************** + //*************** region: debug *************** #ifdef CRU_DEBUG_LAYOUT - bool IsDebugLayout() const - { - return debug_layout_; - } + bool IsDebugLayout() const + { + return debug_layout_; + } - void SetDebugLayout(bool value); + void SetDebugLayout(bool value); #endif - - public: - //*************** region: events *************** - events::UiEvent activated_event; - events::UiEvent deactivated_event; - - events::WindowNativeMessageEvent native_message_event; - - private: - //*************** region: native operations *************** - - //Get the client rect in pixel. - RECT GetClientRectPixel(); - - bool IsMessageInQueue(UINT message); - - void SetCursorInternal(HCURSOR cursor); - - - //*************** region: layout *************** - - Size OnMeasureContent(const Size& available_size) override; - - - //*************** region: native messages *************** - - void OnDestroyInternal(); - void OnPaintInternal(); - void OnResizeInternal(int new_width, int new_height); - - void OnSetFocusInternal(); - void OnKillFocusInternal(); - - void OnMouseMoveInternal(POINT point); - void OnMouseLeaveInternal(); - void OnMouseDownInternal(MouseButton button, POINT point); - void OnMouseUpInternal(MouseButton button, POINT point); - - void OnKeyDownInternal(int virtual_code); - void OnKeyUpInternal(int virtual_code); - void OnCharInternal(wchar_t c); - - void OnActivatedInternal(); - void OnDeactivatedInternal(); - - //*************** region: event dispatcher helper *************** - - template<typename EventArgs> - using EventMethod = void (Control::*)(EventArgs&); - - // Dispatch the event. - // - // This will invoke the "event_method" of the control and its parent and parent's - // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. - // - // Args is of type "EventArgs". The first init argument is "sender", which is - // automatically bound to each receiving control. The second init argument is - // "original_sender", which is unchanged. And "args" will be perfectly forwarded - // as the rest arguments. - template<typename EventArgs, typename... Args> - void DispatchEvent(Control* original_sender, EventMethod<EventArgs> event_method, Control* last_receiver, Args&&... args) + + public: + //*************** region: events *************** + events::UiEvent activated_event; + events::UiEvent deactivated_event; + + events::WindowNativeMessageEvent native_message_event; + + private: + //*************** region: native operations *************** + + //Get the client rect in pixel. + RECT GetClientRectPixel(); + + bool IsMessageInQueue(UINT message); + + void SetCursorInternal(HCURSOR cursor); + + + //*************** region: layout *************** + + Size OnMeasureContent(const Size& available_size) override; + + + //*************** region: native messages *************** + + void OnDestroyInternal(); + void OnPaintInternal(); + void OnResizeInternal(int new_width, int new_height); + + void OnSetFocusInternal(); + void OnKillFocusInternal(); + + void OnMouseMoveInternal(POINT point); + void OnMouseLeaveInternal(); + void OnMouseDownInternal(MouseButton button, POINT point); + void OnMouseUpInternal(MouseButton button, POINT point); + + void OnKeyDownInternal(int virtual_code); + void OnKeyUpInternal(int virtual_code); + void OnCharInternal(wchar_t c); + + void OnActivatedInternal(); + void OnDeactivatedInternal(); + + //*************** region: event dispatcher helper *************** + + template<typename EventArgs> + using EventMethod = void (Control::*)(EventArgs&); + + // Dispatch the event. + // + // This will invoke the "event_method" of the control and its parent and parent's + // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. + // + // Args is of type "EventArgs". The first init argument is "sender", which is + // automatically bound to each receiving control. The second init argument is + // "original_sender", which is unchanged. And "args" will be perfectly forwarded + // as the rest arguments. + template<typename EventArgs, typename... Args> + void DispatchEvent(Control* original_sender, EventMethod<EventArgs> event_method, Control* last_receiver, Args&&... args) + { + auto control = original_sender; + while (control != nullptr && control != last_receiver) { - auto control = original_sender; - while (control != nullptr && control != last_receiver) - { - EventArgs event_args(control, original_sender, std::forward<Args>(args)...); - (control->*event_method)(event_args); - control = control->GetParent(); - } + EventArgs event_args(control, original_sender, std::forward<Args>(args)...); + (control->*event_method)(event_args); + control = control->GetParent(); } - - void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); - - private: - HWND hwnd_ = nullptr; - std::shared_ptr<graph::WindowRenderTarget> render_target_{}; - - std::list<Control*> control_list_{}; - - Control* mouse_hover_control_ = nullptr; - - bool window_focus_ = false; - Control* focus_control_ = this; // "focus_control_" can't be nullptr - - Control* mouse_capture_control_ = nullptr; - + } + + void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); + + private: + HWND hwnd_ = nullptr; + std::shared_ptr<graph::WindowRenderTarget> render_target_{}; + + std::list<Control*> control_list_{}; + + Control* mouse_hover_control_ = nullptr; + + bool window_focus_ = false; + Control* focus_control_ = this; // "focus_control_" can't be nullptr + + Control* mouse_capture_control_ = nullptr; + #ifdef CRU_DEBUG_LAYOUT - bool debug_layout_ = false; + bool debug_layout_ = false; #endif - }; - } + }; } + |