aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj3
-rw-r--r--CruUI.vcxproj.filters9
-rw-r--r--src/main.cpp31
-rw-r--r--src/ui/control.cpp9
-rw-r--r--src/ui/controls/list_item.cpp62
-rw-r--r--src/ui/controls/list_item.hpp67
-rw-r--r--src/ui/convert_util.hpp13
-rw-r--r--src/ui/layout_base.cpp18
-rw-r--r--src/ui/layout_base.hpp46
-rw-r--r--src/ui/ui_base.cpp5
-rw-r--r--src/ui/ui_base.hpp45
-rw-r--r--src/ui/ui_manager.cpp37
-rw-r--r--src/ui/ui_manager.hpp9
-rw-r--r--src/ui/window.cpp79
-rw-r--r--src/ui/window.hpp20
15 files changed, 347 insertions, 106 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index de86b39c..0c0ba1b6 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -130,11 +130,13 @@
<ClCompile Include="src\ui\border_property.cpp" />
<ClCompile Include="src\ui\controls\button.cpp" />
<ClCompile Include="src\ui\controls\linear_layout.cpp" />
+ <ClCompile Include="src\ui\controls\list_item.cpp" />
<ClCompile Include="src\ui\controls\text_block.cpp" />
<ClCompile Include="src\ui\controls\text_box.cpp" />
<ClInclude Include="src\any_map.hpp" />
<ClInclude Include="src\format.hpp" />
<ClInclude Include="src\ui\border_property.hpp" />
+ <ClInclude Include="src\ui\controls\list_item.hpp" />
<ClInclude Include="src\ui\controls\text_control.hpp" />
<ClCompile Include="src\ui\controls\toggle_button.cpp" />
<ClCompile Include="src\ui\cursor.cpp" />
@@ -162,6 +164,7 @@
<ClInclude Include="src\ui\controls\text_box.hpp" />
<ClCompile Include="src\ui\controls\text_control.cpp" />
<ClInclude Include="src\ui\controls\toggle_button.hpp" />
+ <ClInclude Include="src\ui\convert_util.hpp" />
<ClInclude Include="src\ui\cursor.hpp" />
<ClInclude Include="src\ui\events\ui_event.hpp" />
<ClInclude Include="src\ui\layout_base.hpp" />
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 92fe4869..7a84270a 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -81,6 +81,9 @@
<ClCompile Include="src\ui\ui_manager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\ui\controls\list_item.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\graph\graph.hpp">
@@ -161,6 +164,12 @@
<ClInclude Include="src\any_map.hpp">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\ui\controls\list_item.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\convert_util.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\application.cpp">
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