diff options
author | 杨宇千 <crupest@outlook.com> | 2018-11-10 22:39:26 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-10 22:39:26 +0800 |
commit | 8b04c0dd788be75c2dd7d8f58aebc7d6bf6752df (patch) | |
tree | 2d00b4f6a7af93a13d271d78e6ef682c335c91c7 /src | |
parent | 7c2fb4578b6997b5ab0d98121cda253f734139c1 (diff) | |
parent | b2eced8d9719eb00796c2674fc2c23ab0c9bbdbf (diff) | |
download | cru-8b04c0dd788be75c2dd7d8f58aebc7d6bf6752df.tar.gz cru-8b04c0dd788be75c2dd7d8f58aebc7d6bf6752df.tar.bz2 cru-8b04c0dd788be75c2dd7d8f58aebc7d6bf6752df.zip |
Merge pull request #11 from crupest/listitem
Add ListItem.
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 31 | ||||
-rw-r--r-- | src/ui/control.cpp | 9 | ||||
-rw-r--r-- | src/ui/controls/list_item.cpp | 62 | ||||
-rw-r--r-- | src/ui/controls/list_item.hpp | 67 | ||||
-rw-r--r-- | src/ui/convert_util.hpp | 13 | ||||
-rw-r--r-- | src/ui/layout_base.cpp | 18 | ||||
-rw-r--r-- | src/ui/layout_base.hpp | 46 | ||||
-rw-r--r-- | src/ui/ui_base.cpp | 5 | ||||
-rw-r--r-- | src/ui/ui_base.hpp | 45 | ||||
-rw-r--r-- | src/ui/ui_manager.cpp | 37 | ||||
-rw-r--r-- | src/ui/ui_manager.hpp | 9 | ||||
-rw-r--r-- | src/ui/window.cpp | 79 | ||||
-rw-r--r-- | src/ui/window.hpp | 20 |
13 files changed, 335 insertions, 106 deletions
diff --git a/src/main.cpp b/src/main.cpp index f9bc975c..19e821a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,10 +5,12 @@ #include "ui/controls/toggle_button.hpp" #include "ui/controls/button.hpp" #include "ui/controls/text_box.hpp" +#include "ui/controls/list_item.hpp" -using cru::ui::Rect; using cru::String; +using cru::StringView; using cru::Application; +using cru::ui::Rect; using cru::ui::Window; using cru::ui::Alignment; using cru::ui::LayoutSideParams; @@ -20,6 +22,7 @@ using cru::ui::controls::TextBlock; using cru::ui::controls::ToggleButton; using cru::ui::controls::Button; using cru::ui::controls::TextBox; +using cru::ui::controls::ListItem; int APIENTRY wWinMain( HINSTANCE hInstance, @@ -115,9 +118,31 @@ int APIENTRY wWinMain( const auto button = Button::Create(); button->GetLayoutParams()->padding = Thickness(20, 5); button->AddChild(TextBlock::Create(L"Show popup window parenting this.")); - button->mouse_click_event.AddHandler([window](auto) + button->mouse_click_event.AddHandler([window, button](auto) { - Window::CreatePopup(window)->Show(); + const auto popup = Window::CreatePopup(window); + + auto create_menu_item = [](const String& text) -> ListItem* + { + return CreateWithLayout<ListItem>( + LayoutSideParams::Content(Alignment::Start), + LayoutSideParams::Content(Alignment::Start), + ControlList{ TextBlock::Create(text) } + ); + }; + + const auto menu = LinearLayout::Create(LinearLayout::Orientation::Vertical, ControlList{ + create_menu_item(L"copy"), + create_menu_item(L"cut"), + create_menu_item(L"paste") + }); + + popup->AddChild(menu); + + popup->SetSizeFitContent(); + popup->SetWindowPosition(window->PointToScreen(button->GetPositionAbsolute())); + + popup->Show(); }); layout->AddChild(button); } diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 5d15e287..2d09a382 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -7,6 +7,7 @@ #include "graph/graph.hpp" #include "exception.hpp" #include "cru_debug.hpp" +#include "convert_util.hpp" #ifdef CRU_DEBUG_LAYOUT #include "ui_manager.hpp" @@ -237,7 +238,7 @@ namespace cru::ui void Control::InvalidateLayout() { if (const auto window = GetWindow()) - LayoutManager::GetInstance()->InvalidateWindowLayout(window); + window->WindowInvalidateLayout(); } void Control::Measure(const Size& available_size) @@ -376,12 +377,6 @@ namespace cru::ui 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::OnDrawCore(ID2D1DeviceContext* device_context) { #ifdef CRU_DEBUG_LAYOUT diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp new file mode 100644 index 00000000..bdd44273 --- /dev/null +++ b/src/ui/controls/list_item.cpp @@ -0,0 +1,62 @@ +#include "list_item.hpp" + +#include "ui/ui_manager.hpp" +#include "ui/convert_util.hpp" + +namespace cru::ui::controls +{ + ListItem::ListItem() : Control(true) + { + const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources(); + + brushes_[State::Normal].border_brush = predefine_resources->list_item_normal_border_brush; + brushes_[State::Normal].fill_brush = predefine_resources->list_item_normal_fill_brush; + brushes_[State::Hover] .border_brush = predefine_resources->list_item_hover_border_brush; + brushes_[State::Hover] .fill_brush = predefine_resources->list_item_hover_fill_brush; + brushes_[State::Select].border_brush = predefine_resources->list_item_select_border_brush; + brushes_[State::Select].fill_brush = predefine_resources->list_item_select_fill_brush; + } + + StringView ListItem::GetControlType() const + { + return control_type; + } + + void ListItem::SetState(const State state) + { + state_ = state; + Repaint(); + } + + void ListItem::OnDrawForeground(ID2D1DeviceContext* device_context) + { + const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize()); + device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get()); + device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1); + } + + void ListItem::OnMouseEnterCore(events::MouseEventArgs& args) + { + if (GetState() == State::Select) + return; + + if (IsAnyMouseButtonDown()) + return; + + SetState(State::Hover); + } + + void ListItem::OnMouseLeaveCore(events::MouseEventArgs& args) + { + if (GetState() == State::Select) + return; + + SetState(State::Normal); + } + + void ListItem::OnMouseClickCore(events::MouseButtonEventArgs& args) + { + if (args.GetMouseButton() == MouseButton::Left) + SetState(State::Select); + } +} diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp new file mode 100644 index 00000000..1de89b5f --- /dev/null +++ b/src/ui/controls/list_item.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <map> +#include <initializer_list> + +#include "ui/control.hpp" + +namespace cru::ui::controls +{ + class ListItem : public Control + { + public: + static constexpr auto control_type = L"ListItem"; + + enum class State + { + Normal, + Hover, + Select + }; + + private: + struct StateBrush + { + Microsoft::WRL::ComPtr<ID2D1Brush> border_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> fill_brush; + }; + + public: + static ListItem* Create(const std::initializer_list<Control*>& children) + { + const auto list_item = new ListItem(); + for (auto control : children) + list_item->AddChild(control); + return list_item; + } + + private: + ListItem(); + public: + ListItem(const ListItem& other) = delete; + ListItem(ListItem&& other) = delete; + ListItem& operator=(const ListItem& other) = delete; + ListItem& operator=(ListItem&& other) = delete; + ~ListItem() override = default; + + StringView GetControlType() const override; + + State GetState() const + { + return state_; + } + + void SetState(State state); + + protected: + void OnDrawForeground(ID2D1DeviceContext* device_context) override; + + void OnMouseEnterCore(events::MouseEventArgs& args) override final; + void OnMouseLeaveCore(events::MouseEventArgs& args) override final; + void OnMouseClickCore(events::MouseButtonEventArgs& args) override final; + + private: + State state_ = State::Normal; + std::map<State, StateBrush> brushes_{}; + }; +} diff --git a/src/ui/convert_util.hpp b/src/ui/convert_util.hpp new file mode 100644 index 00000000..1c18f59b --- /dev/null +++ b/src/ui/convert_util.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "system_headers.hpp" + +#include "ui_base.hpp" + +namespace cru::ui +{ + inline D2D1_RECT_F Convert(const Rect& rect) + { + return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); + } +} diff --git a/src/ui/layout_base.cpp b/src/ui/layout_base.cpp index 5363c52b..40bb71b3 100644 --- a/src/ui/layout_base.cpp +++ b/src/ui/layout_base.cpp @@ -68,24 +68,6 @@ namespace cru::ui RefreshControlPositionCacheInternal(control, point); } - void LayoutManager::InvalidateWindowLayout(Window* window) - { - layout_invalid_windows_.insert(window); - - if (layout_invalid_windows_.size() == 1) - InvokeLater([this]() - { - this->RefreshInvalidWindowLayout(); - }); - } - - void LayoutManager::RefreshInvalidWindowLayout() - { - for (const auto window : layout_invalid_windows_) - window->Relayout(); - layout_invalid_windows_.clear(); - } - void LayoutManager::RefreshControlPositionCacheInternal(Control * control, const Point & parent_lefttop_absolute) { const auto position = control->GetPositionRelative(); diff --git a/src/ui/layout_base.hpp b/src/ui/layout_base.hpp index 512301b7..7ae6f65c 100644 --- a/src/ui/layout_base.hpp +++ b/src/ui/layout_base.hpp @@ -33,45 +33,6 @@ namespace cru::ui Margin // including content, padding, border and margin }; - struct Thickness - { - constexpr static Thickness Zero() - { - return Thickness(0); - } - - constexpr Thickness() : Thickness(0) { } - - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } - - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } - - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } - - float GetHorizontalTotal() const - { - return left + right; - } - - float GetVerticalTotal() const - { - return top + bottom; - } - - float Validate() const - { - return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; - } - - float left; - float top; - float right; - float bottom; - }; - struct LayoutSideParams final { constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) @@ -171,13 +132,6 @@ namespace cru::ui //Refresh position cache of the control and its descendants immediately. static void RefreshControlPositionCache(Control* control); - - //*************** region: layout *************** - - void InvalidateWindowLayout(Window* window); - - void RefreshInvalidWindowLayout(); - private: static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute); diff --git a/src/ui/ui_base.cpp b/src/ui/ui_base.cpp index b52694e7..c91fcd7b 100644 --- a/src/ui/ui_base.cpp +++ b/src/ui/ui_base.cpp @@ -15,4 +15,9 @@ namespace cru::ui const auto result = ::GetKeyState(virtual_code); return (static_cast<unsigned short>(result) & 1) != 0; } + + bool IsAnyMouseButtonDown() + { + return IsKeyDown(VK_LBUTTON) || IsKeyDown(VK_RBUTTON) || IsKeyDown(VK_MBUTTON); + } } diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index 8daa43d7..c20d44b6 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -63,6 +63,45 @@ namespace cru::ui return !(left == right); } + struct Thickness + { + constexpr static Thickness Zero() + { + return Thickness(0); + } + + constexpr Thickness() : Thickness(0) { } + + constexpr explicit Thickness(const float width) + : left(width), top(width), right(width), bottom(width) { } + + constexpr explicit Thickness(const float horizontal, const float vertical) + : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } + + constexpr Thickness(const float left, const float top, const float right, const float bottom) + : left(left), top(top), right(right), bottom(bottom) { } + + float GetHorizontalTotal() const + { + return left + right; + } + + float GetVerticalTotal() const + { + return top + bottom; + } + + float Validate() const + { + return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; + } + + float left; + float top; + float right; + float bottom; + }; + struct Rect { constexpr Rect() = default; @@ -101,6 +140,11 @@ namespace cru::ui return Size(width, height); } + constexpr Rect Shrink(const Thickness& thickness) const + { + return Rect(left + thickness.left, top + thickness.top, width - thickness.GetHorizontalTotal(), height - thickness.GetVerticalTotal()); + } + constexpr bool IsPointInside(const Point& point) const { return @@ -154,4 +198,5 @@ namespace cru::ui bool IsKeyDown(int virtual_code); bool IsKeyToggled(int virtual_code); + bool IsAnyMouseButtonDown(); } diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp index 3918b2d5..d803e0cb 100644 --- a/src/ui/ui_manager.cpp +++ b/src/ui/ui_manager.cpp @@ -52,29 +52,36 @@ namespace cru::ui //!!! never use default constructor of border at here, because it will recursively call this method! PredefineResources::PredefineResources(graph::GraphManager* graph_manager) : - border_property_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + border_property_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - button_normal_border{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::RoyalBlue)), 2, 6, 6}, - button_press_border{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Blue)), 2, 6, 6}, + button_normal_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::RoyalBlue)), 2, 6, 6}, + button_press_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Blue)), 2, 6, 6}, - text_control_selection_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, + text_control_selection_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, - text_box_border{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_format{CreateDefaultTextFormat(graph_manager)}, - text_box_caret_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + text_box_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + text_box_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + text_box_text_format {CreateDefaultTextFormat(graph_manager)}, + text_box_caret_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_block_text_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_block_text_format{CreateDefaultTextFormat(graph_manager)}, + text_block_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + text_block_text_format {CreateDefaultTextFormat(graph_manager)}, - toggle_button_on_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DeepSkyBlue))}, - toggle_button_off_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightGray))} + toggle_button_on_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DeepSkyBlue))}, + toggle_button_off_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightGray))}, + + list_item_normal_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, + list_item_normal_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, + list_item_hover_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue))}, + list_item_hover_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3))}, + list_item_select_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::MediumBlue))}, + list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3))} #ifdef CRU_DEBUG_LAYOUT , - debug_layout_out_border_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, - debug_layout_margin_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, - debug_layout_padding_brush{CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} + debug_layout_out_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, + debug_layout_margin_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, + debug_layout_padding_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} #endif { diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp index 753da907..6b368e12 100644 --- a/src/ui/ui_manager.hpp +++ b/src/ui/ui_manager.hpp @@ -53,6 +53,15 @@ namespace cru::ui Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_on_brush; Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_off_brush; + //region ListItem + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_border_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_fill_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_border_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_fill_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_border_brush; + Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_fill_brush; + + #ifdef CRU_DEBUG_LAYOUT //region debug Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_out_border_brush; diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 2737b70b..f8e6d4f3 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -98,6 +98,15 @@ namespace cru::ui ); } + inline POINT DipToPi(const Point& dip_point) + { + POINT result; + result.x = graph::DipToPixelX(dip_point.x); + result.y = graph::DipToPixelY(dip_point.y); + return result; + } + + namespace { Cursor::Ptr GetCursorInherit(Control* control) @@ -275,6 +284,41 @@ namespace cru::ui } } + void Window::SetWindowPosition(const Point& position) + { + if (IsWindowValid()) { + SetWindowPos( + hwnd_, nullptr, + graph::DipToPixelX(position.x), + graph::DipToPixelY(position.y), + 0, 0, + SWP_NOZORDER | SWP_NOSIZE + ); + } + } + + Point Window::PointToScreen(const Point& point) + { + if (!IsWindowValid()) + return Point::Zero(); + + auto p = DipToPi(point); + if (::ClientToScreen(GetWindowHandle(), &p) == 0) + throw Win32Error(::GetLastError(), "Failed transform point from window to screen."); + return PiToDip(p); + } + + Point Window::PointFromScreen(const Point& point) + { + if (!IsWindowValid()) + return Point::Zero(); + + auto p = DipToPi(point); + if (::ScreenToClient(GetWindowHandle(), &p) == 0) + throw Win32Error(::GetLastError(), "Failed transform point from screen to window."); + return PiToDip(p); + } + bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) { if (!native_message_event.IsNoHandler()) @@ -429,10 +473,32 @@ namespace cru::ui } + void Window::WindowInvalidateLayout() + { + if (is_layout_invalid_) + return; + + is_layout_invalid_ = true; + InvokeLater([this] + { + if (is_layout_invalid_) + Relayout(); + }); + } + void Window::Relayout() { - OnMeasureCore(GetSize()); + Measure(GetSize()); OnLayoutCore(Rect(Point::Zero(), GetSize())); + is_layout_invalid_ = false; + } + + void Window::SetSizeFitContent(const Size& max_size) + { + Measure(max_size); + SetClientSize(GetDesiredSize()); + OnLayoutCore(Rect(Point::Zero(), GetSize())); + is_layout_invalid_ = false; } void Window::RefreshControlList() { @@ -557,15 +623,6 @@ namespace cru::ui } } - Size Window::OnMeasureContent(const Size& available_size) - { - for (auto control: GetChildren()) - { - control->Measure(available_size); - } - return available_size; - } - void Window::OnDestroyInternal() { WindowManager::GetInstance()->UnregisterWindow(hwnd_); hwnd_ = nullptr; @@ -597,7 +654,7 @@ namespace cru::ui 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(); + WindowInvalidateLayout(); } void Window::OnSetFocusInternal() diff --git a/src/ui/window.hpp b/src/ui/window.hpp index 963bff78..b2b8cde0 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -162,6 +162,13 @@ namespace cru::ui //The lefttop of the rect is relative to screen lefttop. void SetWindowRect(const Rect& rect); + //Set the lefttop of the window relative to screen. + void SetWindowPosition(const Point& position); + + Point PointToScreen(const Point& point); + + Point PointFromScreen(const Point& point); + //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. @@ -192,8 +199,12 @@ namespace cru::ui //*************** region: layout *************** + void WindowInvalidateLayout(); + void Relayout(); + void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); + //*************** region: functions *************** //Refresh control list. @@ -250,11 +261,6 @@ namespace cru::ui void SetCursorInternal(HCURSOR cursor); - //*************** region: layout *************** - - Size OnMeasureContent(const Size& available_size) override; - - //*************** region: native messages *************** void OnDestroyInternal(); @@ -319,7 +325,9 @@ namespace cru::ui Control* focus_control_ = this; // "focus_control_" can't be nullptr Control* mouse_capture_control_ = nullptr; - + + bool is_layout_invalid_ = false; + #ifdef CRU_DEBUG_LAYOUT bool debug_layout_ = false; #endif |