aboutsummaryrefslogtreecommitdiff
path: root/src/ui/window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/window.cpp')
-rw-r--r--src/ui/window.cpp1291
1 files changed, 604 insertions, 687 deletions
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index 86fa4436..91319db7 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -1,790 +1,707 @@
#include "window.hpp"
+#include <d2d1_1.h>
+#include <windowsx.h>
+
#include "application.hpp"
-#include "graph/graph.hpp"
#include "exception.hpp"
-#include "cursor.hpp"
-
-namespace cru::ui
-{
- 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.");
- }
-
- LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
- auto window = WindowManager::GetInstance()->FromHandle(hWnd);
-
- LRESULT result;
- if (window != nullptr && window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result))
- return result;
-
- return DefWindowProc(hWnd, Msg, wParam, lParam);
- }
-
- WindowManager* WindowManager::GetInstance()
- {
- return Application::GetInstance()->ResolveSingleton<WindowManager>([](auto)
- {
- return new WindowManager{};
- });
- }
-
- 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;
- }
-
- std::vector<Window*> WindowManager::GetAllWindows() const
- {
- std::vector<Window*> windows;
- for (auto [key, value] : window_map_)
- windows.push_back(value);
- return windows;
- }
+#include "graph/graph_manager.hpp"
+#include "graph/graph_util.hpp"
+#include "graph/window_render_target.hpp"
+#include "render/window_render_object.hpp"
+
+namespace cru::ui {
+namespace {
+// Dispatch the event.
+//
+// This will raise routed event of the control and its parent and parent's
+// parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
+//
+// First tunnel from top to bottom possibly stopped by "handled" flag in
+// EventArgs. Second bubble from bottom to top possibly stopped by "handled"
+// flag in EventArgs. Last direct to each control.
+//
+// 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* const original_sender,
+ events::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
+ Control* const last_receiver, Args&&... args) {
+ std::list<Control*> receive_list;
+
+ auto parent = original_sender;
+ while (parent != last_receiver) {
+ receive_list.push_back(parent);
+ parent = parent->GetParent();
+ }
+
+ auto handled = false;
+
+ // tunnel
+ for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
+ EventArgs event_args(*i, original_sender, std::forward<Args>(args)...);
+ (*i->*event_ptr)()->tunnel.Raise(event_args);
+ if (event_args.IsHandled()) {
+ handled = true;
+ break;
+ }
+ }
+
+ // bubble
+ if (!handled) {
+ for (auto i : receive_list) {
+ EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
+ (i->*event_ptr)()->bubble.Raise(event_args);
+ if (event_args.IsHandled()) break;
+ }
+ }
+
+ // direct
+ for (auto i : receive_list) {
+ EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
+ (i->*event_ptr)()->direct.Raise(event_args);
+ }
+}
- inline Point PiToDip(const POINT& pi_point)
- {
- return Point(
- graph::PixelToDipX(pi_point.x),
- graph::PixelToDipY(pi_point.y)
- );
- }
+std::list<Control*> GetAncestorList(Control* control) {
+ std::list<Control*> l;
+ while (control != nullptr) {
+ l.push_front(control);
+ control = control->GetParent();
+ }
+ return l;
+}
- 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;
- }
+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;
+ }
+}
+} // namespace
+LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam,
+ LPARAM lParam) {
+ auto window = WindowManager::GetInstance()->FromHandle(hWnd);
- namespace
- {
- Cursor::Ptr GetCursorInherit(Control* control)
- {
- while (control != nullptr)
- {
- const auto cursor = control->GetCursor();
- if (cursor != nullptr)
- return cursor;
- control = control->GetInternalParent();
- }
- return cursors::arrow;
- }
- }
+ LRESULT result;
+ if (window != nullptr &&
+ window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result))
+ return result;
- Window* Window::CreateOverlapped()
- {
- return new Window(tag_overlapped_constructor{});
- }
-
- Window* Window::CreatePopup(Window* parent, const bool caption)
- {
- return new Window(tag_popup_constructor{}, parent, caption);
- }
+ return DefWindowProc(hWnd, Msg, wParam, lParam);
+}
+WindowManager* WindowManager::GetInstance() {
+ return Application::GetInstance()->ResolveSingleton<WindowManager>(
+ [](auto) { return new WindowManager{}; });
+}
- Window::Window(tag_overlapped_constructor)
- {
- BeforeCreateHwnd();
+WindowManager::WindowManager() {
+ general_window_class_ = std::make_unique<WindowClass>(
+ L"CruUIWindowClass", GeneralWndProc,
+ Application::GetInstance()->GetInstanceHandle());
+}
- const auto window_manager = WindowManager::GetInstance();
+void WindowManager::RegisterWindow(HWND hwnd, Window* window) {
+ assert(window_map_.count(hwnd) == 0); // The hwnd is already in the map.
+ window_map_.emplace(hwnd, window);
+}
- hwnd_ = CreateWindowEx(0,
- window_manager->GetGeneralWindowClass()->GetName(),
- L"", WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- nullptr, nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr
- );
+void WindowManager::UnregisterWindow(HWND hwnd) {
+ const auto find_result = window_map_.find(hwnd);
+ assert(find_result != window_map_.end()); // The hwnd is not in the map.
+ window_map_.erase(find_result);
+ if (window_map_.empty()) Application::GetInstance()->Quit(0);
+}
- if (hwnd_ == nullptr)
- throw std::runtime_error("Failed to create window.");
+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;
+}
- AfterCreateHwnd(window_manager);
- }
+std::vector<Window*> WindowManager::GetAllWindows() const {
+ std::vector<Window*> windows;
+ for (auto [key, value] : window_map_) windows.push_back(value);
+ return windows;
+}
- Window::Window(tag_popup_constructor, Window* parent, const bool caption)
- {
- if (parent != nullptr && !parent->IsWindowValid())
- throw std::runtime_error("Parent window is not valid.");
+inline Point PiToDip(const POINT& pi_point) {
+ return Point(graph::PixelToDipX(pi_point.x), graph::PixelToDipY(pi_point.y));
+}
- BeforeCreateHwnd();
+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;
+}
- parent_window_ = parent;
+Window* Window::CreateOverlapped() {
+ return new Window(tag_overlapped_constructor{});
+}
- const auto window_manager = WindowManager::GetInstance();
+Window* Window::CreatePopup(Window* parent, const bool caption) {
+ return new Window(tag_popup_constructor{}, parent, caption);
+}
- hwnd_ = CreateWindowEx(0,
- window_manager->GetGeneralWindowClass()->GetName(),
- L"", caption ? (WS_POPUPWINDOW | WS_CAPTION) : WS_POPUP,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- parent == nullptr ? nullptr : parent->GetWindowHandle(),
- nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr
- );
+Window::Window(tag_overlapped_constructor) {
+ BeforeCreateHwnd();
- if (hwnd_ == nullptr)
- throw std::runtime_error("Failed to create window.");
+ const auto window_manager = WindowManager::GetInstance();
- AfterCreateHwnd(window_manager);
- }
+ hwnd_ =
+ CreateWindowEx(0, window_manager->GetGeneralWindowClass()->GetName(), L"",
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr,
+ Application::GetInstance()->GetInstanceHandle(), nullptr);
- void Window::BeforeCreateHwnd()
- {
- window_ = this;
- }
+ if (hwnd_ == nullptr)
+ throw Win32Error(::GetLastError(), "Failed to create window.");
- void Window::AfterCreateHwnd(WindowManager* window_manager)
- {
- window_manager->RegisterWindow(hwnd_, this);
+ AfterCreateHwnd(window_manager);
+}
- render_target_ = graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_);
+Window::Window(tag_popup_constructor, Window* parent, const bool caption) {
+ assert(parent == nullptr ||
+ parent->IsWindowValid()); // Parent window is not valid.
- SetCursor(cursors::arrow);
- }
+ BeforeCreateHwnd();
- Window::~Window() {
- if (IsWindowValid())
- {
- SetDeleteThisOnDestroy(false); // avoid double delete.
- Close();
- }
- TraverseDescendants([this](Control* control) {
- control->OnDetachToWindow(this);
- });
- }
+ parent_window_ = parent;
- StringView Window::GetControlType() const
- {
- return control_type;
- }
+ const auto window_manager = WindowManager::GetInstance();
- void Window::SetDeleteThisOnDestroy(bool value)
- {
- delete_this_on_destroy_ = value;
- }
+ hwnd_ = CreateWindowEx(
+ 0, window_manager->GetGeneralWindowClass()->GetName(), L"",
+ caption ? (WS_POPUPWINDOW | WS_CAPTION) : WS_POPUP, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr,
+ Application::GetInstance()->GetInstanceHandle(), nullptr);
- void Window::Close() {
- if (IsWindowValid())
- DestroyWindow(hwnd_);
- }
+ if (hwnd_ == nullptr)
+ throw Win32Error(::GetLastError(), "Failed to create window.");
- void Window::InvalidateDraw() {
- if (IsWindowValid()) {
- InvalidateRect(hwnd_, nullptr, false);
- }
- }
+ AfterCreateHwnd(window_manager);
+}
- void Window::Show() {
- if (IsWindowValid()) {
- ShowWindow(hwnd_, SW_SHOWNORMAL);
- }
- }
+void Window::BeforeCreateHwnd() { window_ = this; }
- void Window::Hide() {
- if (IsWindowValid()) {
- ShowWindow(hwnd_, SW_HIDE);
- }
- }
+void Window::AfterCreateHwnd(WindowManager* window_manager) {
+ window_manager->RegisterWindow(hwnd_, this);
- Size Window::GetClientSize() {
- if (!IsWindowValid())
- return Size();
+ render_target_.reset(
+ new graph::WindowRenderTarget(graph::GraphManager::GetInstance(), hwnd_));
- const auto pixel_rect = GetClientRectPixel();
- return Size(
- graph::PixelToDipX(pixel_rect.right),
- graph::PixelToDipY(pixel_rect.bottom)
- );
- }
+ render_object_.reset(new render::WindowRenderObject(this));
+}
- 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
- );
- }
- }
+Window::~Window() {
+ if (IsWindowValid()) {
+ SetDeleteThisOnDestroy(false); // avoid double delete.
+ Close();
+ }
+ TraverseDescendants(
+ [this](Control* control) { control->OnDetachToWindow(this); });
+}
- Rect Window::GetWindowRect() {
- if (!IsWindowValid())
- return Rect();
+StringView Window::GetControlType() const { return control_type; }
- RECT rect;
- ::GetWindowRect(hwnd_, &rect);
+render::RenderObject* Window::GetRenderObject() const {
+ return render_object_.get();
+}
- return Rect::FromVertices(
- graph::PixelToDipX(rect.left),
- graph::PixelToDipY(rect.top),
- graph::PixelToDipX(rect.right),
- graph::PixelToDipY(rect.bottom)
- );
- }
+void Window::SetDeleteThisOnDestroy(bool value) {
+ delete_this_on_destroy_ = value;
+}
- 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::Close() {
+ if (IsWindowValid()) DestroyWindow(hwnd_);
+}
- 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
- );
- }
- }
+void Window::InvalidateDraw() {
+ if (IsWindowValid()) {
+ InvalidateRect(hwnd_, nullptr, false);
+ }
+}
- Point Window::PointToScreen(const Point& point)
- {
- if (!IsWindowValid())
- return Point::Zero();
+void Window::Show() {
+ if (IsWindowValid()) {
+ ShowWindow(hwnd_, SW_SHOWNORMAL);
+ }
+}
- auto p = DipToPi(point);
- if (::ClientToScreen(GetWindowHandle(), &p) == 0)
- throw Win32Error(::GetLastError(), "Failed transform point from window to screen.");
- return PiToDip(p);
- }
+void Window::Hide() {
+ if (IsWindowValid()) {
+ ShowWindow(hwnd_, SW_HIDE);
+ }
+}
- Point Window::PointFromScreen(const Point& point)
- {
- if (!IsWindowValid())
- return Point::Zero();
+Size Window::GetClientSize() {
+ if (!IsWindowValid()) return Size();
- auto p = DipToPi(point);
- if (::ScreenToClient(GetWindowHandle(), &p) == 0)
- throw Win32Error(::GetLastError(), "Failed transform point from screen to window.");
- return PiToDip(p);
- }
+ const auto pixel_rect = GetClientRectPixel();
+ return Size(graph::PixelToDipX(pixel_rect.right),
+ graph::PixelToDipY(pixel_rect.bottom));
+}
- bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) {
-
- 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;
- }
-
- 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:
- {
- 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_MOUSEWHEEL:
- POINT point;
- point.x = GET_X_LPARAM(l_param);
- point.y = GET_Y_LPARAM(l_param);
- ScreenToClient(hwnd, &point);
- OnMouseWheelInternal(GET_WHEEL_DELTA_WPARAM(w_param), 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;
- }
- }
+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);
+ }
+}
- Point Window::GetMousePosition()
- {
- if (!IsWindowValid())
- return Point::Zero();
- POINT point;
- ::GetCursorPos(&point);
- ::ScreenToClient(hwnd_, &point);
- return PiToDip(point);
- }
+Rect Window::GetWindowRect() {
+ if (!IsWindowValid()) return Rect();
- Point Window::GetOffset()
- {
- return Point();
- }
+ RECT rect;
+ ::GetWindowRect(hwnd_, &rect);
- Size Window::GetSize()
- {
- return GetClientSize();
- }
+ return Rect::FromVertices(
+ graph::PixelToDipX(rect.left), graph::PixelToDipY(rect.top),
+ graph::PixelToDipX(rect.right), graph::PixelToDipY(rect.bottom));
+}
- void Window::SetRect(const Rect& size)
- {
+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::SetWindowPosition(const Point& position) {
+ if (IsWindowValid()) {
+ SetWindowPos(hwnd_, nullptr, graph::DipToPixelX(position.x),
+ graph::DipToPixelY(position.y), 0, 0,
+ SWP_NOZORDER | SWP_NOSIZE);
+ }
+}
- bool Window::IsPointInside(const Point& point)
- {
- return Rect(Point::Zero(), GetClientSize()).IsPointInside(point);
- }
+Point Window::PointToScreen(const Point& point) {
+ if (!IsWindowValid()) return Point::Zero();
- void Window::WindowInvalidateLayout()
- {
- if (is_layout_invalid_)
- return;
-
- is_layout_invalid_ = true;
- InvokeLater([this]
- {
- if (is_layout_invalid_)
- Relayout();
- });
- }
+ auto p = DipToPi(point);
+ if (::ClientToScreen(GetWindowHandle(), &p) == 0)
+ throw Win32Error(::GetLastError(),
+ "Failed transform point from window to screen.");
+ return PiToDip(p);
+}
- void Window::Relayout()
- {
- Measure(GetSize(), AdditionalMeasureInfo{});
- OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{});
- is_layout_invalid_ = false;
- }
+Point Window::PointFromScreen(const Point& point) {
+ if (!IsWindowValid()) return Point::Zero();
- void Window::SetSizeFitContent(const Size& max_size)
- {
- Measure(max_size, AdditionalMeasureInfo{});
- SetClientSize(GetDesiredSize());
- OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{});
- is_layout_invalid_ = false;
- }
+ auto p = DipToPi(point);
+ if (::ScreenToClient(GetWindowHandle(), &p) == 0)
+ throw Win32Error(::GetLastError(),
+ "Failed transform point from screen to window.");
+ return PiToDip(p);
+}
- 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::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param,
+ LPARAM l_param, LRESULT& result) {
+ 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;
+ }
+
+ 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: {
+ 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_MOUSEWHEEL:
+ POINT point;
+ point.x = GET_X_LPARAM(l_param);
+ point.y = GET_Y_LPARAM(l_param);
+ ScreenToClient(hwnd, &point);
+ OnMouseWheelInternal(GET_WHEEL_DELTA_WPARAM(w_param), 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;
+ }
+}
- if (!IsWindowValid())
- return false;
+Point Window::GetMousePosition() {
+ if (!IsWindowValid()) return Point::Zero();
+ POINT point;
+ ::GetCursorPos(&point);
+ ::ScreenToClient(hwnd_, &point);
+ return PiToDip(point);
+}
- if (!window_focus_)
- {
- focus_control_ = control;
- ::SetFocus(hwnd_);
- return true; // event dispatch will be done in window message handling function "OnSetFocusInternal".
- }
+bool Window::RequestFocusFor(Control* control) {
+ assert(control != nullptr); // The control to request focus can't be null.
+ // You can set it as the window.
- if (focus_control_ == control)
- return true;
+ if (!IsWindowValid()) return false;
- DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, false);
+ if (!window_focus_) {
+ focus_control_ = control;
+ ::SetFocus(hwnd_);
+ return true; // event dispatch will be done in window message handling
+ // function "OnSetFocusInternal".
+ }
- focus_control_ = control;
+ if (focus_control_ == control) return true;
- DispatchEvent(control, &Control::get_focus_event, nullptr, false);
+ DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, false);
- return true;
- }
+ focus_control_ = control;
- Control* Window::GetFocusControl()
- {
- return focus_control_;
- }
+ DispatchEvent(control, &Control::GainFocusEvent, nullptr, false);
- 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;
- }
- else
- {
- return ReleaseCurrentMouseCapture();
- }
- }
+ return true;
+}
- Control* Window::ReleaseCurrentMouseCapture()
- {
- 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;
- }
- }
+Control* Window::GetFocusControl() { return focus_control_; }
+
+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;
+ } else {
+ return ReleaseCurrentMouseCapture();
+ }
+}
- void Window::UpdateCursor()
- {
- if (IsWindowValid() && mouse_hover_control_ != nullptr)
- {
- SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle());
- }
- }
+Control* Window::ReleaseCurrentMouseCapture() {
+ 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;
+ }
+}
#ifdef CRU_DEBUG_LAYOUT
- void Window::SetDebugLayout(const bool value)
- {
- if (debug_layout_ != value)
- {
- debug_layout_ = value;
- InvalidateDraw();
- }
- }
+void Window::SetDebugLayout(const bool value) {
+ if (debug_layout_ != value) {
+ debug_layout_ = value;
+ InvalidateDraw();
+ }
+}
#endif
- 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;
- }
-
- void Window::SetCursorInternal(HCURSOR cursor)
- {
- if (IsWindowValid())
- {
- ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor));
- if (mouse_hover_control_ != nullptr)
- ::SetCursor(cursor);
- }
- }
-
- void Window::OnDestroyInternal() {
- WindowManager::GetInstance()->UnregisterWindow(hwnd_);
- hwnd_ = nullptr;
- if (delete_this_on_destroy_)
- InvokeLater([this]{ delete this; });
- }
+void Window::OnChildChanged(Control* old_child, Control* new_child) {
+ if (old_child) render_object_->RemoveChild(0);
+ if (new_child) render_object_->AddChild(new_child->GetRenderObject(), 0);
+}
- void Window::OnPaintInternal() {
- render_target_->SetAsTarget();
+Control* Window::HitTest(const Point& point) {
+ return render_object_->HitTest(point)->GetAttachedControl();
+}
- auto device_context = render_target_->GetD2DDeviceContext();
+RECT Window::GetClientRectPixel() {
+ RECT rect{};
+ GetClientRect(hwnd_, &rect);
+ return rect;
+}
- device_context->BeginDraw();
+bool Window::IsMessageInQueue(UINT message) {
+ MSG msg;
+ return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0;
+}
- //Clear the background.
- device_context->Clear(D2D1::ColorF(D2D1::ColorF::White));
+void Window::SetCursorInternal(HCURSOR cursor) {
+ if (IsWindowValid()) {
+ ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR,
+ reinterpret_cast<LONG_PTR>(cursor));
+ if (mouse_hover_control_ != nullptr) ::SetCursor(cursor);
+ }
+}
- Draw(device_context.Get());
+void Window::OnDestroyInternal() {
+ WindowManager::GetInstance()->UnregisterWindow(hwnd_);
+ hwnd_ = nullptr;
+ if (delete_this_on_destroy_) InvokeLater([this] { delete this; });
+}
- ThrowIfFailed(
- device_context->EndDraw(), "Failed to draw window."
- );
+void Window::OnPaintInternal() {
+ render_target_->SetAsTarget();
- render_target_->Present();
+ auto device_context =
+ render_target_->GetGraphManager()->GetD2D1DeviceContext();
+ device_context->BeginDraw();
+ // Clear the background.
+ device_context->Clear(D2D1::ColorF(D2D1::ColorF::White));
+ render_object_->Draw(device_context);
+ ThrowIfFailed(device_context->EndDraw(), "Failed to draw window.");
+ 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))
- WindowInvalidateLayout();
- }
+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)) render_object_->MeasureAndLayout();
+}
- void Window::OnSetFocusInternal()
- {
- window_focus_ = true;
- DispatchEvent(focus_control_, &Control::get_focus_event, nullptr, true);
- }
+void Window::OnSetFocusInternal() {
+ window_focus_ = true;
+ DispatchEvent(focus_control_, &Control::GainFocusEvent, nullptr, true);
+}
- void Window::OnKillFocusInternal()
- {
- window_focus_ = false;
- DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, true);
- }
+void Window::OnKillFocusInternal() {
+ window_focus_ = false;
+ DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, true);
+}
- 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_;
-
- 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;
-
- if (mouse_capture_control_) // if mouse is captured
- {
- DispatchEvent(mouse_capture_control_, &Control::mouse_move_event, nullptr, dip_point);
- }
- else
- {
- DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point);
- DispatchEvent(new_control_mouse_hover, &Control::mouse_move_event, nullptr, dip_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_;
+
+ 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;
+
+ if (mouse_capture_control_) // if mouse is captured
+ {
+ DispatchEvent(mouse_capture_control_, &Control::MouseMoveEvent, nullptr,
+ dip_point);
+ } else {
+ DispatchMouseHoverControlChangeEvent(old_control_mouse_hover,
+ new_control_mouse_hover, dip_point);
+ DispatchEvent(new_control_mouse_hover, &Control::MouseMoveEvent, nullptr,
+ dip_point);
+ }
+}
- void Window::OnMouseLeaveInternal()
- {
- DispatchEvent(mouse_hover_control_, &Control::mouse_leave_event, nullptr);
- mouse_hover_control_ = nullptr;
- }
+void Window::OnMouseLeaveInternal() {
+ DispatchEvent(mouse_hover_control_, &Control::MouseLeaveEvent, nullptr);
+ mouse_hover_control_ = nullptr;
+}
- void Window::OnMouseDownInternal(MouseButton button, POINT point)
- {
- const auto dip_point = PiToDip(point);
+void Window::OnMouseDownInternal(MouseButton button, POINT point) {
+ const auto dip_point = PiToDip(point);
- Control* control;
+ Control* control;
- if (mouse_capture_control_)
- control = mouse_capture_control_;
- else
- control = HitTest(dip_point);
+ if (mouse_capture_control_)
+ control = mouse_capture_control_;
+ else
+ control = HitTest(dip_point);
- DispatchEvent(control, &Control::mouse_down_event, nullptr, dip_point, button);
- }
+ DispatchEvent(control, &Control::MouseDownEvent, nullptr, dip_point,
+ button);
+}
- void Window::OnMouseUpInternal(MouseButton button, POINT point)
- {
- const auto dip_point = PiToDip(point);
+void Window::OnMouseUpInternal(MouseButton button, POINT point) {
+ const auto dip_point = PiToDip(point);
- Control* control;
+ Control* control;
- if (mouse_capture_control_)
- control = mouse_capture_control_;
- else
- control = HitTest(dip_point);
+ if (mouse_capture_control_)
+ control = mouse_capture_control_;
+ else
+ control = HitTest(dip_point);
- DispatchEvent(control, &Control::mouse_up_event, nullptr, dip_point, button);
- }
+ DispatchEvent(control, &Control::MouseUpEvent, nullptr, dip_point, button);
+}
- void Window::OnMouseWheelInternal(short delta, POINT point)
- {
- const auto dip_point = PiToDip(point);
+void Window::OnMouseWheelInternal(short delta, POINT point) {
+ const auto dip_point = PiToDip(point);
- Control* control;
+ Control* control;
- if (mouse_capture_control_)
- control = mouse_capture_control_;
- else
- control = HitTest(dip_point);
+ if (mouse_capture_control_)
+ control = mouse_capture_control_;
+ else
+ control = HitTest(dip_point);
- DispatchEvent(control, &Control::mouse_wheel_event, nullptr, dip_point, static_cast<float>(delta));
- }
+ DispatchEvent(control, &Control::MouseWheelEvent, nullptr, dip_point,
+ static_cast<float>(delta));
+}
- void Window::OnKeyDownInternal(int virtual_code)
- {
- DispatchEvent(focus_control_, &Control::key_down_event, nullptr, virtual_code);
- }
+void Window::OnKeyDownInternal(int virtual_code) {
+ DispatchEvent(focus_control_, &Control::KeyDownEvent, nullptr,
+ virtual_code);
+}
- void Window::OnKeyUpInternal(int virtual_code)
- {
- DispatchEvent(focus_control_, &Control::key_up_event, nullptr, virtual_code);
- }
+void Window::OnKeyUpInternal(int virtual_code) {
+ DispatchEvent(focus_control_, &Control::KeyUpEvent, nullptr, virtual_code);
+}
- void Window::OnCharInternal(wchar_t c)
- {
- DispatchEvent(focus_control_, &Control::char_event, nullptr, c);
- }
+void Window::OnCharInternal(wchar_t c) {
+ DispatchEvent(focus_control_, &Control::CharEvent, nullptr, c);
+}
- void Window::OnActivatedInternal()
- {
- events::UiEventArgs args(this, this);
- activated_event.Raise(args);
- }
+void Window::OnActivatedInternal() {
+ events::UiEventArgs args(this, this);
+ activated_event_.Raise(args);
+}
- void Window::OnDeactivatedInternal()
- {
- events::UiEventArgs args(this, this);
- deactivated_event.Raise(args);
- }
+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
- {
- 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::mouse_leave_event, lowest_common_ancestor); // dispatch mouse leave event.
- if (new_control != nullptr)
- {
- DispatchEvent(new_control, &Control::mouse_enter_event, lowest_common_ancestor, point); // dispatch mouse enter event.
- UpdateCursor();
- }
- }
- }
+void Window::DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point) {
+ 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::MouseLeaveEvent,
+ lowest_common_ancestor); // dispatch mouse leave event.
+ if (new_control != nullptr) {
+ DispatchEvent(new_control, &Control::MouseEnterEvent,
+ lowest_common_ancestor,
+ point); // dispatch mouse enter event.
+ }
+ }
}
+} // namespace cru::ui