aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI/application.cpp4
-rw-r--r--CruUI/application.h4
-rw-r--r--CruUI/ui/control.cpp25
-rw-r--r--CruUI/ui/control.h7
-rw-r--r--CruUI/ui/controls/text_block.cpp27
-rw-r--r--CruUI/ui/controls/text_block.h40
-rw-r--r--CruUI/ui/ui_base.h24
-rw-r--r--CruUI/ui/window.cpp1073
8 files changed, 651 insertions, 553 deletions
diff --git a/CruUI/application.cpp b/CruUI/application.cpp
index 4fd2cf9f..3ee7817d 100644
--- a/CruUI/application.cpp
+++ b/CruUI/application.cpp
@@ -82,9 +82,9 @@ namespace cru {
return false;
}
- void InvokeLater(const InvokeLaterAction& action) {
+ void InvokeLater(InvokeLaterAction&& action) {
//copy the action to a safe place
- auto p_action_copy = new InvokeLaterAction(action);
+ auto p_action_copy = new InvokeLaterAction(std::move(action));
PostMessage(
nullptr,
diff --git a/CruUI/application.h b/CruUI/application.h
index ee108cbc..daea7e06 100644
--- a/CruUI/application.h
+++ b/CruUI/application.h
@@ -70,6 +70,6 @@ namespace cru
};
- using InvokeLaterAction = std::function<void()>;
- void InvokeLater(const InvokeLaterAction& action);
+ using InvokeLaterAction = Action<>;
+ void InvokeLater(InvokeLaterAction&& action);
}
diff --git a/CruUI/ui/control.cpp b/CruUI/ui/control.cpp
index e8223498..68713841 100644
--- a/CruUI/ui/control.cpp
+++ b/CruUI/ui/control.cpp
@@ -3,6 +3,7 @@
#include <algorithm>
#include "window.h"
+#include "timer.h"
namespace cru {
namespace ui {
@@ -12,6 +13,7 @@ namespace cru {
window_(nullptr),
parent_(nullptr),
children_(),
+ old_position_(Point::zero),
position_(Point::zero),
size_(Size::zero),
position_cache_(),
@@ -116,17 +118,15 @@ namespace cru {
return ancestor;
}
- void TraverseDescendantsInternal(Control* control,
- const std::function<void(Control*)>& predicate)
+ void TraverseDescendantsInternal(Control* control, Action<Control*>& predicate)
{
predicate(control);
- control->ForeachChild([predicate](Control* c) {
+ control->ForeachChild([&predicate](Control* c) {
TraverseDescendantsInternal(c, predicate);
});
}
- void Control::TraverseDescendants(
- const std::function<void(Control*)>& predicate)
+ void Control::TraverseDescendants(Action<Control*>&& predicate)
{
TraverseDescendantsInternal(this, predicate);
}
@@ -138,13 +138,14 @@ namespace cru {
void Control::SetPositionRelative(const Point & position)
{
+ if (old_position_ == position) // if cache has been refreshed and no pending notify
+ old_position_ = position_;
position_ = position;
if (auto window = GetWindow())
{
window->GetLayoutManager()->InvalidateControlPositionCache(this);
window->Repaint();
}
- //TODO: Position change notify.
}
Size Control::GetSize()
@@ -416,7 +417,7 @@ namespace cru {
auto&& calculate_final_length = [](const MeasureLength& layout_length, const float length_for_children, const float max_child_length) -> float
{
- switch(layout_length.mode)
+ switch (layout_length.mode)
{
case MeasureMode::Exactly:
case MeasureMode::Stretch:
@@ -442,6 +443,16 @@ namespace cru {
});
}
+ void Control::CheckAndNotifyPositionChanged()
+ {
+ if (this->old_position_ != this->position_)
+ {
+ PositionChangedEventArgs args(this, this, this->old_position_, this->position_);
+ this->OnPositionChangedCore(args);
+ this->old_position_ = this->position_;
+ }
+ }
+
std::list<Control*> GetAncestorList(Control* control)
{
std::list<Control*> l;
diff --git a/CruUI/ui/control.h b/CruUI/ui/control.h
index f9c47a95..588eda17 100644
--- a/CruUI/ui/control.h
+++ b/CruUI/ui/control.h
@@ -80,7 +80,7 @@ namespace cru
}
//Traverse the tree rooted the control.
- void TraverseDescendants(const std::function<void(Control*)>& predicate);
+ void TraverseDescendants(Action<Control*>&& predicate);
//*************** region: position and size ***************
// Position and size part must be isolated from layout part.
@@ -225,11 +225,16 @@ namespace cru
virtual void OnLayout(const Rect& rect);
private:
+ // Only for layout manager to use.
+ void CheckAndNotifyPositionChanged();
+
+ private:
Window * window_;
Control * parent_;
std::vector<Control*> children_;
+ Point old_position_;
Point position_;
Size size_;
diff --git a/CruUI/ui/controls/text_block.cpp b/CruUI/ui/controls/text_block.cpp
new file mode 100644
index 00000000..b8fe742d
--- /dev/null
+++ b/CruUI/ui/controls/text_block.cpp
@@ -0,0 +1,27 @@
+#include "text_block.h"
+
+namespace cru
+{
+ namespace ui
+ {
+ namespace controls
+ {
+ void TextBlock::SetText(const String& text)
+ {
+ const auto old_text = text_;
+ text_ = text;
+ OnTextChangedCore(old_text, text);
+ }
+
+ void TextBlock::OnSizeChangedCore(events::SizeChangedEventArgs& args)
+ {
+
+ }
+
+ void TextBlock::OnTextChangedCore(const String& old_text, const String& new_text)
+ {
+
+ }
+ }
+ }
+}
diff --git a/CruUI/ui/controls/text_block.h b/CruUI/ui/controls/text_block.h
new file mode 100644
index 00000000..aea4629f
--- /dev/null
+++ b/CruUI/ui/controls/text_block.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "ui/ui_base.h"
+#include "ui/control.h"
+
+namespace cru
+{
+ namespace ui
+ {
+ namespace controls
+ {
+ class TextBlock : public Control
+ {
+ public:
+ TextBlock();
+ TextBlock(const TextBlock& other) = delete;
+ TextBlock(TextBlock&& other) = delete;
+ TextBlock& operator=(const TextBlock& other) = delete;
+ TextBlock& operator=(TextBlock&& other) = delete;
+ ~TextBlock() override;
+
+ String GetText() const
+ {
+ return text_;
+ }
+
+ void SetText(const String& text);
+
+ protected:
+ void OnSizeChangedCore(events::SizeChangedEventArgs& args) override final;
+
+ private:
+ void OnTextChangedCore(const String& old_text, const String& new_text);
+
+ private:
+ String text_;
+ };
+ }
+ }
+}
diff --git a/CruUI/ui/ui_base.h b/CruUI/ui/ui_base.h
index 8dfbf53d..43b4a6dc 100644
--- a/CruUI/ui/ui_base.h
+++ b/CruUI/ui/ui_base.h
@@ -15,6 +15,16 @@ namespace cru
float y;
};
+ inline bool operator==(const Point& left, const Point& right)
+ {
+ return left.x == right.x && left.y == right.y;
+ }
+
+ inline bool operator!=(const Point& left, const Point& right)
+ {
+ return !(left == right);
+ }
+
struct Size
{
static const Size zero;
@@ -26,7 +36,7 @@ namespace cru
float height;
};
- struct Rect
+ struct Rect
{
Rect() = default;
Rect(const float left, const float top, const float width, const float height)
@@ -54,10 +64,10 @@ namespace cru
return Point(left, top);
}
- Point GetRightBottom() const
+ Point GetRightBottom() const
{
- return Point(left + width, top + height);
- }
+ return Point(left + width, top + height);
+ }
Size GetSize() const
{
@@ -67,9 +77,9 @@ namespace cru
bool IsPointInside(const Point& point) const
{
return
- point.x >= left &&
- point.x < GetRight() &&
- point.y >= top &&
+ point.x >= left &&
+ point.x < GetRight() &&
+ point.y >= top &&
point.y < GetBottom();
}
diff --git a/CruUI/ui/window.cpp b/CruUI/ui/window.cpp
index 22d97696..bc7798c3 100644
--- a/CruUI/ui/window.cpp
+++ b/CruUI/ui/window.cpp
@@ -6,540 +6,545 @@
namespace cru
{
- namespace ui
- {
- WindowClass::WindowClass(const std::wstring& name, WNDPROC window_proc, HINSTANCE hinstance)
- : name_(name)
- {
- WNDCLASSEX window_class;
- window_class.cbSize = sizeof(WNDCLASSEX);
-
- window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
- window_class.lpfnWndProc = window_proc;
- window_class.cbClsExtra = 0;
- window_class.cbWndExtra = 0;
- window_class.hInstance = hinstance;
- 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.");
- }
-
- WindowClass::~WindowClass()
- {
-
- }
-
- const wchar_t * WindowClass::GetName() {
- return name_.c_str();
- }
-
- ATOM WindowClass::GetAtom() {
- return atom_;
- }
-
- 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;
-
- return DefWindowProc(hWnd, Msg, wParam, lParam);
- }
-
- 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.");
-
- 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);
-
- 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;
- }
-
- WindowLayoutManager::WindowLayoutManager()
- {
- }
-
- WindowLayoutManager::~WindowLayoutManager()
- {
- }
-
- void WindowLayoutManager::InvalidateControlPositionCache(Control * control)
- {
- if (cache_invalid_controls_.count(control) == 1)
- return;
-
- // find descendant then erase it; find ancestor then just return.
- for (auto i = cache_invalid_controls_.cbegin(); i != cache_invalid_controls_.cend(); ++i)
- {
- if (IsAncestorOrDescendant(*i, control) == control)
- cache_invalid_controls_.erase(i);
- else
- return; // find a ancestor of "control", just return
- }
-
- cache_invalid_controls_.insert(control);
-
- if (cache_invalid_controls_.size() == 1) // when insert just now and not repeat to "InvokeLater".
- {
- InvokeLater([this] {
- RefreshInvalidControlPositionCache();
- });
- }
- }
-
- void WindowLayoutManager::RefreshInvalidControlPositionCache()
- {
- for (auto i : cache_invalid_controls_)
- RefreshControlPositionCache(i);
- cache_invalid_controls_.clear();
- }
-
- void WindowLayoutManager::RefreshControlPositionCache(Control * control)
- {
- Point point = Point::zero;
- auto parent = control;
- while ((parent = parent->GetParent())) {
- const auto p = parent->GetPositionRelative();
- point.x += p.x;
- point.y += p.y;
- }
- RefreshControlPositionCacheInternal(control, point);
- }
-
- void WindowLayoutManager::RefreshControlPositionCacheInternal(Control * control, const Point & parent_lefttop_absolute)
- {
- const auto position = control->GetPositionRelative();
- Point lefttop(
- parent_lefttop_absolute.x + position.x,
- parent_lefttop_absolute.y + position.x
- );
- control->position_cache_.lefttop_position_absolute = lefttop;
- control->ForeachChild([lefttop](Control* c) {
- RefreshControlPositionCacheInternal(c, lefttop);
- });
- }
-
- Window::Window() : layout_manager_(new WindowLayoutManager()), control_list_({ this }) {
- 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.");
-
- app->GetWindowManager()->RegisterWindow(hwnd_, this);
-
- render_target_ = app->GetGraphManager()->CreateWindowRenderTarget(hwnd_);
- }
-
- Window::~Window() {
- Close();
- }
-
- WindowLayoutManager* Window::GetLayoutManager()
- {
- return layout_manager_.get();
- }
-
- HWND Window::GetWindowHandle()
- {
- return hwnd_;
- }
-
- bool Window::IsWindowValid() {
- return hwnd_ != nullptr;
- }
-
- void Window::Close() {
- if (IsWindowValid())
- DestroyWindow(hwnd_);
- }
-
- void Window::Repaint() {
- if (IsWindowValid()) {
- InvalidateRect(hwnd_, nullptr, false);
- UpdateWindow(hwnd_);
- }
- }
-
- void Window::Show() {
- if (IsWindowValid()) {
- ShowWindow(hwnd_, SW_SHOWNORMAL);
- }
- }
-
- 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)
- );
- }
-
- 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 rect;
- ::GetWindowRect(hwnd_, &rect);
-
- 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
- );
- }
- }
-
- 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_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);
- OnMouseDownInternal(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);
- OnMouseDownInternal(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:
- {
- 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_SIZE:
- OnResizeInternal(LOWORD(l_param), HIWORD(l_param));
- result = 0;
- return true;
- case WM_DESTROY:
- OnDestroyInternal();
- result = 0;
- return true;
- default:
+ namespace ui
+ {
+ WindowClass::WindowClass(const std::wstring& name, WNDPROC window_proc, HINSTANCE hinstance)
+ : name_(name)
+ {
+ WNDCLASSEX window_class;
+ window_class.cbSize = sizeof(WNDCLASSEX);
+
+ window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
+ window_class.lpfnWndProc = window_proc;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = hinstance;
+ 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.");
+ }
+
+ WindowClass::~WindowClass()
+ {
+
+ }
+
+ const wchar_t * WindowClass::GetName() {
+ return name_.c_str();
+ }
+
+ ATOM WindowClass::GetAtom() {
+ return atom_;
+ }
+
+ 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;
+
+ return DefWindowProc(hWnd, Msg, wParam, lParam);
+ }
+
+ 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.");
+
+ 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);
+
+ 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;
+ }
+
+ WindowLayoutManager::WindowLayoutManager()
+ {
+ }
+
+ WindowLayoutManager::~WindowLayoutManager()
+ {
+ }
+
+ void WindowLayoutManager::InvalidateControlPositionCache(Control * control)
+ {
+ if (cache_invalid_controls_.count(control) == 1)
+ return;
+
+ // find descendant then erase it; find ancestor then just return.
+ for (auto i = cache_invalid_controls_.cbegin(); i != cache_invalid_controls_.cend(); ++i)
+ {
+ if (IsAncestorOrDescendant(*i, control) == control)
+ cache_invalid_controls_.erase(i);
+ else
+ return; // find a ancestor of "control", just return
+ }
+
+ cache_invalid_controls_.insert(control);
+
+ if (cache_invalid_controls_.size() == 1) // when insert just now and not repeat to "InvokeLater".
+ {
+ InvokeLater([this] {
+ RefreshInvalidControlPositionCache();
+ for (const auto i : cache_invalid_controls_)
+ i->TraverseDescendants([](Control* control)
+ {
+ control->CheckAndNotifyPositionChanged();
+ });
+ cache_invalid_controls_.clear();
+ });
+ }
+ }
+
+ void WindowLayoutManager::RefreshInvalidControlPositionCache()
+ {
+ for (const auto i : cache_invalid_controls_)
+ RefreshControlPositionCache(i);
+ }
+
+ void WindowLayoutManager::RefreshControlPositionCache(Control * control)
+ {
+ Point point = Point::zero;
+ auto parent = control;
+ while ((parent = parent->GetParent())) {
+ const auto p = parent->GetPositionRelative();
+ point.x += p.x;
+ point.y += p.y;
+ }
+ RefreshControlPositionCacheInternal(control, point);
+ }
+
+ void WindowLayoutManager::RefreshControlPositionCacheInternal(Control * control, const Point & parent_lefttop_absolute)
+ {
+ const auto position = control->GetPositionRelative();
+ Point lefttop(
+ parent_lefttop_absolute.x + position.x,
+ parent_lefttop_absolute.y + position.x
+ );
+ control->position_cache_.lefttop_position_absolute = lefttop;
+ control->ForeachChild([lefttop](Control* c) {
+ RefreshControlPositionCacheInternal(c, lefttop);
+ });
+ }
+
+ Window::Window() : layout_manager_(new WindowLayoutManager()), control_list_({ this }) {
+ 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.");
+
+ app->GetWindowManager()->RegisterWindow(hwnd_, this);
+
+ render_target_ = app->GetGraphManager()->CreateWindowRenderTarget(hwnd_);
+ }
+
+ Window::~Window() {
+ Close();
+ }
+
+ WindowLayoutManager* Window::GetLayoutManager()
+ {
+ return layout_manager_.get();
+ }
+
+ HWND Window::GetWindowHandle()
+ {
+ return hwnd_;
+ }
+
+ bool Window::IsWindowValid() {
+ return hwnd_ != nullptr;
+ }
+
+ void Window::Close() {
+ if (IsWindowValid())
+ DestroyWindow(hwnd_);
+ }
+
+ void Window::Repaint() {
+ if (IsWindowValid()) {
+ InvalidateRect(hwnd_, nullptr, false);
+ UpdateWindow(hwnd_);
+ }
+ }
+
+ void Window::Show() {
+ if (IsWindowValid()) {
+ ShowWindow(hwnd_, SW_SHOWNORMAL);
+ }
+ }
+
+ 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)
+ );
+ }
+
+ 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 rect;
+ ::GetWindowRect(hwnd_, &rect);
+
+ 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
+ );
+ }
+ }
+
+ 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_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);
+ OnMouseDownInternal(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);
+ OnMouseDownInternal(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:
+ {
+ 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_SIZE:
+ OnResizeInternal(LOWORD(l_param), HIWORD(l_param));
+ result = 0;
+ return true;
+ case WM_DESTROY:
+ OnDestroyInternal();
+ result = 0;
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ Point Window::GetPositionRelative()
+ {
+ return Point();
+ }
+
+ void Window::SetPositionRelative(const Point & position)
+ {
+
+ }
+
+ Size Window::GetSize()
+ {
+ return GetClientSize();
+ }
+
+ void Window::SetSize(const Size & size)
+ {
+ SetClientSize(size);
+ }
+
+ 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->AbsoluteToLocal(point))) {
+ return control;
+ }
+ }
+ 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.");
+
+ if (!IsWindowValid())
return false;
- }
- }
-
- Point Window::GetPositionRelative()
- {
- return Point();
- }
-
- void Window::SetPositionRelative(const Point & position)
- {
-
- }
-
- Size Window::GetSize()
- {
- return GetClientSize();
- }
-
- void Window::SetSize(const Size & size)
- {
- SetClientSize(size);
- }
- 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->AbsoluteToLocal(point))) {
- return control;
- }
- }
- 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.");
-
- if (!IsWindowValid())
- return false;
-
- if (!window_focus_)
- {
- ::SetFocus(hwnd_);
- focus_control_ = control;
- return true; // event dispatch will be done in window message handling function "OnSetFocusInternal".
- }
-
- if (focus_control_ == control)
- return true;
-
- DispatchEvent(focus_control_, &Control::OnLoseFocusCore, nullptr);
-
- focus_control_ = control;
-
- DispatchEvent(control, &Control::OnGetFocusCore, nullptr);
-
- return true;
- }
-
- Control* Window::GetFocusControl()
- {
- return focus_control_;
- }
-
- RECT Window::GetClientRectPixel() {
- RECT rect{ };
- GetClientRect(hwnd_, &rect);
- return rect;
- }
-
- void Window::OnDestroyInternal() {
- Application::GetInstance()->GetWindowManager()->UnregisterWindow(hwnd_);
- hwnd_ = nullptr;
- }
-
- void Window::OnPaintInternal() {
- render_target_->SetAsTarget();
-
- auto device_context = render_target_->GetD2DDeviceContext();
-
- device_context->BeginDraw();
-
- //Clear the background.
- device_context->Clear(D2D1::ColorF(D2D1::ColorF::White));
-
- Draw(device_context.Get());
-
- ThrowIfFailed(
- device_context->EndDraw(), "Failed to draw window."
- );
-
- render_target_->Present();
-
- ValidateRect(hwnd_, nullptr);
- }
-
- void Window::OnResizeInternal(int new_width, int new_height) {
- render_target_->ResizeBuffer(new_width, new_height);
- }
-
- void Window::OnSetFocusInternal()
- {
- window_focus_ = true;
- if (focus_control_ != nullptr)
- DispatchEvent(focus_control_, &Control::OnGetFocusCore, nullptr);
- }
-
- void Window::OnKillFocusInternal()
- {
- window_focus_ = false;
- if (focus_control_ != nullptr)
- DispatchEvent(focus_control_, &Control::OnLoseFocusCore, nullptr);
- }
-
- void Window::OnMouseMoveInternal(POINT point)
- {
- Point dip_point(
- graph::PixelToDipX(point.x),
- graph::PixelToDipY(point.y)
- );
-
- //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);
- }
-
- //Find the first control that hit test succeed.
- const auto new_control_mouse_hover = HitTest(dip_point);
-
- if (new_control_mouse_hover != mouse_hover_control_) //if the mouse-hover-on control changed
- {
- const auto lowest_common_ancestor = FindLowestCommonAncestor(mouse_hover_control_, new_control_mouse_hover);
- if (mouse_hover_control_ != nullptr) // if last mouse-hover-on control exists
- {
- // dispatch mouse leave event.
- DispatchEvent(mouse_hover_control_, &Control::OnMouseLeaveCore, lowest_common_ancestor);
- }
- mouse_hover_control_ = new_control_mouse_hover;
- // dispatch mouse enter event.
- DispatchEvent(new_control_mouse_hover, &Control::OnMouseEnterCore, lowest_common_ancestor, dip_point);
- }
-
- DispatchEvent(new_control_mouse_hover, &Control::OnMouseMoveCore, nullptr, dip_point);
- }
-
- void Window::OnMouseLeaveInternal()
- {
- DispatchEvent(mouse_hover_control_, &Control::OnMouseLeaveCore, nullptr);
- mouse_hover_control_ = nullptr;
- }
-
- void Window::OnMouseDownInternal(MouseButton button, POINT point)
- {
- Point dip_point(
- graph::PixelToDipX(point.x),
- graph::PixelToDipY(point.y)
- );
-
- const auto control = HitTest(dip_point);
-
- DispatchEvent(control, &Control::OnMouseDownCore, nullptr, dip_point, button);
- }
-
- void Window::OnMouseUpInternal(MouseButton button, POINT point)
- {
- Point dip_point(
- graph::PixelToDipX(point.x),
- graph::PixelToDipY(point.y)
- );
-
- const auto control = HitTest(dip_point);
-
- DispatchEvent(control, &Control::OnMouseUpCore, nullptr, dip_point, button);
- }
- }
+ if (!window_focus_)
+ {
+ ::SetFocus(hwnd_);
+ focus_control_ = control;
+ return true; // event dispatch will be done in window message handling function "OnSetFocusInternal".
+ }
+
+ if (focus_control_ == control)
+ return true;
+
+ DispatchEvent(focus_control_, &Control::OnLoseFocusCore, nullptr);
+
+ focus_control_ = control;
+
+ DispatchEvent(control, &Control::OnGetFocusCore, nullptr);
+
+ return true;
+ }
+
+ Control* Window::GetFocusControl()
+ {
+ return focus_control_;
+ }
+
+ RECT Window::GetClientRectPixel() {
+ RECT rect{ };
+ GetClientRect(hwnd_, &rect);
+ return rect;
+ }
+
+ void Window::OnDestroyInternal() {
+ Application::GetInstance()->GetWindowManager()->UnregisterWindow(hwnd_);
+ hwnd_ = nullptr;
+ }
+
+ void Window::OnPaintInternal() {
+ render_target_->SetAsTarget();
+
+ auto device_context = render_target_->GetD2DDeviceContext();
+
+ device_context->BeginDraw();
+
+ //Clear the background.
+ device_context->Clear(D2D1::ColorF(D2D1::ColorF::White));
+
+ Draw(device_context.Get());
+
+ ThrowIfFailed(
+ device_context->EndDraw(), "Failed to draw window."
+ );
+
+ render_target_->Present();
+
+ ValidateRect(hwnd_, nullptr);
+ }
+
+ void Window::OnResizeInternal(int new_width, int new_height) {
+ render_target_->ResizeBuffer(new_width, new_height);
+ }
+
+ void Window::OnSetFocusInternal()
+ {
+ window_focus_ = true;
+ if (focus_control_ != nullptr)
+ DispatchEvent(focus_control_, &Control::OnGetFocusCore, nullptr);
+ }
+
+ void Window::OnKillFocusInternal()
+ {
+ window_focus_ = false;
+ if (focus_control_ != nullptr)
+ DispatchEvent(focus_control_, &Control::OnLoseFocusCore, nullptr);
+ }
+
+ void Window::OnMouseMoveInternal(POINT point)
+ {
+ Point dip_point(
+ graph::PixelToDipX(point.x),
+ graph::PixelToDipY(point.y)
+ );
+
+ //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);
+ }
+
+ //Find the first control that hit test succeed.
+ const auto new_control_mouse_hover = HitTest(dip_point);
+
+ if (new_control_mouse_hover != mouse_hover_control_) //if the mouse-hover-on control changed
+ {
+ const auto lowest_common_ancestor = FindLowestCommonAncestor(mouse_hover_control_, new_control_mouse_hover);
+ if (mouse_hover_control_ != nullptr) // if last mouse-hover-on control exists
+ {
+ // dispatch mouse leave event.
+ DispatchEvent(mouse_hover_control_, &Control::OnMouseLeaveCore, lowest_common_ancestor);
+ }
+ mouse_hover_control_ = new_control_mouse_hover;
+ // dispatch mouse enter event.
+ DispatchEvent(new_control_mouse_hover, &Control::OnMouseEnterCore, lowest_common_ancestor, dip_point);
+ }
+
+ DispatchEvent(new_control_mouse_hover, &Control::OnMouseMoveCore, nullptr, dip_point);
+ }
+
+ void Window::OnMouseLeaveInternal()
+ {
+ DispatchEvent(mouse_hover_control_, &Control::OnMouseLeaveCore, nullptr);
+ mouse_hover_control_ = nullptr;
+ }
+
+ void Window::OnMouseDownInternal(MouseButton button, POINT point)
+ {
+ Point dip_point(
+ graph::PixelToDipX(point.x),
+ graph::PixelToDipY(point.y)
+ );
+
+ const auto control = HitTest(dip_point);
+
+ DispatchEvent(control, &Control::OnMouseDownCore, nullptr, dip_point, button);
+ }
+
+ void Window::OnMouseUpInternal(MouseButton button, POINT point)
+ {
+ Point dip_point(
+ graph::PixelToDipX(point.x),
+ graph::PixelToDipY(point.y)
+ );
+
+ const auto control = HitTest(dip_point);
+
+ DispatchEvent(control, &Control::OnMouseUpCore, nullptr, dip_point, button);
+ }
+ }
}