From 06d1d0442276a05b6caad6e3468f4afb1e8ee5df Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Jun 2020 00:03:11 +0800 Subject: ... --- src/common/Logger.cpp | 65 ++++++ src/common/logger.cpp | 65 ------ src/ui/Control.cpp | 130 +++++++++++ src/ui/Helper.cpp | 15 ++ src/ui/Helper.hpp | 17 ++ src/ui/Window.cpp | 28 +++ src/ui/control.cpp | 130 ----------- src/ui/controls/Button.cpp | 73 +++++++ src/ui/controls/Container.cpp | 18 ++ src/ui/controls/button.cpp | 73 ------- src/ui/controls/container.cpp | 18 -- src/ui/helper.cpp | 15 -- src/ui/helper.hpp | 17 -- src/ui/window.cpp | 28 --- src/win/Exception.cpp | 38 ++++ src/win/String.cpp | 188 ++++++++++++++++ src/win/exception.cpp | 38 ---- src/win/graph/direct/Brush.cpp | 17 ++ src/win/graph/direct/Factory.cpp | 107 +++++++++ src/win/graph/direct/Font.cpp | 33 +++ src/win/graph/direct/Geometry.cpp | 62 ++++++ src/win/graph/direct/Painter.cpp | 104 +++++++++ src/win/graph/direct/Resource.cpp | 12 ++ src/win/graph/direct/brush.cpp | 17 -- src/win/graph/direct/factory.cpp | 107 --------- src/win/graph/direct/font.cpp | 33 --- src/win/graph/direct/geometry.cpp | 62 ------ src/win/graph/direct/painter.cpp | 104 --------- src/win/graph/direct/resource.cpp | 12 -- src/win/native/Cursor.cpp | 51 +++++ src/win/native/Keyboard.cpp | 74 +++++++ src/win/native/Timer.cpp | 28 +++ src/win/native/Timer.hpp | 34 +++ src/win/native/Window.cpp | 441 ++++++++++++++++++++++++++++++++++++++ src/win/native/cursor.cpp | 51 ----- src/win/native/keyboard.cpp | 74 ------- src/win/native/timer.cpp | 28 --- src/win/native/timer.hpp | 34 --- src/win/native/window.cpp | 441 -------------------------------------- src/win/string.cpp | 188 ---------------- 40 files changed, 1535 insertions(+), 1535 deletions(-) create mode 100644 src/common/Logger.cpp delete mode 100644 src/common/logger.cpp create mode 100644 src/ui/Control.cpp create mode 100644 src/ui/Helper.cpp create mode 100644 src/ui/Helper.hpp create mode 100644 src/ui/Window.cpp delete mode 100644 src/ui/control.cpp create mode 100644 src/ui/controls/Button.cpp create mode 100644 src/ui/controls/Container.cpp delete mode 100644 src/ui/controls/button.cpp delete mode 100644 src/ui/controls/container.cpp delete mode 100644 src/ui/helper.cpp delete mode 100644 src/ui/helper.hpp delete mode 100644 src/ui/window.cpp create mode 100644 src/win/Exception.cpp create mode 100644 src/win/String.cpp delete mode 100644 src/win/exception.cpp create mode 100644 src/win/graph/direct/Brush.cpp create mode 100644 src/win/graph/direct/Factory.cpp create mode 100644 src/win/graph/direct/Font.cpp create mode 100644 src/win/graph/direct/Geometry.cpp create mode 100644 src/win/graph/direct/Painter.cpp create mode 100644 src/win/graph/direct/Resource.cpp delete mode 100644 src/win/graph/direct/brush.cpp delete mode 100644 src/win/graph/direct/factory.cpp delete mode 100644 src/win/graph/direct/font.cpp delete mode 100644 src/win/graph/direct/geometry.cpp delete mode 100644 src/win/graph/direct/painter.cpp delete mode 100644 src/win/graph/direct/resource.cpp create mode 100644 src/win/native/Cursor.cpp create mode 100644 src/win/native/Keyboard.cpp create mode 100644 src/win/native/Timer.cpp create mode 100644 src/win/native/Timer.hpp create mode 100644 src/win/native/Window.cpp delete mode 100644 src/win/native/cursor.cpp delete mode 100644 src/win/native/keyboard.cpp delete mode 100644 src/win/native/timer.cpp delete mode 100644 src/win/native/timer.hpp delete mode 100644 src/win/native/window.cpp delete mode 100644 src/win/string.cpp (limited to 'src') diff --git a/src/common/Logger.cpp b/src/common/Logger.cpp new file mode 100644 index 00000000..ed9f9e64 --- /dev/null +++ b/src/common/Logger.cpp @@ -0,0 +1,65 @@ +#include "cru/common/Logger.hpp" + +#include +#include +#include +#include +#include + +namespace cru::log { +namespace { +Logger *CreateLogger() { + const auto logger = new Logger(); + logger->AddSource(std::make_unique()); + return logger; +} +} // namespace + +Logger *Logger::GetInstance() { + static std::unique_ptr logger{CreateLogger()}; + return logger.get(); +} + +void Logger::AddSource(std::unique_ptr source) { + sources_.push_back(std::move(source)); +} + +void Logger::RemoveSource(ILogSource *source) { + sources_.remove_if([source](const std::unique_ptr &s) { + return s.get() == source; + }); +} + +namespace { +std::string_view LogLevelToString(LogLevel level) { + switch (level) { + case LogLevel::Debug: + return "DEBUG"; + case LogLevel::Info: + return "INFO"; + case LogLevel::Warn: + return "WARN"; + case LogLevel::Error: + return "ERROR"; + default: + std::abort(); + } +} +} // namespace + +void Logger::Log(LogLevel level, const std::string_view &s) { +#ifndef CRU_DEBUG + if (level == LogLevel::Debug) { + return; + } +#endif + for (const auto &source : sources_) { + auto now = std::time(nullptr); + std::array buffer; + std::strftime(buffer.data(), 50, "%c", std::localtime(&now)); + + source->Write(level, fmt::format("[{}] {}: {}\n", buffer.data(), + LogLevelToString(level), s)); + } +} +} // namespace cru::log diff --git a/src/common/logger.cpp b/src/common/logger.cpp deleted file mode 100644 index ed9f9e64..00000000 --- a/src/common/logger.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "cru/common/Logger.hpp" - -#include -#include -#include -#include -#include - -namespace cru::log { -namespace { -Logger *CreateLogger() { - const auto logger = new Logger(); - logger->AddSource(std::make_unique()); - return logger; -} -} // namespace - -Logger *Logger::GetInstance() { - static std::unique_ptr logger{CreateLogger()}; - return logger.get(); -} - -void Logger::AddSource(std::unique_ptr source) { - sources_.push_back(std::move(source)); -} - -void Logger::RemoveSource(ILogSource *source) { - sources_.remove_if([source](const std::unique_ptr &s) { - return s.get() == source; - }); -} - -namespace { -std::string_view LogLevelToString(LogLevel level) { - switch (level) { - case LogLevel::Debug: - return "DEBUG"; - case LogLevel::Info: - return "INFO"; - case LogLevel::Warn: - return "WARN"; - case LogLevel::Error: - return "ERROR"; - default: - std::abort(); - } -} -} // namespace - -void Logger::Log(LogLevel level, const std::string_view &s) { -#ifndef CRU_DEBUG - if (level == LogLevel::Debug) { - return; - } -#endif - for (const auto &source : sources_) { - auto now = std::time(nullptr); - std::array buffer; - std::strftime(buffer.data(), 50, "%c", std::localtime(&now)); - - source->Write(level, fmt::format("[{}] {}: {}\n", buffer.data(), - LogLevelToString(level), s)); - } -} -} // namespace cru::log diff --git a/src/ui/Control.cpp b/src/ui/Control.cpp new file mode 100644 index 00000000..cd1367fe --- /dev/null +++ b/src/ui/Control.cpp @@ -0,0 +1,130 @@ +#include "cru/ui/Control.hpp" + +#include "cru/platform/native/Cursor.hpp" +#include "cru/platform/native/UiApplication.hpp" +#include "cru/ui/Base.hpp" +#include "cru/ui/UiHost.hpp" +#include "RoutedEventDispatch.hpp" + +#include + +namespace cru::ui { +using platform::native::ICursor; +using platform::native::IUiApplication; +using platform::native::SystemCursorType; + +Control::Control() { + MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = true; + this->OnMouseHoverChange(true); + }); + + MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = false; + this->OnMouseHoverChange(true); + }); +} + +void Control::_SetParent(Control* parent) { + const auto old_parent = GetParent(); + parent_ = parent; + const auto new_parent = GetParent(); + if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); +} + +void Control::_SetDescendantUiHost(UiHost* host) { + if (host == nullptr && ui_host_ == nullptr) return; + + // You can only attach or detach window. + Expects((host != nullptr && ui_host_ == nullptr) || + (host == nullptr && ui_host_ != nullptr)); + + if (host == nullptr) { + const auto old = ui_host_; + TraverseDescendants([old](Control* control) { + control->ui_host_ = nullptr; + control->OnDetachFromHost(old); + }); + } else + TraverseDescendants([host](Control* control) { + control->ui_host_ = host; + control->OnAttachToHost(host); + }); +} + +void Control::TraverseDescendants( + const std::function& predicate) { + _TraverseDescendants(this, predicate); +} + +void Control::_TraverseDescendants( + Control* control, const std::function& predicate) { + predicate(control); + for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate); +} + +bool Control::RequestFocus() { + auto host = GetUiHost(); + if (host == nullptr) return false; + + return host->RequestFocusFor(this); +} + +bool Control::HasFocus() { + auto host = GetUiHost(); + if (host == nullptr) return false; + + return host->GetFocusControl() == this; +} + +bool Control::CaptureMouse() { + auto host = GetUiHost(); + if (host == nullptr) return false; + + return host->CaptureMouseFor(this); +} + +bool Control::ReleaseMouse() { + auto host = GetUiHost(); + if (host == nullptr) return false; + + return host->CaptureMouseFor(nullptr); +} + +bool Control::IsMouseCaptured() { + auto host = GetUiHost(); + if (host == nullptr) return false; + + return host->GetMouseCaptureControl() == this; +} + +std::shared_ptr Control::GetCursor() { return cursor_; } + +std::shared_ptr Control::GetInheritedCursor() { + Control* control = this; + while (control != nullptr) { + const auto cursor = control->GetCursor(); + if (cursor != nullptr) return cursor; + control = control->GetParent(); + } + return IUiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( + SystemCursorType::Arrow); +} + +void Control::SetCursor(std::shared_ptr cursor) { + cursor_ = std::move(cursor); + const auto host = GetUiHost(); + if (host != nullptr) { + host->UpdateCursor(); + } +} + +void Control::OnParentChanged(Control* old_parent, Control* new_parent) { + CRU_UNUSED(old_parent) + CRU_UNUSED(new_parent) +} + +void Control::OnAttachToHost(UiHost* host) { CRU_UNUSED(host) } + +void Control::OnDetachFromHost(UiHost* host) { CRU_UNUSED(host) } +} // namespace cru::ui diff --git a/src/ui/Helper.cpp b/src/ui/Helper.cpp new file mode 100644 index 00000000..6f67e701 --- /dev/null +++ b/src/ui/Helper.cpp @@ -0,0 +1,15 @@ +#include "Helper.hpp" + +#include "cru/platform/graph/Factory.hpp" +#include "cru/platform/native/UiApplication.hpp" + +namespace cru::ui { +using cru::platform::graph::IGraphFactory; +using cru::platform::native::IUiApplication; + +IGraphFactory* GetGraphFactory() { + return IUiApplication::GetInstance()->GetGraphFactory(); +} + +IUiApplication* GetUiApplication() { return IUiApplication::GetInstance(); } +} // namespace cru::ui diff --git a/src/ui/Helper.hpp b/src/ui/Helper.hpp new file mode 100644 index 00000000..6923852f --- /dev/null +++ b/src/ui/Helper.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "cru/ui/Base.hpp" + +namespace cru::platform { +namespace graph { +struct IGraphFactory; +} +namespace native { +struct ICursor; +struct IUiApplication; +} // namespace native +} // namespace cru::platform + +namespace cru::ui { +cru::platform::graph::IGraphFactory* GetGraphFactory(); +cru::platform::native::IUiApplication* GetUiApplication(); +} // namespace cru::ui diff --git a/src/ui/Window.cpp b/src/ui/Window.cpp new file mode 100644 index 00000000..de7044dd --- /dev/null +++ b/src/ui/Window.cpp @@ -0,0 +1,28 @@ +#include "cru/ui/Window.hpp" + +#include "cru/ui/render/WindowRenderObject.hpp" +#include "cru/ui/UiHost.hpp" + +namespace cru::ui { +Window* Window::CreateOverlapped() { + return new Window(tag_overlapped_constructor{}); +} + +Window::Window(tag_overlapped_constructor) { + managed_ui_host_ = std::make_unique(this); +} + +Window::~Window() { + // explicit destroy ui host first. + managed_ui_host_.reset(); +} + +std::string_view Window::GetControlType() const { return control_type; } + +render::RenderObject* Window::GetRenderObject() const { return render_object_; } + +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); +} +} // namespace cru::ui diff --git a/src/ui/control.cpp b/src/ui/control.cpp deleted file mode 100644 index cd1367fe..00000000 --- a/src/ui/control.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "cru/ui/Control.hpp" - -#include "cru/platform/native/Cursor.hpp" -#include "cru/platform/native/UiApplication.hpp" -#include "cru/ui/Base.hpp" -#include "cru/ui/UiHost.hpp" -#include "RoutedEventDispatch.hpp" - -#include - -namespace cru::ui { -using platform::native::ICursor; -using platform::native::IUiApplication; -using platform::native::SystemCursorType; - -Control::Control() { - MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = true; - this->OnMouseHoverChange(true); - }); - - MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = false; - this->OnMouseHoverChange(true); - }); -} - -void Control::_SetParent(Control* parent) { - const auto old_parent = GetParent(); - parent_ = parent; - const auto new_parent = GetParent(); - if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); -} - -void Control::_SetDescendantUiHost(UiHost* host) { - if (host == nullptr && ui_host_ == nullptr) return; - - // You can only attach or detach window. - Expects((host != nullptr && ui_host_ == nullptr) || - (host == nullptr && ui_host_ != nullptr)); - - if (host == nullptr) { - const auto old = ui_host_; - TraverseDescendants([old](Control* control) { - control->ui_host_ = nullptr; - control->OnDetachFromHost(old); - }); - } else - TraverseDescendants([host](Control* control) { - control->ui_host_ = host; - control->OnAttachToHost(host); - }); -} - -void Control::TraverseDescendants( - const std::function& predicate) { - _TraverseDescendants(this, predicate); -} - -void Control::_TraverseDescendants( - Control* control, const std::function& predicate) { - predicate(control); - for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate); -} - -bool Control::RequestFocus() { - auto host = GetUiHost(); - if (host == nullptr) return false; - - return host->RequestFocusFor(this); -} - -bool Control::HasFocus() { - auto host = GetUiHost(); - if (host == nullptr) return false; - - return host->GetFocusControl() == this; -} - -bool Control::CaptureMouse() { - auto host = GetUiHost(); - if (host == nullptr) return false; - - return host->CaptureMouseFor(this); -} - -bool Control::ReleaseMouse() { - auto host = GetUiHost(); - if (host == nullptr) return false; - - return host->CaptureMouseFor(nullptr); -} - -bool Control::IsMouseCaptured() { - auto host = GetUiHost(); - if (host == nullptr) return false; - - return host->GetMouseCaptureControl() == this; -} - -std::shared_ptr Control::GetCursor() { return cursor_; } - -std::shared_ptr Control::GetInheritedCursor() { - Control* control = this; - while (control != nullptr) { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) return cursor; - control = control->GetParent(); - } - return IUiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( - SystemCursorType::Arrow); -} - -void Control::SetCursor(std::shared_ptr cursor) { - cursor_ = std::move(cursor); - const auto host = GetUiHost(); - if (host != nullptr) { - host->UpdateCursor(); - } -} - -void Control::OnParentChanged(Control* old_parent, Control* new_parent) { - CRU_UNUSED(old_parent) - CRU_UNUSED(new_parent) -} - -void Control::OnAttachToHost(UiHost* host) { CRU_UNUSED(host) } - -void Control::OnDetachFromHost(UiHost* host) { CRU_UNUSED(host) } -} // namespace cru::ui diff --git a/src/ui/controls/Button.cpp b/src/ui/controls/Button.cpp new file mode 100644 index 00000000..6f6af878 --- /dev/null +++ b/src/ui/controls/Button.cpp @@ -0,0 +1,73 @@ +#include "cru/ui/controls/Button.hpp" +#include + +#include "../Helper.hpp" +#include "cru/platform/graph/Brush.hpp" +#include "cru/platform/native/Cursor.hpp" +#include "cru/platform/native/UiApplication.hpp" +#include "cru/ui/render/BorderRenderObject.hpp" +#include "cru/ui/UiManager.hpp" +#include "cru/ui/Window.hpp" + +namespace cru::ui::controls { +using cru::platform::native::SystemCursorType; + +namespace { +void Set(render::BorderRenderObject* o, const ButtonStateStyle& s) { + o->SetBorderBrush(s.border_brush); + o->SetBorderThickness(s.border_thickness); + o->SetBorderRadius(s.border_radius); + o->SetForegroundBrush(s.foreground_brush); + o->SetBackgroundBrush(s.background_brush); +} + +std::shared_ptr GetSystemCursor( + SystemCursorType type) { + return GetUiApplication()->GetCursorManager()->GetSystemCursor(type); +} +} // namespace + +Button::Button() : click_detector_(this) { + style_ = UiManager::GetInstance()->GetThemeResources()->button_style; + + render_object_ = std::make_unique(); + render_object_->SetAttachedControl(this); + Set(render_object_.get(), style_.normal); + render_object_->SetBorderEnabled(true); + + click_detector_.StateChangeEvent()->AddHandler( + [this](const ClickState& state) { + switch (state) { + case ClickState::None: + Set(render_object_.get(), style_.normal); + SetCursor(GetSystemCursor(SystemCursorType::Arrow)); + break; + case ClickState::Hover: + Set(render_object_.get(), style_.hover); + SetCursor(GetSystemCursor(SystemCursorType::Hand)); + break; + case ClickState::Press: + Set(render_object_.get(), style_.press); + SetCursor(GetSystemCursor(SystemCursorType::Hand)); + break; + case ClickState::PressInactive: + Set(render_object_.get(), style_.press_cancel); + SetCursor(GetSystemCursor(SystemCursorType::Arrow)); + break; + } + }); +} + +Button::~Button() = default; + +render::RenderObject* Button::GetRenderObject() const { + return render_object_.get(); +} + +void Button::OnChildChanged(Control* old_child, Control* new_child) { + if (old_child != nullptr) render_object_->RemoveChild(0); + if (new_child != nullptr) + render_object_->AddChild(new_child->GetRenderObject(), 0); +} + +} // namespace cru::ui::controls diff --git a/src/ui/controls/Container.cpp b/src/ui/controls/Container.cpp new file mode 100644 index 00000000..de58ee64 --- /dev/null +++ b/src/ui/controls/Container.cpp @@ -0,0 +1,18 @@ +#include "cru/ui/controls/Container.hpp" + +#include "cru/platform/graph/Factory.hpp" +#include "cru/ui/render/BorderRenderObject.hpp" + +namespace cru::ui::controls { +Container::Container() { + render_object_ = std::make_unique(); + render_object_->SetBorderEnabled(false); +} + +Container::~Container() = default; + +void Container::OnChildChanged(Control*, Control* new_child) { + render_object_->RemoveChild(0); + render_object_->AddChild(new_child->GetRenderObject(), 0); +} +} // namespace cru::ui::controls diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp deleted file mode 100644 index 6f6af878..00000000 --- a/src/ui/controls/button.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "cru/ui/controls/Button.hpp" -#include - -#include "../Helper.hpp" -#include "cru/platform/graph/Brush.hpp" -#include "cru/platform/native/Cursor.hpp" -#include "cru/platform/native/UiApplication.hpp" -#include "cru/ui/render/BorderRenderObject.hpp" -#include "cru/ui/UiManager.hpp" -#include "cru/ui/Window.hpp" - -namespace cru::ui::controls { -using cru::platform::native::SystemCursorType; - -namespace { -void Set(render::BorderRenderObject* o, const ButtonStateStyle& s) { - o->SetBorderBrush(s.border_brush); - o->SetBorderThickness(s.border_thickness); - o->SetBorderRadius(s.border_radius); - o->SetForegroundBrush(s.foreground_brush); - o->SetBackgroundBrush(s.background_brush); -} - -std::shared_ptr GetSystemCursor( - SystemCursorType type) { - return GetUiApplication()->GetCursorManager()->GetSystemCursor(type); -} -} // namespace - -Button::Button() : click_detector_(this) { - style_ = UiManager::GetInstance()->GetThemeResources()->button_style; - - render_object_ = std::make_unique(); - render_object_->SetAttachedControl(this); - Set(render_object_.get(), style_.normal); - render_object_->SetBorderEnabled(true); - - click_detector_.StateChangeEvent()->AddHandler( - [this](const ClickState& state) { - switch (state) { - case ClickState::None: - Set(render_object_.get(), style_.normal); - SetCursor(GetSystemCursor(SystemCursorType::Arrow)); - break; - case ClickState::Hover: - Set(render_object_.get(), style_.hover); - SetCursor(GetSystemCursor(SystemCursorType::Hand)); - break; - case ClickState::Press: - Set(render_object_.get(), style_.press); - SetCursor(GetSystemCursor(SystemCursorType::Hand)); - break; - case ClickState::PressInactive: - Set(render_object_.get(), style_.press_cancel); - SetCursor(GetSystemCursor(SystemCursorType::Arrow)); - break; - } - }); -} - -Button::~Button() = default; - -render::RenderObject* Button::GetRenderObject() const { - return render_object_.get(); -} - -void Button::OnChildChanged(Control* old_child, Control* new_child) { - if (old_child != nullptr) render_object_->RemoveChild(0); - if (new_child != nullptr) - render_object_->AddChild(new_child->GetRenderObject(), 0); -} - -} // namespace cru::ui::controls diff --git a/src/ui/controls/container.cpp b/src/ui/controls/container.cpp deleted file mode 100644 index de58ee64..00000000 --- a/src/ui/controls/container.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "cru/ui/controls/Container.hpp" - -#include "cru/platform/graph/Factory.hpp" -#include "cru/ui/render/BorderRenderObject.hpp" - -namespace cru::ui::controls { -Container::Container() { - render_object_ = std::make_unique(); - render_object_->SetBorderEnabled(false); -} - -Container::~Container() = default; - -void Container::OnChildChanged(Control*, Control* new_child) { - render_object_->RemoveChild(0); - render_object_->AddChild(new_child->GetRenderObject(), 0); -} -} // namespace cru::ui::controls diff --git a/src/ui/helper.cpp b/src/ui/helper.cpp deleted file mode 100644 index 6f67e701..00000000 --- a/src/ui/helper.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "Helper.hpp" - -#include "cru/platform/graph/Factory.hpp" -#include "cru/platform/native/UiApplication.hpp" - -namespace cru::ui { -using cru::platform::graph::IGraphFactory; -using cru::platform::native::IUiApplication; - -IGraphFactory* GetGraphFactory() { - return IUiApplication::GetInstance()->GetGraphFactory(); -} - -IUiApplication* GetUiApplication() { return IUiApplication::GetInstance(); } -} // namespace cru::ui diff --git a/src/ui/helper.hpp b/src/ui/helper.hpp deleted file mode 100644 index 6923852f..00000000 --- a/src/ui/helper.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "cru/ui/Base.hpp" - -namespace cru::platform { -namespace graph { -struct IGraphFactory; -} -namespace native { -struct ICursor; -struct IUiApplication; -} // namespace native -} // namespace cru::platform - -namespace cru::ui { -cru::platform::graph::IGraphFactory* GetGraphFactory(); -cru::platform::native::IUiApplication* GetUiApplication(); -} // namespace cru::ui diff --git a/src/ui/window.cpp b/src/ui/window.cpp deleted file mode 100644 index de7044dd..00000000 --- a/src/ui/window.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "cru/ui/Window.hpp" - -#include "cru/ui/render/WindowRenderObject.hpp" -#include "cru/ui/UiHost.hpp" - -namespace cru::ui { -Window* Window::CreateOverlapped() { - return new Window(tag_overlapped_constructor{}); -} - -Window::Window(tag_overlapped_constructor) { - managed_ui_host_ = std::make_unique(this); -} - -Window::~Window() { - // explicit destroy ui host first. - managed_ui_host_.reset(); -} - -std::string_view Window::GetControlType() const { return control_type; } - -render::RenderObject* Window::GetRenderObject() const { return render_object_; } - -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); -} -} // namespace cru::ui diff --git a/src/win/Exception.cpp b/src/win/Exception.cpp new file mode 100644 index 00000000..9f9fb03d --- /dev/null +++ b/src/win/Exception.cpp @@ -0,0 +1,38 @@ +#include "cru/win/Exception.hpp" + +#include +#include + +namespace cru::platform::win { + +inline std::string HResultMakeMessage(HRESULT h_result, + std::optional message) { + if (message.has_value()) + return fmt::format(FMT_STRING("HRESULT: {:#08x}. Message: {}"), h_result, + *message); + else + return fmt::format(FMT_STRING("HRESULT: {:#08x}."), h_result); +} + +HResultError::HResultError(HRESULT h_result) + : PlatformException(HResultMakeMessage(h_result, std::nullopt)), + h_result_(h_result) {} + +HResultError::HResultError(HRESULT h_result, + const std::string_view& additional_message) + : PlatformException(HResultMakeMessage(h_result, additional_message)), + h_result_(h_result) {} + +inline std::string Win32MakeMessage(DWORD error_code, + std::string_view message) { + return fmt::format("Last error code: {:#04x}.\nMessage: {}\n", error_code, + message); +} + +Win32Error::Win32Error(const std::string_view& message) + : Win32Error(::GetLastError(), message) {} + +Win32Error::Win32Error(DWORD error_code, const std::string_view& message) + : PlatformException(Win32MakeMessage(error_code, message)), + error_code_(error_code) {} +} // namespace cru::platform::win diff --git a/src/win/String.cpp b/src/win/String.cpp new file mode 100644 index 00000000..65a280f2 --- /dev/null +++ b/src/win/String.cpp @@ -0,0 +1,188 @@ +#include "cru/win/String.hpp" + +#include "cru/win/Exception.hpp" + +#include + +namespace cru::platform::win { +std::string ToUtf8String(const std::wstring_view& string) { + if (string.empty()) return std::string{}; + + const auto length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), + static_cast(string.size()), nullptr, 0, nullptr, nullptr); + if (length == 0) { + throw Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-8."); + } + + std::string result; + result.resize(length); + if (::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), + static_cast(string.size()), result.data(), + static_cast(result.size()), nullptr, + nullptr) == 0) + throw Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-8."); + return result; +} + +std::wstring ToUtf16String(const std::string_view& string) { + if (string.empty()) return std::wstring{}; + + const auto length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), + static_cast(string.size()), nullptr, 0); + if (length == 0) { + throw Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-16."); + } + + std::wstring result; + result.resize(length); + if (::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), + static_cast(string.size()), result.data(), + static_cast(result.size())) == 0) + throw win::Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-16."); + return result; +} + +template +inline std::enable_if_t, CodePoint> ExtractBits( + UInt n) { + return static_cast(n & ((1u << number_of_bit) - 1)); +} + +CodePoint Utf8Iterator::Next() { + if (position_ == static_cast(string_.length())) return k_code_point_end; + + const auto cu0 = static_cast(string_[position_++]); + + auto read_next_folowing_code = [this]() -> CodePoint { + if (this->position_ == static_cast(string_.length())) + throw TextEncodeException( + "Unexpected end when read continuing byte of multi-byte code point."); + +#ifdef CRU_DEBUG + const auto u = static_cast(string_[position_]); + if (!(u & (1u << 7)) || (u & (1u << 6))) { + throw TextEncodeException( + "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " + "multi-byte code point."); + } +#endif + + return ExtractBits(string_[position_++]); + }; + + if ((1u << 7) & cu0) { + if ((1u << 6) & cu0) { // 2~4-length code point + if ((1u << 5) & cu0) { // 3~4-length code point + if ((1u << 4) & cu0) { // 4-length code point +#ifdef CRU_DEBUG + if (cu0 & (1u << 3)) { + throw TextEncodeException( + "Unexpected bad-format begin byte (not 0b10xxxxxx) of 4-byte " + "code point."); + } +#endif + + const CodePoint s0 = ExtractBits(cu0) << (6 * 3); + const CodePoint s1 = read_next_folowing_code() << (6 * 2); + const CodePoint s2 = read_next_folowing_code() << 6; + const CodePoint s3 = read_next_folowing_code(); + return s0 + s1 + s2 + s3; + } else { // 3-length code point + const CodePoint s0 = ExtractBits(cu0) << (6 * 2); + const CodePoint s1 = read_next_folowing_code() << 6; + const CodePoint s2 = read_next_folowing_code(); + return s0 + s1 + s2; + } + } else { // 2-length code point + const CodePoint s0 = ExtractBits(cu0) << 6; + const CodePoint s1 = read_next_folowing_code(); + return s0 + s1; + } + } else { + throw TextEncodeException( + "Unexpected bad-format (0b10xxxxxx) begin byte of a code point."); + } + } else { + return static_cast(cu0); + } +} + +CodePoint Utf16Iterator::Next() { + if (position_ == static_cast(string_.length())) return k_code_point_end; + + const auto cu0 = static_cast(string_[position_++]); + + if (cu0 < 0xd800u || cu0 >= 0xe000u) { // 1-length code point + return static_cast(cu0); + } else if (cu0 <= 0xdbffu) { // 2-length code point + if (position_ == static_cast(string_.length())) { + throw TextEncodeException( + "Unexpected end when reading second code unit of surrogate pair."); + } + const auto cu1 = static_cast(string_[position_++]); + +#ifdef CRU_DEBUG + if (cu1 < 0xDC00u || cu1 > 0xdfffu) { + throw TextEncodeException( + "Unexpected bad-format second code unit of surrogate pair."); + } +#endif + + const auto s0 = ExtractBits(cu0) << 10; + const auto s1 = ExtractBits(cu1); + + return s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-format first code unit of surrogate pair."); + } +} + +int IndexUtf8ToUtf16(const std::string_view& utf8_string, int utf8_index, + const std::wstring_view& utf16_string) { + if (utf8_index >= static_cast(utf8_string.length())) + return static_cast(utf16_string.length()); + + int cp_index = 0; + Utf8Iterator iter{utf8_string}; + while (iter.CurrentPosition() <= utf8_index) { + iter.Next(); + cp_index++; + } + + Utf16Iterator result_iter{utf16_string}; + for (int i = 0; i < cp_index - 1; i++) { + if (result_iter.Next() == k_code_point_end) break; + } + + return result_iter.CurrentPosition(); +} + +int IndexUtf16ToUtf8(const std::wstring_view& utf16_string, int utf16_index, + const std::string_view& utf8_string) { + if (utf16_index >= static_cast(utf16_string.length())) + return static_cast(utf8_string.length()); + + int cp_index = 0; + Utf16Iterator iter{utf16_string}; + while (iter.CurrentPosition() <= utf16_index) { + iter.Next(); + cp_index++; + } + + Utf8Iterator result_iter{utf8_string}; + for (int i = 0; i < cp_index - 1; i++) { + if (result_iter.Next() == k_code_point_end) break; + } + + return result_iter.CurrentPosition(); +} + +} // namespace cru::platform::win diff --git a/src/win/exception.cpp b/src/win/exception.cpp deleted file mode 100644 index 9f9fb03d..00000000 --- a/src/win/exception.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "cru/win/Exception.hpp" - -#include -#include - -namespace cru::platform::win { - -inline std::string HResultMakeMessage(HRESULT h_result, - std::optional message) { - if (message.has_value()) - return fmt::format(FMT_STRING("HRESULT: {:#08x}. Message: {}"), h_result, - *message); - else - return fmt::format(FMT_STRING("HRESULT: {:#08x}."), h_result); -} - -HResultError::HResultError(HRESULT h_result) - : PlatformException(HResultMakeMessage(h_result, std::nullopt)), - h_result_(h_result) {} - -HResultError::HResultError(HRESULT h_result, - const std::string_view& additional_message) - : PlatformException(HResultMakeMessage(h_result, additional_message)), - h_result_(h_result) {} - -inline std::string Win32MakeMessage(DWORD error_code, - std::string_view message) { - return fmt::format("Last error code: {:#04x}.\nMessage: {}\n", error_code, - message); -} - -Win32Error::Win32Error(const std::string_view& message) - : Win32Error(::GetLastError(), message) {} - -Win32Error::Win32Error(DWORD error_code, const std::string_view& message) - : PlatformException(Win32MakeMessage(error_code, message)), - error_code_(error_code) {} -} // namespace cru::platform::win diff --git a/src/win/graph/direct/Brush.cpp b/src/win/graph/direct/Brush.cpp new file mode 100644 index 00000000..2b9d4ac3 --- /dev/null +++ b/src/win/graph/direct/Brush.cpp @@ -0,0 +1,17 @@ +#include "cru/win/graph/direct/Brush.hpp" + +#include "cru/win/graph/direct/ConvertUtil.hpp" +#include "cru/win/graph/direct/Exception.hpp" +#include "cru/win/graph/direct/Factory.hpp" + +namespace cru::platform::graph::win::direct { +D2DSolidColorBrush::D2DSolidColorBrush(DirectGraphFactory* factory) + : DirectGraphResource(factory) { + ThrowIfFailed(factory->GetDefaultD2D1DeviceContext()->CreateSolidColorBrush( + Convert(color_), &brush_)); +} + +void D2DSolidColorBrush::SetColor(const Color& color) { + brush_->SetColor(Convert(color)); +} +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/Factory.cpp b/src/win/graph/direct/Factory.cpp new file mode 100644 index 00000000..d9213994 --- /dev/null +++ b/src/win/graph/direct/Factory.cpp @@ -0,0 +1,107 @@ +#include "cru/win/graph/direct/Factory.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/win/graph/direct/Brush.hpp" +#include "cru/win/graph/direct/Exception.hpp" +#include "cru/win/graph/direct/Font.hpp" +#include "cru/win/graph/direct/Geometry.hpp" +#include "cru/win/graph/direct/TextLayout.hpp" + +#include +#include + +namespace cru::platform::graph::win::direct { +namespace { +void InitializeCom() { + const auto hresult = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(hresult)) { + throw HResultError(hresult, "Failed to call CoInitializeEx."); + } + if (hresult == S_FALSE) { + log::Debug( + "Try to call CoInitializeEx, but it seems COM is already " + "initialized."); + } +} + +void UninitializeCom() { ::CoUninitialize(); } +} // namespace + +DirectGraphFactory::DirectGraphFactory() { + // TODO! Detect repeated creation. Because I don't think we can create two d2d + // and dwrite factory so we need to prevent the "probably dangerous" behavior. + + InitializeCom(); + + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + +#ifdef CRU_DEBUG + creation_flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + const D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + + Microsoft::WRL::ComPtr d3d11_device_context; + + ThrowIfFailed(D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, + feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, + &d3d11_device_, nullptr, &d3d11_device_context)); + + Microsoft::WRL::ComPtr dxgi_device; + ThrowIfFailed(d3d11_device_->QueryInterface(dxgi_device.GetAddressOf())); + + ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + IID_PPV_ARGS(&d2d1_factory_))); + + ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); + + d2d1_device_context_ = CreateD2D1DeviceContext(); + + // Identify the physical adapter (GPU or card) this device is runs on. + Microsoft::WRL::ComPtr dxgi_adapter; + ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); + + // Get the factory object that created the DXGI device. + ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); + + ThrowIfFailed(DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast(dwrite_factory_.GetAddressOf()))); + + ThrowIfFailed(dwrite_factory_->GetSystemFontCollection( + &dwrite_system_font_collection_)); +} + +DirectGraphFactory::~DirectGraphFactory() { UninitializeCom(); } + +Microsoft::WRL::ComPtr +DirectGraphFactory::CreateD2D1DeviceContext() { + Microsoft::WRL::ComPtr d2d1_device_context; + ThrowIfFailed(d2d1_device_->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context)); + return d2d1_device_context; +} + +std::unique_ptr DirectGraphFactory::CreateSolidColorBrush() { + return std::make_unique(this); +} + +std::unique_ptr DirectGraphFactory::CreateGeometryBuilder() { + return std::make_unique(this); +} + +std::unique_ptr DirectGraphFactory::CreateFont( + const std::string_view& font_family, float font_size) { + return std::make_unique(this, font_family, font_size); +} + +std::unique_ptr DirectGraphFactory::CreateTextLayout( + std::shared_ptr font, std::string text) { + return std::make_unique(this, std::move(font), + std::move(text)); +} +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/Font.cpp b/src/win/graph/direct/Font.cpp new file mode 100644 index 00000000..edbbc59d --- /dev/null +++ b/src/win/graph/direct/Font.cpp @@ -0,0 +1,33 @@ +#include "cru/win/graph/direct/Font.hpp" + +#include "cru/win/graph/direct/Exception.hpp" +#include "cru/win/graph/direct/Factory.hpp" +#include "cru/win/String.hpp" + +#include +#include + +namespace cru::platform::graph::win::direct { +DWriteFont::DWriteFont(DirectGraphFactory* factory, + const std::string_view& font_family, float font_size) + : DirectGraphResource(factory) { + // Get locale + std::array buffer; + if (!::GetUserDefaultLocaleName(buffer.data(), + static_cast(buffer.size()))) + throw platform::win::Win32Error( + ::GetLastError(), "Failed to get locale when create dwrite font."); + + const std::wstring&& wff = cru::platform::win::ToUtf16String(font_family); + + ThrowIfFailed(factory->GetDWriteFactory()->CreateTextFormat( + wff.data(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, font_size, buffer.data(), &text_format_)); + + ThrowIfFailed(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); + ThrowIfFailed( + text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR)); +} + +float DWriteFont::GetFontSize() { return text_format_->GetFontSize(); } +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/Geometry.cpp b/src/win/graph/direct/Geometry.cpp new file mode 100644 index 00000000..e77b4749 --- /dev/null +++ b/src/win/graph/direct/Geometry.cpp @@ -0,0 +1,62 @@ +#include "cru/win/graph/direct/Geometry.hpp" + +#include "cru/win/graph/direct/ConvertUtil.hpp" +#include "cru/win/graph/direct/Exception.hpp" +#include "cru/win/graph/direct/Factory.hpp" + +namespace cru::platform::graph::win::direct { +D2DGeometryBuilder::D2DGeometryBuilder(DirectGraphFactory* factory) + : DirectGraphResource(factory) { + ThrowIfFailed(factory->GetD2D1Factory()->CreatePathGeometry(&geometry_)); + ThrowIfFailed(geometry_->Open(&geometry_sink_)); +} + +void D2DGeometryBuilder::CheckValidation() { + if (!IsValid()) + throw ReuseException("The geometry builder is already disposed."); +} + +void D2DGeometryBuilder::BeginFigure(const Point& point) { + CheckValidation(); + geometry_sink_->BeginFigure(Convert(point), D2D1_FIGURE_BEGIN_FILLED); +} + +void D2DGeometryBuilder::LineTo(const Point& point) { + CheckValidation(); + geometry_sink_->AddLine(Convert(point)); +} + +void D2DGeometryBuilder::QuadraticBezierTo(const Point& control_point, + const Point& end_point) { + CheckValidation(); + geometry_sink_->AddQuadraticBezier( + D2D1::QuadraticBezierSegment(Convert(control_point), Convert(end_point))); +} + +void D2DGeometryBuilder::CloseFigure(bool close) { + CheckValidation(); + geometry_sink_->EndFigure(close ? D2D1_FIGURE_END_CLOSED + : D2D1_FIGURE_END_OPEN); +} + +std::unique_ptr D2DGeometryBuilder::Build() { + CheckValidation(); + ThrowIfFailed(geometry_sink_->Close()); + geometry_sink_ = nullptr; + auto geometry = + std::make_unique(GetDirectFactory(), std::move(geometry_)); + geometry_ = nullptr; + return geometry; +} + +D2DGeometry::D2DGeometry(DirectGraphFactory* factory, + Microsoft::WRL::ComPtr geometry) + : DirectGraphResource(factory), geometry_(std::move(geometry)) {} + +bool D2DGeometry::FillContains(const Point& point) { + BOOL result; + ThrowIfFailed(geometry_->FillContainsPoint( + Convert(point), D2D1::Matrix3x2F::Identity(), &result)); + return result != 0; +} +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/Painter.cpp b/src/win/graph/direct/Painter.cpp new file mode 100644 index 00000000..3ffb5208 --- /dev/null +++ b/src/win/graph/direct/Painter.cpp @@ -0,0 +1,104 @@ +#include "cru/win/graph/direct/Painter.hpp" + +#include "cru/platform/Check.hpp" +#include "cru/win/graph/direct/Brush.hpp" +#include "cru/win/graph/direct/ConvertUtil.hpp" +#include "cru/win/graph/direct/Exception.hpp" +#include "cru/win/graph/direct/Geometry.hpp" +#include "cru/win/graph/direct/TextLayout.hpp" + +#include + +namespace cru::platform::graph::win::direct { +D2DPainter::D2DPainter(ID2D1RenderTarget* render_target) { + Expects(render_target); + render_target_ = render_target; +} + +platform::Matrix D2DPainter::GetTransform() { + CheckValidation(); + D2D1_MATRIX_3X2_F m; + render_target_->GetTransform(&m); + return Convert(m); +} + +void D2DPainter::SetTransform(const platform::Matrix& matrix) { + CheckValidation(); + render_target_->SetTransform(Convert(matrix)); +} + +void D2DPainter::Clear(const Color& color) { + CheckValidation(); + render_target_->Clear(Convert(color)); +} + +void D2DPainter::StrokeRectangle(const Rect& rectangle, IBrush* brush, + float width) { + CheckValidation(); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->DrawRectangle(Convert(rectangle), b->GetD2DBrushInterface(), + width); +} + +void D2DPainter::FillRectangle(const Rect& rectangle, IBrush* brush) { + CheckValidation(); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->FillRectangle(Convert(rectangle), b->GetD2DBrushInterface()); +} + +void D2DPainter::StrokeGeometry(IGeometry* geometry, IBrush* brush, + float width) { + CheckValidation(); + const auto g = CheckPlatform(geometry, GetPlatformId()); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->DrawGeometry(g->GetComInterface(), b->GetD2DBrushInterface(), + width); +} + +void D2DPainter::FillGeometry(IGeometry* geometry, IBrush* brush) { + CheckValidation(); + const auto g = CheckPlatform(geometry, GetPlatformId()); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->FillGeometry(g->GetComInterface(), b->GetD2DBrushInterface()); +} + +void D2DPainter::DrawText(const Point& offset, ITextLayout* text_layout, + IBrush* brush) { + CheckValidation(); + const auto t = CheckPlatform(text_layout, GetPlatformId()); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->DrawTextLayout(Convert(offset), t->GetComInterface(), + b->GetD2DBrushInterface()); +} + +void D2DPainter::PushLayer(const Rect& bounds) { + CheckValidation(); + + Microsoft::WRL::ComPtr layer; + ThrowIfFailed(render_target_->CreateLayer(&layer)); + + render_target_->PushLayer(D2D1::LayerParameters(Convert(bounds)), + layer.Get()); + + layers_.push_back(std::move(layer)); +} + +void D2DPainter::PopLayer() { + render_target_->PopLayer(); + layers_.pop_back(); +} + +void D2DPainter::EndDraw() { + if (is_drawing_) { + is_drawing_ = false; + DoEndDraw(); + } +} + +void D2DPainter::CheckValidation() { + if (!is_drawing_) { + throw cru::platform::ReuseException( + "Can't do that on painter after end drawing."); + } +} +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/Resource.cpp b/src/win/graph/direct/Resource.cpp new file mode 100644 index 00000000..772bb74b --- /dev/null +++ b/src/win/graph/direct/Resource.cpp @@ -0,0 +1,12 @@ +#include "cru/win/graph/direct/Resource.hpp" + +#include "cru/win/graph/direct/Factory.hpp" + +namespace cru::platform::graph::win::direct { +DirectGraphResource::DirectGraphResource(DirectGraphFactory* factory) + : factory_(factory) { + Expects(factory); +} + +IGraphFactory* DirectGraphResource::GetGraphFactory() { return factory_; } +} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/brush.cpp b/src/win/graph/direct/brush.cpp deleted file mode 100644 index 2b9d4ac3..00000000 --- a/src/win/graph/direct/brush.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "cru/win/graph/direct/Brush.hpp" - -#include "cru/win/graph/direct/ConvertUtil.hpp" -#include "cru/win/graph/direct/Exception.hpp" -#include "cru/win/graph/direct/Factory.hpp" - -namespace cru::platform::graph::win::direct { -D2DSolidColorBrush::D2DSolidColorBrush(DirectGraphFactory* factory) - : DirectGraphResource(factory) { - ThrowIfFailed(factory->GetDefaultD2D1DeviceContext()->CreateSolidColorBrush( - Convert(color_), &brush_)); -} - -void D2DSolidColorBrush::SetColor(const Color& color) { - brush_->SetColor(Convert(color)); -} -} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/factory.cpp b/src/win/graph/direct/factory.cpp deleted file mode 100644 index d9213994..00000000 --- a/src/win/graph/direct/factory.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "cru/win/graph/direct/Factory.hpp" - -#include "cru/common/Logger.hpp" -#include "cru/win/graph/direct/Brush.hpp" -#include "cru/win/graph/direct/Exception.hpp" -#include "cru/win/graph/direct/Font.hpp" -#include "cru/win/graph/direct/Geometry.hpp" -#include "cru/win/graph/direct/TextLayout.hpp" - -#include -#include - -namespace cru::platform::graph::win::direct { -namespace { -void InitializeCom() { - const auto hresult = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - if (FAILED(hresult)) { - throw HResultError(hresult, "Failed to call CoInitializeEx."); - } - if (hresult == S_FALSE) { - log::Debug( - "Try to call CoInitializeEx, but it seems COM is already " - "initialized."); - } -} - -void UninitializeCom() { ::CoUninitialize(); } -} // namespace - -DirectGraphFactory::DirectGraphFactory() { - // TODO! Detect repeated creation. Because I don't think we can create two d2d - // and dwrite factory so we need to prevent the "probably dangerous" behavior. - - InitializeCom(); - - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - -#ifdef CRU_DEBUG - creation_flags |= D3D11_CREATE_DEVICE_DEBUG; -#endif - - const D3D_FEATURE_LEVEL feature_levels[] = { - D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1}; - - Microsoft::WRL::ComPtr d3d11_device_context; - - ThrowIfFailed(D3D11CreateDevice( - nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, - feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, - &d3d11_device_, nullptr, &d3d11_device_context)); - - Microsoft::WRL::ComPtr dxgi_device; - ThrowIfFailed(d3d11_device_->QueryInterface(dxgi_device.GetAddressOf())); - - ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, - IID_PPV_ARGS(&d2d1_factory_))); - - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); - - d2d1_device_context_ = CreateD2D1DeviceContext(); - - // Identify the physical adapter (GPU or card) this device is runs on. - Microsoft::WRL::ComPtr dxgi_adapter; - ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); - - // Get the factory object that created the DXGI device. - ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); - - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), - reinterpret_cast(dwrite_factory_.GetAddressOf()))); - - ThrowIfFailed(dwrite_factory_->GetSystemFontCollection( - &dwrite_system_font_collection_)); -} - -DirectGraphFactory::~DirectGraphFactory() { UninitializeCom(); } - -Microsoft::WRL::ComPtr -DirectGraphFactory::CreateD2D1DeviceContext() { - Microsoft::WRL::ComPtr d2d1_device_context; - ThrowIfFailed(d2d1_device_->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context)); - return d2d1_device_context; -} - -std::unique_ptr DirectGraphFactory::CreateSolidColorBrush() { - return std::make_unique(this); -} - -std::unique_ptr DirectGraphFactory::CreateGeometryBuilder() { - return std::make_unique(this); -} - -std::unique_ptr DirectGraphFactory::CreateFont( - const std::string_view& font_family, float font_size) { - return std::make_unique(this, font_family, font_size); -} - -std::unique_ptr DirectGraphFactory::CreateTextLayout( - std::shared_ptr font, std::string text) { - return std::make_unique(this, std::move(font), - std::move(text)); -} -} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/font.cpp b/src/win/graph/direct/font.cpp deleted file mode 100644 index edbbc59d..00000000 --- a/src/win/graph/direct/font.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "cru/win/graph/direct/Font.hpp" - -#include "cru/win/graph/direct/Exception.hpp" -#include "cru/win/graph/direct/Factory.hpp" -#include "cru/win/String.hpp" - -#include -#include - -namespace cru::platform::graph::win::direct { -DWriteFont::DWriteFont(DirectGraphFactory* factory, - const std::string_view& font_family, float font_size) - : DirectGraphResource(factory) { - // Get locale - std::array buffer; - if (!::GetUserDefaultLocaleName(buffer.data(), - static_cast(buffer.size()))) - throw platform::win::Win32Error( - ::GetLastError(), "Failed to get locale when create dwrite font."); - - const std::wstring&& wff = cru::platform::win::ToUtf16String(font_family); - - ThrowIfFailed(factory->GetDWriteFactory()->CreateTextFormat( - wff.data(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, font_size, buffer.data(), &text_format_)); - - ThrowIfFailed(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); - ThrowIfFailed( - text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR)); -} - -float DWriteFont::GetFontSize() { return text_format_->GetFontSize(); } -} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/geometry.cpp b/src/win/graph/direct/geometry.cpp deleted file mode 100644 index e77b4749..00000000 --- a/src/win/graph/direct/geometry.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "cru/win/graph/direct/Geometry.hpp" - -#include "cru/win/graph/direct/ConvertUtil.hpp" -#include "cru/win/graph/direct/Exception.hpp" -#include "cru/win/graph/direct/Factory.hpp" - -namespace cru::platform::graph::win::direct { -D2DGeometryBuilder::D2DGeometryBuilder(DirectGraphFactory* factory) - : DirectGraphResource(factory) { - ThrowIfFailed(factory->GetD2D1Factory()->CreatePathGeometry(&geometry_)); - ThrowIfFailed(geometry_->Open(&geometry_sink_)); -} - -void D2DGeometryBuilder::CheckValidation() { - if (!IsValid()) - throw ReuseException("The geometry builder is already disposed."); -} - -void D2DGeometryBuilder::BeginFigure(const Point& point) { - CheckValidation(); - geometry_sink_->BeginFigure(Convert(point), D2D1_FIGURE_BEGIN_FILLED); -} - -void D2DGeometryBuilder::LineTo(const Point& point) { - CheckValidation(); - geometry_sink_->AddLine(Convert(point)); -} - -void D2DGeometryBuilder::QuadraticBezierTo(const Point& control_point, - const Point& end_point) { - CheckValidation(); - geometry_sink_->AddQuadraticBezier( - D2D1::QuadraticBezierSegment(Convert(control_point), Convert(end_point))); -} - -void D2DGeometryBuilder::CloseFigure(bool close) { - CheckValidation(); - geometry_sink_->EndFigure(close ? D2D1_FIGURE_END_CLOSED - : D2D1_FIGURE_END_OPEN); -} - -std::unique_ptr D2DGeometryBuilder::Build() { - CheckValidation(); - ThrowIfFailed(geometry_sink_->Close()); - geometry_sink_ = nullptr; - auto geometry = - std::make_unique(GetDirectFactory(), std::move(geometry_)); - geometry_ = nullptr; - return geometry; -} - -D2DGeometry::D2DGeometry(DirectGraphFactory* factory, - Microsoft::WRL::ComPtr geometry) - : DirectGraphResource(factory), geometry_(std::move(geometry)) {} - -bool D2DGeometry::FillContains(const Point& point) { - BOOL result; - ThrowIfFailed(geometry_->FillContainsPoint( - Convert(point), D2D1::Matrix3x2F::Identity(), &result)); - return result != 0; -} -} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/painter.cpp b/src/win/graph/direct/painter.cpp deleted file mode 100644 index 3ffb5208..00000000 --- a/src/win/graph/direct/painter.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "cru/win/graph/direct/Painter.hpp" - -#include "cru/platform/Check.hpp" -#include "cru/win/graph/direct/Brush.hpp" -#include "cru/win/graph/direct/ConvertUtil.hpp" -#include "cru/win/graph/direct/Exception.hpp" -#include "cru/win/graph/direct/Geometry.hpp" -#include "cru/win/graph/direct/TextLayout.hpp" - -#include - -namespace cru::platform::graph::win::direct { -D2DPainter::D2DPainter(ID2D1RenderTarget* render_target) { - Expects(render_target); - render_target_ = render_target; -} - -platform::Matrix D2DPainter::GetTransform() { - CheckValidation(); - D2D1_MATRIX_3X2_F m; - render_target_->GetTransform(&m); - return Convert(m); -} - -void D2DPainter::SetTransform(const platform::Matrix& matrix) { - CheckValidation(); - render_target_->SetTransform(Convert(matrix)); -} - -void D2DPainter::Clear(const Color& color) { - CheckValidation(); - render_target_->Clear(Convert(color)); -} - -void D2DPainter::StrokeRectangle(const Rect& rectangle, IBrush* brush, - float width) { - CheckValidation(); - const auto b = CheckPlatform(brush, GetPlatformId()); - render_target_->DrawRectangle(Convert(rectangle), b->GetD2DBrushInterface(), - width); -} - -void D2DPainter::FillRectangle(const Rect& rectangle, IBrush* brush) { - CheckValidation(); - const auto b = CheckPlatform(brush, GetPlatformId()); - render_target_->FillRectangle(Convert(rectangle), b->GetD2DBrushInterface()); -} - -void D2DPainter::StrokeGeometry(IGeometry* geometry, IBrush* brush, - float width) { - CheckValidation(); - const auto g = CheckPlatform(geometry, GetPlatformId()); - const auto b = CheckPlatform(brush, GetPlatformId()); - render_target_->DrawGeometry(g->GetComInterface(), b->GetD2DBrushInterface(), - width); -} - -void D2DPainter::FillGeometry(IGeometry* geometry, IBrush* brush) { - CheckValidation(); - const auto g = CheckPlatform(geometry, GetPlatformId()); - const auto b = CheckPlatform(brush, GetPlatformId()); - render_target_->FillGeometry(g->GetComInterface(), b->GetD2DBrushInterface()); -} - -void D2DPainter::DrawText(const Point& offset, ITextLayout* text_layout, - IBrush* brush) { - CheckValidation(); - const auto t = CheckPlatform(text_layout, GetPlatformId()); - const auto b = CheckPlatform(brush, GetPlatformId()); - render_target_->DrawTextLayout(Convert(offset), t->GetComInterface(), - b->GetD2DBrushInterface()); -} - -void D2DPainter::PushLayer(const Rect& bounds) { - CheckValidation(); - - Microsoft::WRL::ComPtr layer; - ThrowIfFailed(render_target_->CreateLayer(&layer)); - - render_target_->PushLayer(D2D1::LayerParameters(Convert(bounds)), - layer.Get()); - - layers_.push_back(std::move(layer)); -} - -void D2DPainter::PopLayer() { - render_target_->PopLayer(); - layers_.pop_back(); -} - -void D2DPainter::EndDraw() { - if (is_drawing_) { - is_drawing_ = false; - DoEndDraw(); - } -} - -void D2DPainter::CheckValidation() { - if (!is_drawing_) { - throw cru::platform::ReuseException( - "Can't do that on painter after end drawing."); - } -} -} // namespace cru::platform::graph::win::direct diff --git a/src/win/graph/direct/resource.cpp b/src/win/graph/direct/resource.cpp deleted file mode 100644 index 772bb74b..00000000 --- a/src/win/graph/direct/resource.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "cru/win/graph/direct/Resource.hpp" - -#include "cru/win/graph/direct/Factory.hpp" - -namespace cru::platform::graph::win::direct { -DirectGraphResource::DirectGraphResource(DirectGraphFactory* factory) - : factory_(factory) { - Expects(factory); -} - -IGraphFactory* DirectGraphResource::GetGraphFactory() { return factory_; } -} // namespace cru::platform::graph::win::direct diff --git a/src/win/native/Cursor.cpp b/src/win/native/Cursor.cpp new file mode 100644 index 00000000..ca8bb1cd --- /dev/null +++ b/src/win/native/Cursor.cpp @@ -0,0 +1,51 @@ +#include "cru/win/native/Cursor.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/win/native/Exception.hpp" + +#include + +namespace cru::platform::native::win { +WinCursor::WinCursor(HCURSOR handle, bool auto_destroy) { + handle_ = handle; + auto_destroy_ = auto_destroy; +} + +WinCursor::~WinCursor() { + if (auto_destroy_) { + if (!::DestroyCursor(handle_)) { + // This is not a fetal error but might still need notice because it may + // cause leak. + log::Warn("Failed to destroy a cursor. Last error code: {}", + ::GetLastError()); + } + } +} + +namespace { +WinCursor* LoadWinCursor(const wchar_t* name) { + const auto handle = static_cast(::LoadImageW( + NULL, name, IMAGE_CURSOR, SM_CYCURSOR, SM_CYCURSOR, LR_SHARED)); + if (handle == NULL) { + throw Win32Error(::GetLastError(), "Failed to load system cursor."); + } + return new WinCursor(handle, false); +} +} // namespace + +WinCursorManager::WinCursorManager() + : sys_arrow_(LoadWinCursor(IDC_ARROW)), + sys_hand_(LoadWinCursor(IDC_HAND)) {} + +std::shared_ptr WinCursorManager::GetSystemWinCursor( + SystemCursorType type) { + switch (type) { + case SystemCursorType::Arrow: + return sys_arrow_; + case SystemCursorType::Hand: + return sys_hand_; + default: + throw std::runtime_error("Unknown system cursor value."); + } +} +} // namespace cru::platform::native::win diff --git a/src/win/native/Keyboard.cpp b/src/win/native/Keyboard.cpp new file mode 100644 index 00000000..aa22e4a4 --- /dev/null +++ b/src/win/native/Keyboard.cpp @@ -0,0 +1,74 @@ +#include "cru/win/native/Keyboard.hpp" + +namespace cru::platform::native::win { +KeyCode VirtualKeyToKeyCode(int virtual_key) { + if (virtual_key >= 0x30 && virtual_key <= 0x39) { + return KeyCode{static_cast(KeyCode::N0) + (virtual_key - 0x30)}; + } else if (virtual_key >= 0x41 && virtual_key <= 0x5a) { + return KeyCode{static_cast(KeyCode::A) + (virtual_key - 0x41)}; + } else if (virtual_key >= VK_NUMPAD0 && virtual_key <= VK_NUMPAD9) { + return KeyCode{static_cast(KeyCode::NumPad0) + + (virtual_key - VK_NUMPAD0)}; + } else if (virtual_key >= VK_F1 && virtual_key <= VK_F12) { + return KeyCode{static_cast(KeyCode::F1) + (virtual_key - VK_F1)}; + } else { + switch (virtual_key) { +#define CRU_MAP_KEY(virtual_key, keycode) \ + case virtual_key: \ + return KeyCode::keycode; + + CRU_MAP_KEY(VK_LBUTTON, LeftButton) + CRU_MAP_KEY(VK_MBUTTON, MiddleButton) + CRU_MAP_KEY(VK_RBUTTON, RightButton) + CRU_MAP_KEY(VK_ESCAPE, Escape) + CRU_MAP_KEY(VK_OEM_3, GraveAccent) + CRU_MAP_KEY(VK_TAB, Tab) + CRU_MAP_KEY(VK_CAPITAL, CapsLock) + CRU_MAP_KEY(VK_LSHIFT, LeftShift) + CRU_MAP_KEY(VK_LCONTROL, LeftCtrl) + CRU_MAP_KEY(VK_LWIN, LeftSuper) + CRU_MAP_KEY(VK_LMENU, LeftAlt) + CRU_MAP_KEY(VK_OEM_MINUS, Minus) + CRU_MAP_KEY(VK_OEM_PLUS, Equal) + CRU_MAP_KEY(VK_BACK, Backspace) + CRU_MAP_KEY(VK_OEM_4, LeftSquareBracket) + CRU_MAP_KEY(VK_OEM_6, RightSquareBracket) + CRU_MAP_KEY(VK_OEM_5, BackSlash) + CRU_MAP_KEY(VK_OEM_1, Semicolon) + CRU_MAP_KEY(VK_OEM_7, Quote) + CRU_MAP_KEY(VK_OEM_COMMA, Comma) + CRU_MAP_KEY(VK_OEM_PERIOD, Period) + CRU_MAP_KEY(VK_OEM_2, Slash) + CRU_MAP_KEY(VK_RSHIFT, RightShift) + CRU_MAP_KEY(VK_RCONTROL, RightCtrl) + CRU_MAP_KEY(VK_RWIN, RightSuper) + CRU_MAP_KEY(VK_RMENU, RightAlt) + CRU_MAP_KEY(VK_INSERT, Insert) + CRU_MAP_KEY(VK_DELETE, Delete) + CRU_MAP_KEY(VK_HOME, Home) + CRU_MAP_KEY(VK_END, End) + CRU_MAP_KEY(VK_PRIOR, PageUp) + CRU_MAP_KEY(VK_NEXT, PageDown) + CRU_MAP_KEY(VK_UP, Up) + CRU_MAP_KEY(VK_LEFT, Left) + CRU_MAP_KEY(VK_DOWN, Down) + CRU_MAP_KEY(VK_RIGHT, Right) + CRU_MAP_KEY(VK_SNAPSHOT, PrintScreen) + CRU_MAP_KEY(VK_PAUSE, Pause) + +#undef CRU_MAP_KEY + + default: + return KeyCode::Unknown; + } + } +} + +KeyModifier RetrieveKeyMofifier() { + KeyModifier result{0}; + if (::GetKeyState(VK_SHIFT) < 0) result |= key_modifiers::shift; + if (::GetKeyState(VK_CONTROL) < 0) result |= key_modifiers::ctrl; + if (::GetKeyState(VK_MENU) < 0) result |= key_modifiers::alt; + return result; +} +} // namespace cru::platform::native::win diff --git a/src/win/native/Timer.cpp b/src/win/native/Timer.cpp new file mode 100644 index 00000000..662067fb --- /dev/null +++ b/src/win/native/Timer.cpp @@ -0,0 +1,28 @@ +#include "Timer.hpp" + +namespace cru::platform::native::win { +TimerManager::TimerManager(GodWindow* god_window) { god_window_ = god_window; } + +UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, + TimerAction action) { + const auto id = current_count_++; + ::SetTimer(god_window_->GetHandle(), id, milliseconds, nullptr); + map_.emplace(id, std::make_pair(loop, std::move(action))); + return id; +} + +void TimerManager::KillTimer(const UINT_PTR id) { + const auto find_result = map_.find(id); + if (find_result != map_.cend()) { + ::KillTimer(god_window_->GetHandle(), id); + map_.erase(find_result); + } +} + +std::optional> TimerManager::GetAction( + const UINT_PTR id) { + const auto find_result = map_.find(id); + if (find_result == map_.cend()) return std::nullopt; + return find_result->second; +} +} // namespace cru::platform::native::win diff --git a/src/win/native/Timer.hpp b/src/win/native/Timer.hpp new file mode 100644 index 00000000..95f186a1 --- /dev/null +++ b/src/win/native/Timer.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "cru/win/WinPreConfig.hpp" + +#include "cru/common/Base.hpp" +#include "cru/win/native/GodWindow.hpp" + +#include +#include +#include +#include + +namespace cru::platform::native::win { +using TimerAction = std::function; + +class TimerManager : public Object { + public: + TimerManager(GodWindow* god_window); + + CRU_DELETE_COPY(TimerManager) + CRU_DELETE_MOVE(TimerManager) + + ~TimerManager() override = default; + + UINT_PTR CreateTimer(UINT milliseconds, bool loop, TimerAction action); + void KillTimer(UINT_PTR id); + std::optional> GetAction(UINT_PTR id); + + private: + GodWindow* god_window_; + + std::map> map_{}; + UINT_PTR current_count_ = 0; +}; +} // namespace cru::platform::native::win diff --git a/src/win/native/Window.cpp b/src/win/native/Window.cpp new file mode 100644 index 00000000..9dde1af3 --- /dev/null +++ b/src/win/native/Window.cpp @@ -0,0 +1,441 @@ +#include "cru/win/native/Window.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/platform/Check.hpp" +#include "cru/win/native/Cursor.hpp" +#include "cru/win/native/Exception.hpp" +#include "cru/win/native/Keyboard.hpp" +#include "cru/win/native/UiApplication.hpp" +#include "cru/win/native/WindowClass.hpp" +#include "cru/win/native/WindowRenderTarget.hpp" +#include "cru/win/String.hpp" +#include "DpiUtil.hpp" +#include "WindowD2DPainter.hpp" +#include "WindowManager.hpp" + +#include +#include + +namespace cru::platform::native::win { +WinNativeWindow::WinNativeWindow(WinUiApplication* application, + WindowClass* window_class, DWORD window_style, + WinNativeWindow* parent) + : application_(application), + resolver_(std::make_shared(this)), + parent_window_(parent) { + Expects(application); // application can't be null. + + if (parent != nullptr) { + throw new std::runtime_error("Can't use a invalid window as parent."); + } + + const auto window_manager = application->GetWindowManager(); + + hwnd_ = CreateWindowExW( + 0, window_class->GetName(), L"", window_style, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr, + application->GetInstanceHandle(), nullptr); + + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); + + window_manager->RegisterWindow(hwnd_, this); + + SetCursor(application->GetCursorManager()->GetSystemCursor( + cru::platform::native::SystemCursorType::Arrow)); + + window_render_target_ = std::make_unique( + application->GetDirectFactory(), hwnd_); +} + +WinNativeWindow::~WinNativeWindow() { + if (!sync_flag_) { + sync_flag_ = true; + Close(); + } + resolver_->Reset(); +} + +void WinNativeWindow::Close() { ::DestroyWindow(hwnd_); } + +bool WinNativeWindow::IsVisible() { return ::IsWindowVisible(hwnd_); } + +void WinNativeWindow::SetVisible(bool is_visible) { + is_visible ? ShowWindow(hwnd_, SW_SHOWNORMAL) : ShowWindow(hwnd_, SW_HIDE); +} +Size WinNativeWindow::GetClientSize() { + const auto pixel_rect = GetClientRectPixel(); + return Size(PixelToDipX(pixel_rect.right), PixelToDipY(pixel_rect.bottom)); +} + +void WinNativeWindow::SetClientSize(const Size& size) { + const auto window_style = + static_cast(GetWindowLongPtr(hwnd_, GWL_STYLE)); + const auto window_ex_style = + static_cast(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); + + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = DipToPixelX(size.width); + rect.bottom = DipToPixelY(size.height); + if (!AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style)) + throw Win32Error(::GetLastError(), "Failed to invoke AdjustWindowRectEx."); + + if (!SetWindowPos(hwnd_, nullptr, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE)) + throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); +} + +Rect WinNativeWindow::GetWindowRect() { + RECT rect; + if (!::GetWindowRect(hwnd_, &rect)) + throw Win32Error(::GetLastError(), "Failed to invoke GetWindowRect."); + + return Rect::FromVertices(PixelToDipX(rect.left), PixelToDipY(rect.top), + PixelToDipX(rect.right), PixelToDipY(rect.bottom)); +} + +void WinNativeWindow::SetWindowRect(const Rect& rect) { + if (!SetWindowPos(hwnd_, nullptr, DipToPixelX(rect.left), + DipToPixelY(rect.top), DipToPixelX(rect.GetRight()), + DipToPixelY(rect.GetBottom()), SWP_NOZORDER)) + throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); +} + +Point WinNativeWindow::GetMousePosition() { + POINT p; + if (!::GetCursorPos(&p)) + throw Win32Error(::GetLastError(), "Failed to get cursor position."); + if (!::ScreenToClient(hwnd_, &p)) + throw Win32Error(::GetLastError(), "Failed to call ScreenToClient."); + return PiToDip(p); +} + +bool WinNativeWindow::CaptureMouse() { + ::SetCapture(hwnd_); + return true; +} + +bool WinNativeWindow::ReleaseMouse() { + const auto result = ::ReleaseCapture(); + return result != 0; +} + +void WinNativeWindow::RequestRepaint() { + log::Debug("A repaint is requested."); + if (!::InvalidateRect(hwnd_, nullptr, FALSE)) + throw Win32Error(::GetLastError(), "Failed to invalidate window."); + if (!::UpdateWindow(hwnd_)) + throw Win32Error(::GetLastError(), "Failed to update window."); +} + +std::unique_ptr WinNativeWindow::BeginPaint() { + return std::make_unique(window_render_target_.get()); +} + +void WinNativeWindow::SetCursor(std::shared_ptr cursor) { + if (cursor == nullptr) { + throw std::runtime_error("Can't use a nullptr as cursor."); + } + + cursor_ = CheckPlatform(cursor, GetPlatformId()); + + if (!::SetClassLongPtrW(hwnd_, GCLP_HCURSOR, + reinterpret_cast(cursor_->GetHandle()))) { + log::Warn( + "Failed to set cursor because failed to set class long. Last error " + "code: {}.", + ::GetLastError()); + return; + } + + if (!IsVisible()) return; + + auto lg = [](const std::string_view& reason) { + log::Warn( + "Failed to set cursor because {} when window is visible. (We need " + "to update cursor if it is inside the window.) Last error code: {}.", + reason, ::GetLastError()); + }; + + ::POINT point; + if (!::GetCursorPos(&point)) { + lg("failed to get cursor pos"); + return; + } + + ::RECT rect; + if (!::GetClientRect(hwnd_, &rect)) { + lg("failed to get window's client rect"); + return; + } + + ::POINT lefttop{rect.left, rect.top}; + ::POINT rightbottom{rect.right, rect.bottom}; + if (!::ClientToScreen(hwnd_, &lefttop)) { + lg("failed to call ClientToScreen on lefttop"); + return; + } + + if (!::ClientToScreen(hwnd_, &rightbottom)) { + lg("failed to call ClientToScreen on rightbottom"); + return; + } + + if (point.x >= lefttop.x && point.y >= lefttop.y && + point.x <= rightbottom.x && point.y <= rightbottom.y) { + ::SetCursor(cursor_->GetHandle()); + } +} + +bool WinNativeWindow::HandleNativeWindowMessage(HWND hwnd, UINT msg, + WPARAM w_param, LPARAM l_param, + LRESULT* result) { + WindowNativeMessageEventArgs args{ + WindowNativeMessage{hwnd, msg, w_param, l_param}}; + native_message_event_.Raise(args); + if (args.IsHandled()) { + *result = args.GetResult(); + 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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(w_param)); + *result = 0; + return true; + case WM_KEYUP: + OnKeyUpInternal(static_cast(w_param)); + *result = 0; + return true; + case WM_SYSKEYDOWN: + if (l_param & (1 << 29)) { + OnKeyDownInternal(static_cast(w_param)); + *result = 0; + return true; + } + return false; + case WM_SYSKEYUP: + if (l_param & (1 << 29)) { + OnKeyUpInternal(static_cast(w_param)); + *result = 0; + return true; + } + return false; + 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; + case WM_IME_SETCONTEXT: + l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW; + *result = ::DefWindowProcW(hwnd, msg, w_param, l_param); + return true; + // We must block these message from DefWindowProc or it will create + // an ugly composition window. + case WM_IME_STARTCOMPOSITION: + case WM_IME_COMPOSITION: + *result = 0; + return true; + default: + return false; + } +} + +RECT WinNativeWindow::GetClientRectPixel() { + RECT rect; + if (!GetClientRect(hwnd_, &rect)) + throw Win32Error(::GetLastError(), "Failed to invoke GetClientRect."); + return rect; +} + +void WinNativeWindow::OnDestroyInternal() { + application_->GetWindowManager()->UnregisterWindow(hwnd_); + hwnd_ = nullptr; + destroy_event_.Raise(nullptr); + if (!sync_flag_) { + sync_flag_ = true; + delete this; + } +} + +void WinNativeWindow::OnPaintInternal() { + paint_event_.Raise(nullptr); + ValidateRect(hwnd_, nullptr); +} + +void WinNativeWindow::OnResizeInternal(const int new_width, + const int new_height) { + if (!(new_width == 0 && new_height == 0)) { + window_render_target_->ResizeBuffer(new_width, new_height); + resize_event_.Raise(Size{PixelToDipX(new_width), PixelToDipY(new_height)}); + } +} + +void WinNativeWindow::OnSetFocusInternal() { + has_focus_ = true; + focus_event_.Raise(FocusChangeType::Gain); +} + +void WinNativeWindow::OnKillFocusInternal() { + has_focus_ = false; + focus_event_.Raise(FocusChangeType::Lost); +} + +void WinNativeWindow::OnMouseMoveInternal(const POINT point) { + // when mouse was previous outside the window + if (!is_mouse_in_) { + // invoke TrackMouseEvent to have WM_MOUSELEAVE sent. + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof tme; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd_; + + TrackMouseEvent(&tme); + + is_mouse_in_ = true; + mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Enter); + } + + mouse_move_event_.Raise(PiToDip(point)); +} + +void WinNativeWindow::OnMouseLeaveInternal() { + is_mouse_in_ = false; + mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Leave); +} + +void WinNativeWindow::OnMouseDownInternal(platform::native::MouseButton button, + POINT point) { + const auto dip_point = PiToDip(point); + mouse_down_event_.Raise({button, dip_point, RetrieveKeyMofifier()}); +} + +void WinNativeWindow::OnMouseUpInternal(platform::native::MouseButton button, + POINT point) { + const auto dip_point = PiToDip(point); + mouse_up_event_.Raise({button, dip_point, RetrieveKeyMofifier()}); +} + +void WinNativeWindow::OnMouseWheelInternal(short delta, POINT point) { + CRU_UNUSED(delta) + CRU_UNUSED(point) +} + +void WinNativeWindow::OnKeyDownInternal(int virtual_code) { + key_down_event_.Raise( + {VirtualKeyToKeyCode(virtual_code), RetrieveKeyMofifier()}); +} + +void WinNativeWindow::OnKeyUpInternal(int virtual_code) { + key_up_event_.Raise( + {VirtualKeyToKeyCode(virtual_code), RetrieveKeyMofifier()}); +} + +void WinNativeWindow::OnActivatedInternal() {} + +void WinNativeWindow::OnDeactivatedInternal() {} + +void WinNativeWindowResolver::Reset() { + Expects(window_); // already reset, can't reset again + window_ = nullptr; +} + +WinNativeWindow* Resolve(gsl::not_null resolver) { + const auto window = resolver->Resolve(); + return window == nullptr ? nullptr + : CheckPlatform( + window, WinNativeResource::k_platform_id); +} // namespace cru::platform::native::win +} // namespace cru::platform::native::win diff --git a/src/win/native/cursor.cpp b/src/win/native/cursor.cpp deleted file mode 100644 index ca8bb1cd..00000000 --- a/src/win/native/cursor.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "cru/win/native/Cursor.hpp" - -#include "cru/common/Logger.hpp" -#include "cru/win/native/Exception.hpp" - -#include - -namespace cru::platform::native::win { -WinCursor::WinCursor(HCURSOR handle, bool auto_destroy) { - handle_ = handle; - auto_destroy_ = auto_destroy; -} - -WinCursor::~WinCursor() { - if (auto_destroy_) { - if (!::DestroyCursor(handle_)) { - // This is not a fetal error but might still need notice because it may - // cause leak. - log::Warn("Failed to destroy a cursor. Last error code: {}", - ::GetLastError()); - } - } -} - -namespace { -WinCursor* LoadWinCursor(const wchar_t* name) { - const auto handle = static_cast(::LoadImageW( - NULL, name, IMAGE_CURSOR, SM_CYCURSOR, SM_CYCURSOR, LR_SHARED)); - if (handle == NULL) { - throw Win32Error(::GetLastError(), "Failed to load system cursor."); - } - return new WinCursor(handle, false); -} -} // namespace - -WinCursorManager::WinCursorManager() - : sys_arrow_(LoadWinCursor(IDC_ARROW)), - sys_hand_(LoadWinCursor(IDC_HAND)) {} - -std::shared_ptr WinCursorManager::GetSystemWinCursor( - SystemCursorType type) { - switch (type) { - case SystemCursorType::Arrow: - return sys_arrow_; - case SystemCursorType::Hand: - return sys_hand_; - default: - throw std::runtime_error("Unknown system cursor value."); - } -} -} // namespace cru::platform::native::win diff --git a/src/win/native/keyboard.cpp b/src/win/native/keyboard.cpp deleted file mode 100644 index aa22e4a4..00000000 --- a/src/win/native/keyboard.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "cru/win/native/Keyboard.hpp" - -namespace cru::platform::native::win { -KeyCode VirtualKeyToKeyCode(int virtual_key) { - if (virtual_key >= 0x30 && virtual_key <= 0x39) { - return KeyCode{static_cast(KeyCode::N0) + (virtual_key - 0x30)}; - } else if (virtual_key >= 0x41 && virtual_key <= 0x5a) { - return KeyCode{static_cast(KeyCode::A) + (virtual_key - 0x41)}; - } else if (virtual_key >= VK_NUMPAD0 && virtual_key <= VK_NUMPAD9) { - return KeyCode{static_cast(KeyCode::NumPad0) + - (virtual_key - VK_NUMPAD0)}; - } else if (virtual_key >= VK_F1 && virtual_key <= VK_F12) { - return KeyCode{static_cast(KeyCode::F1) + (virtual_key - VK_F1)}; - } else { - switch (virtual_key) { -#define CRU_MAP_KEY(virtual_key, keycode) \ - case virtual_key: \ - return KeyCode::keycode; - - CRU_MAP_KEY(VK_LBUTTON, LeftButton) - CRU_MAP_KEY(VK_MBUTTON, MiddleButton) - CRU_MAP_KEY(VK_RBUTTON, RightButton) - CRU_MAP_KEY(VK_ESCAPE, Escape) - CRU_MAP_KEY(VK_OEM_3, GraveAccent) - CRU_MAP_KEY(VK_TAB, Tab) - CRU_MAP_KEY(VK_CAPITAL, CapsLock) - CRU_MAP_KEY(VK_LSHIFT, LeftShift) - CRU_MAP_KEY(VK_LCONTROL, LeftCtrl) - CRU_MAP_KEY(VK_LWIN, LeftSuper) - CRU_MAP_KEY(VK_LMENU, LeftAlt) - CRU_MAP_KEY(VK_OEM_MINUS, Minus) - CRU_MAP_KEY(VK_OEM_PLUS, Equal) - CRU_MAP_KEY(VK_BACK, Backspace) - CRU_MAP_KEY(VK_OEM_4, LeftSquareBracket) - CRU_MAP_KEY(VK_OEM_6, RightSquareBracket) - CRU_MAP_KEY(VK_OEM_5, BackSlash) - CRU_MAP_KEY(VK_OEM_1, Semicolon) - CRU_MAP_KEY(VK_OEM_7, Quote) - CRU_MAP_KEY(VK_OEM_COMMA, Comma) - CRU_MAP_KEY(VK_OEM_PERIOD, Period) - CRU_MAP_KEY(VK_OEM_2, Slash) - CRU_MAP_KEY(VK_RSHIFT, RightShift) - CRU_MAP_KEY(VK_RCONTROL, RightCtrl) - CRU_MAP_KEY(VK_RWIN, RightSuper) - CRU_MAP_KEY(VK_RMENU, RightAlt) - CRU_MAP_KEY(VK_INSERT, Insert) - CRU_MAP_KEY(VK_DELETE, Delete) - CRU_MAP_KEY(VK_HOME, Home) - CRU_MAP_KEY(VK_END, End) - CRU_MAP_KEY(VK_PRIOR, PageUp) - CRU_MAP_KEY(VK_NEXT, PageDown) - CRU_MAP_KEY(VK_UP, Up) - CRU_MAP_KEY(VK_LEFT, Left) - CRU_MAP_KEY(VK_DOWN, Down) - CRU_MAP_KEY(VK_RIGHT, Right) - CRU_MAP_KEY(VK_SNAPSHOT, PrintScreen) - CRU_MAP_KEY(VK_PAUSE, Pause) - -#undef CRU_MAP_KEY - - default: - return KeyCode::Unknown; - } - } -} - -KeyModifier RetrieveKeyMofifier() { - KeyModifier result{0}; - if (::GetKeyState(VK_SHIFT) < 0) result |= key_modifiers::shift; - if (::GetKeyState(VK_CONTROL) < 0) result |= key_modifiers::ctrl; - if (::GetKeyState(VK_MENU) < 0) result |= key_modifiers::alt; - return result; -} -} // namespace cru::platform::native::win diff --git a/src/win/native/timer.cpp b/src/win/native/timer.cpp deleted file mode 100644 index 662067fb..00000000 --- a/src/win/native/timer.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "Timer.hpp" - -namespace cru::platform::native::win { -TimerManager::TimerManager(GodWindow* god_window) { god_window_ = god_window; } - -UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, - TimerAction action) { - const auto id = current_count_++; - ::SetTimer(god_window_->GetHandle(), id, milliseconds, nullptr); - map_.emplace(id, std::make_pair(loop, std::move(action))); - return id; -} - -void TimerManager::KillTimer(const UINT_PTR id) { - const auto find_result = map_.find(id); - if (find_result != map_.cend()) { - ::KillTimer(god_window_->GetHandle(), id); - map_.erase(find_result); - } -} - -std::optional> TimerManager::GetAction( - const UINT_PTR id) { - const auto find_result = map_.find(id); - if (find_result == map_.cend()) return std::nullopt; - return find_result->second; -} -} // namespace cru::platform::native::win diff --git a/src/win/native/timer.hpp b/src/win/native/timer.hpp deleted file mode 100644 index 95f186a1..00000000 --- a/src/win/native/timer.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "cru/win/WinPreConfig.hpp" - -#include "cru/common/Base.hpp" -#include "cru/win/native/GodWindow.hpp" - -#include -#include -#include -#include - -namespace cru::platform::native::win { -using TimerAction = std::function; - -class TimerManager : public Object { - public: - TimerManager(GodWindow* god_window); - - CRU_DELETE_COPY(TimerManager) - CRU_DELETE_MOVE(TimerManager) - - ~TimerManager() override = default; - - UINT_PTR CreateTimer(UINT milliseconds, bool loop, TimerAction action); - void KillTimer(UINT_PTR id); - std::optional> GetAction(UINT_PTR id); - - private: - GodWindow* god_window_; - - std::map> map_{}; - UINT_PTR current_count_ = 0; -}; -} // namespace cru::platform::native::win diff --git a/src/win/native/window.cpp b/src/win/native/window.cpp deleted file mode 100644 index 9dde1af3..00000000 --- a/src/win/native/window.cpp +++ /dev/null @@ -1,441 +0,0 @@ -#include "cru/win/native/Window.hpp" - -#include "cru/common/Logger.hpp" -#include "cru/platform/Check.hpp" -#include "cru/win/native/Cursor.hpp" -#include "cru/win/native/Exception.hpp" -#include "cru/win/native/Keyboard.hpp" -#include "cru/win/native/UiApplication.hpp" -#include "cru/win/native/WindowClass.hpp" -#include "cru/win/native/WindowRenderTarget.hpp" -#include "cru/win/String.hpp" -#include "DpiUtil.hpp" -#include "WindowD2DPainter.hpp" -#include "WindowManager.hpp" - -#include -#include - -namespace cru::platform::native::win { -WinNativeWindow::WinNativeWindow(WinUiApplication* application, - WindowClass* window_class, DWORD window_style, - WinNativeWindow* parent) - : application_(application), - resolver_(std::make_shared(this)), - parent_window_(parent) { - Expects(application); // application can't be null. - - if (parent != nullptr) { - throw new std::runtime_error("Can't use a invalid window as parent."); - } - - const auto window_manager = application->GetWindowManager(); - - hwnd_ = CreateWindowExW( - 0, window_class->GetName(), L"", window_style, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr, - application->GetInstanceHandle(), nullptr); - - if (hwnd_ == nullptr) - throw Win32Error(::GetLastError(), "Failed to create window."); - - window_manager->RegisterWindow(hwnd_, this); - - SetCursor(application->GetCursorManager()->GetSystemCursor( - cru::platform::native::SystemCursorType::Arrow)); - - window_render_target_ = std::make_unique( - application->GetDirectFactory(), hwnd_); -} - -WinNativeWindow::~WinNativeWindow() { - if (!sync_flag_) { - sync_flag_ = true; - Close(); - } - resolver_->Reset(); -} - -void WinNativeWindow::Close() { ::DestroyWindow(hwnd_); } - -bool WinNativeWindow::IsVisible() { return ::IsWindowVisible(hwnd_); } - -void WinNativeWindow::SetVisible(bool is_visible) { - is_visible ? ShowWindow(hwnd_, SW_SHOWNORMAL) : ShowWindow(hwnd_, SW_HIDE); -} -Size WinNativeWindow::GetClientSize() { - const auto pixel_rect = GetClientRectPixel(); - return Size(PixelToDipX(pixel_rect.right), PixelToDipY(pixel_rect.bottom)); -} - -void WinNativeWindow::SetClientSize(const Size& size) { - const auto window_style = - static_cast(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = - static_cast(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = DipToPixelX(size.width); - rect.bottom = DipToPixelY(size.height); - if (!AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style)) - throw Win32Error(::GetLastError(), "Failed to invoke AdjustWindowRectEx."); - - if (!SetWindowPos(hwnd_, nullptr, 0, 0, rect.right - rect.left, - rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE)) - throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); -} - -Rect WinNativeWindow::GetWindowRect() { - RECT rect; - if (!::GetWindowRect(hwnd_, &rect)) - throw Win32Error(::GetLastError(), "Failed to invoke GetWindowRect."); - - return Rect::FromVertices(PixelToDipX(rect.left), PixelToDipY(rect.top), - PixelToDipX(rect.right), PixelToDipY(rect.bottom)); -} - -void WinNativeWindow::SetWindowRect(const Rect& rect) { - if (!SetWindowPos(hwnd_, nullptr, DipToPixelX(rect.left), - DipToPixelY(rect.top), DipToPixelX(rect.GetRight()), - DipToPixelY(rect.GetBottom()), SWP_NOZORDER)) - throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); -} - -Point WinNativeWindow::GetMousePosition() { - POINT p; - if (!::GetCursorPos(&p)) - throw Win32Error(::GetLastError(), "Failed to get cursor position."); - if (!::ScreenToClient(hwnd_, &p)) - throw Win32Error(::GetLastError(), "Failed to call ScreenToClient."); - return PiToDip(p); -} - -bool WinNativeWindow::CaptureMouse() { - ::SetCapture(hwnd_); - return true; -} - -bool WinNativeWindow::ReleaseMouse() { - const auto result = ::ReleaseCapture(); - return result != 0; -} - -void WinNativeWindow::RequestRepaint() { - log::Debug("A repaint is requested."); - if (!::InvalidateRect(hwnd_, nullptr, FALSE)) - throw Win32Error(::GetLastError(), "Failed to invalidate window."); - if (!::UpdateWindow(hwnd_)) - throw Win32Error(::GetLastError(), "Failed to update window."); -} - -std::unique_ptr WinNativeWindow::BeginPaint() { - return std::make_unique(window_render_target_.get()); -} - -void WinNativeWindow::SetCursor(std::shared_ptr cursor) { - if (cursor == nullptr) { - throw std::runtime_error("Can't use a nullptr as cursor."); - } - - cursor_ = CheckPlatform(cursor, GetPlatformId()); - - if (!::SetClassLongPtrW(hwnd_, GCLP_HCURSOR, - reinterpret_cast(cursor_->GetHandle()))) { - log::Warn( - "Failed to set cursor because failed to set class long. Last error " - "code: {}.", - ::GetLastError()); - return; - } - - if (!IsVisible()) return; - - auto lg = [](const std::string_view& reason) { - log::Warn( - "Failed to set cursor because {} when window is visible. (We need " - "to update cursor if it is inside the window.) Last error code: {}.", - reason, ::GetLastError()); - }; - - ::POINT point; - if (!::GetCursorPos(&point)) { - lg("failed to get cursor pos"); - return; - } - - ::RECT rect; - if (!::GetClientRect(hwnd_, &rect)) { - lg("failed to get window's client rect"); - return; - } - - ::POINT lefttop{rect.left, rect.top}; - ::POINT rightbottom{rect.right, rect.bottom}; - if (!::ClientToScreen(hwnd_, &lefttop)) { - lg("failed to call ClientToScreen on lefttop"); - return; - } - - if (!::ClientToScreen(hwnd_, &rightbottom)) { - lg("failed to call ClientToScreen on rightbottom"); - return; - } - - if (point.x >= lefttop.x && point.y >= lefttop.y && - point.x <= rightbottom.x && point.y <= rightbottom.y) { - ::SetCursor(cursor_->GetHandle()); - } -} - -bool WinNativeWindow::HandleNativeWindowMessage(HWND hwnd, UINT msg, - WPARAM w_param, LPARAM l_param, - LRESULT* result) { - WindowNativeMessageEventArgs args{ - WindowNativeMessage{hwnd, msg, w_param, l_param}}; - native_message_event_.Raise(args); - if (args.IsHandled()) { - *result = args.GetResult(); - 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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(platform::native::mouse_buttons::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(w_param)); - *result = 0; - return true; - case WM_KEYUP: - OnKeyUpInternal(static_cast(w_param)); - *result = 0; - return true; - case WM_SYSKEYDOWN: - if (l_param & (1 << 29)) { - OnKeyDownInternal(static_cast(w_param)); - *result = 0; - return true; - } - return false; - case WM_SYSKEYUP: - if (l_param & (1 << 29)) { - OnKeyUpInternal(static_cast(w_param)); - *result = 0; - return true; - } - return false; - 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; - case WM_IME_SETCONTEXT: - l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW; - *result = ::DefWindowProcW(hwnd, msg, w_param, l_param); - return true; - // We must block these message from DefWindowProc or it will create - // an ugly composition window. - case WM_IME_STARTCOMPOSITION: - case WM_IME_COMPOSITION: - *result = 0; - return true; - default: - return false; - } -} - -RECT WinNativeWindow::GetClientRectPixel() { - RECT rect; - if (!GetClientRect(hwnd_, &rect)) - throw Win32Error(::GetLastError(), "Failed to invoke GetClientRect."); - return rect; -} - -void WinNativeWindow::OnDestroyInternal() { - application_->GetWindowManager()->UnregisterWindow(hwnd_); - hwnd_ = nullptr; - destroy_event_.Raise(nullptr); - if (!sync_flag_) { - sync_flag_ = true; - delete this; - } -} - -void WinNativeWindow::OnPaintInternal() { - paint_event_.Raise(nullptr); - ValidateRect(hwnd_, nullptr); -} - -void WinNativeWindow::OnResizeInternal(const int new_width, - const int new_height) { - if (!(new_width == 0 && new_height == 0)) { - window_render_target_->ResizeBuffer(new_width, new_height); - resize_event_.Raise(Size{PixelToDipX(new_width), PixelToDipY(new_height)}); - } -} - -void WinNativeWindow::OnSetFocusInternal() { - has_focus_ = true; - focus_event_.Raise(FocusChangeType::Gain); -} - -void WinNativeWindow::OnKillFocusInternal() { - has_focus_ = false; - focus_event_.Raise(FocusChangeType::Lost); -} - -void WinNativeWindow::OnMouseMoveInternal(const POINT point) { - // when mouse was previous outside the window - if (!is_mouse_in_) { - // invoke TrackMouseEvent to have WM_MOUSELEAVE sent. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof tme; - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hwnd_; - - TrackMouseEvent(&tme); - - is_mouse_in_ = true; - mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Enter); - } - - mouse_move_event_.Raise(PiToDip(point)); -} - -void WinNativeWindow::OnMouseLeaveInternal() { - is_mouse_in_ = false; - mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Leave); -} - -void WinNativeWindow::OnMouseDownInternal(platform::native::MouseButton button, - POINT point) { - const auto dip_point = PiToDip(point); - mouse_down_event_.Raise({button, dip_point, RetrieveKeyMofifier()}); -} - -void WinNativeWindow::OnMouseUpInternal(platform::native::MouseButton button, - POINT point) { - const auto dip_point = PiToDip(point); - mouse_up_event_.Raise({button, dip_point, RetrieveKeyMofifier()}); -} - -void WinNativeWindow::OnMouseWheelInternal(short delta, POINT point) { - CRU_UNUSED(delta) - CRU_UNUSED(point) -} - -void WinNativeWindow::OnKeyDownInternal(int virtual_code) { - key_down_event_.Raise( - {VirtualKeyToKeyCode(virtual_code), RetrieveKeyMofifier()}); -} - -void WinNativeWindow::OnKeyUpInternal(int virtual_code) { - key_up_event_.Raise( - {VirtualKeyToKeyCode(virtual_code), RetrieveKeyMofifier()}); -} - -void WinNativeWindow::OnActivatedInternal() {} - -void WinNativeWindow::OnDeactivatedInternal() {} - -void WinNativeWindowResolver::Reset() { - Expects(window_); // already reset, can't reset again - window_ = nullptr; -} - -WinNativeWindow* Resolve(gsl::not_null resolver) { - const auto window = resolver->Resolve(); - return window == nullptr ? nullptr - : CheckPlatform( - window, WinNativeResource::k_platform_id); -} // namespace cru::platform::native::win -} // namespace cru::platform::native::win diff --git a/src/win/string.cpp b/src/win/string.cpp deleted file mode 100644 index 65a280f2..00000000 --- a/src/win/string.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "cru/win/String.hpp" - -#include "cru/win/Exception.hpp" - -#include - -namespace cru::platform::win { -std::string ToUtf8String(const std::wstring_view& string) { - if (string.empty()) return std::string{}; - - const auto length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), - static_cast(string.size()), nullptr, 0, nullptr, nullptr); - if (length == 0) { - throw Win32Error(::GetLastError(), - "Failed to convert wide string to UTF-8."); - } - - std::string result; - result.resize(length); - if (::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), - static_cast(string.size()), result.data(), - static_cast(result.size()), nullptr, - nullptr) == 0) - throw Win32Error(::GetLastError(), - "Failed to convert wide string to UTF-8."); - return result; -} - -std::wstring ToUtf16String(const std::string_view& string) { - if (string.empty()) return std::wstring{}; - - const auto length = - ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), - static_cast(string.size()), nullptr, 0); - if (length == 0) { - throw Win32Error(::GetLastError(), - "Failed to convert wide string to UTF-16."); - } - - std::wstring result; - result.resize(length); - if (::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), - static_cast(string.size()), result.data(), - static_cast(result.size())) == 0) - throw win::Win32Error(::GetLastError(), - "Failed to convert wide string to UTF-16."); - return result; -} - -template -inline std::enable_if_t, CodePoint> ExtractBits( - UInt n) { - return static_cast(n & ((1u << number_of_bit) - 1)); -} - -CodePoint Utf8Iterator::Next() { - if (position_ == static_cast(string_.length())) return k_code_point_end; - - const auto cu0 = static_cast(string_[position_++]); - - auto read_next_folowing_code = [this]() -> CodePoint { - if (this->position_ == static_cast(string_.length())) - throw TextEncodeException( - "Unexpected end when read continuing byte of multi-byte code point."); - -#ifdef CRU_DEBUG - const auto u = static_cast(string_[position_]); - if (!(u & (1u << 7)) || (u & (1u << 6))) { - throw TextEncodeException( - "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " - "multi-byte code point."); - } -#endif - - return ExtractBits(string_[position_++]); - }; - - if ((1u << 7) & cu0) { - if ((1u << 6) & cu0) { // 2~4-length code point - if ((1u << 5) & cu0) { // 3~4-length code point - if ((1u << 4) & cu0) { // 4-length code point -#ifdef CRU_DEBUG - if (cu0 & (1u << 3)) { - throw TextEncodeException( - "Unexpected bad-format begin byte (not 0b10xxxxxx) of 4-byte " - "code point."); - } -#endif - - const CodePoint s0 = ExtractBits(cu0) << (6 * 3); - const CodePoint s1 = read_next_folowing_code() << (6 * 2); - const CodePoint s2 = read_next_folowing_code() << 6; - const CodePoint s3 = read_next_folowing_code(); - return s0 + s1 + s2 + s3; - } else { // 3-length code point - const CodePoint s0 = ExtractBits(cu0) << (6 * 2); - const CodePoint s1 = read_next_folowing_code() << 6; - const CodePoint s2 = read_next_folowing_code(); - return s0 + s1 + s2; - } - } else { // 2-length code point - const CodePoint s0 = ExtractBits(cu0) << 6; - const CodePoint s1 = read_next_folowing_code(); - return s0 + s1; - } - } else { - throw TextEncodeException( - "Unexpected bad-format (0b10xxxxxx) begin byte of a code point."); - } - } else { - return static_cast(cu0); - } -} - -CodePoint Utf16Iterator::Next() { - if (position_ == static_cast(string_.length())) return k_code_point_end; - - const auto cu0 = static_cast(string_[position_++]); - - if (cu0 < 0xd800u || cu0 >= 0xe000u) { // 1-length code point - return static_cast(cu0); - } else if (cu0 <= 0xdbffu) { // 2-length code point - if (position_ == static_cast(string_.length())) { - throw TextEncodeException( - "Unexpected end when reading second code unit of surrogate pair."); - } - const auto cu1 = static_cast(string_[position_++]); - -#ifdef CRU_DEBUG - if (cu1 < 0xDC00u || cu1 > 0xdfffu) { - throw TextEncodeException( - "Unexpected bad-format second code unit of surrogate pair."); - } -#endif - - const auto s0 = ExtractBits(cu0) << 10; - const auto s1 = ExtractBits(cu1); - - return s0 + s1 + 0x10000; - - } else { - throw TextEncodeException( - "Unexpected bad-format first code unit of surrogate pair."); - } -} - -int IndexUtf8ToUtf16(const std::string_view& utf8_string, int utf8_index, - const std::wstring_view& utf16_string) { - if (utf8_index >= static_cast(utf8_string.length())) - return static_cast(utf16_string.length()); - - int cp_index = 0; - Utf8Iterator iter{utf8_string}; - while (iter.CurrentPosition() <= utf8_index) { - iter.Next(); - cp_index++; - } - - Utf16Iterator result_iter{utf16_string}; - for (int i = 0; i < cp_index - 1; i++) { - if (result_iter.Next() == k_code_point_end) break; - } - - return result_iter.CurrentPosition(); -} - -int IndexUtf16ToUtf8(const std::wstring_view& utf16_string, int utf16_index, - const std::string_view& utf8_string) { - if (utf16_index >= static_cast(utf16_string.length())) - return static_cast(utf8_string.length()); - - int cp_index = 0; - Utf16Iterator iter{utf16_string}; - while (iter.CurrentPosition() <= utf16_index) { - iter.Next(); - cp_index++; - } - - Utf8Iterator result_iter{utf8_string}; - for (int i = 0; i < cp_index - 1; i++) { - if (result_iter.Next() == k_code_point_end) break; - } - - return result_iter.CurrentPosition(); -} - -} // namespace cru::platform::win -- cgit v1.2.3