diff options
author | crupest <crupest@outlook.com> | 2022-01-12 21:35:08 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-01-12 21:35:08 +0800 |
commit | 7a42d92c10a4bc686244668dd0e3f903f30f2fae (patch) | |
tree | e48fc4a47afa5eadbdf54cec6d2b73110f500680 | |
parent | 882d843083895f4905571dc273e801ee18fd5984 (diff) | |
download | cru-7a42d92c10a4bc686244668dd0e3f903f30f2fae.tar.gz cru-7a42d92c10a4bc686244668dd0e3f903f30f2fae.tar.bz2 cru-7a42d92c10a4bc686244668dd0e3f903f30f2fae.zip |
...
-rw-r--r-- | include/cru/common/String.hpp | 2 | ||||
-rw-r--r-- | include/cru/platform/graphics/NullPainter.hpp | 4 | ||||
-rw-r--r-- | include/cru/platform/graphics/Painter.hpp | 3 | ||||
-rw-r--r-- | include/cru/platform/gui/Window.hpp | 1 | ||||
-rw-r--r-- | include/cru/win/WinPreConfig.hpp | 1 | ||||
-rw-r--r-- | include/cru/win/graphics/direct/Painter.hpp | 10 | ||||
-rw-r--r-- | include/cru/win/graphics/direct/TextLayout.hpp | 1 | ||||
-rw-r--r-- | include/cru/win/gui/InputMethod.hpp | 4 | ||||
-rw-r--r-- | include/cru/win/gui/UiApplication.hpp | 14 | ||||
-rw-r--r-- | include/cru/win/gui/Window.hpp | 51 | ||||
-rw-r--r-- | src/common/String.cpp | 7 | ||||
-rw-r--r-- | src/ui/controls/TextHostControlService.cpp | 27 | ||||
-rw-r--r-- | src/win/DebugLogger.hpp | 5 | ||||
-rw-r--r-- | src/win/Exception.cpp | 9 | ||||
-rw-r--r-- | src/win/StdOutLogger.hpp | 5 | ||||
-rw-r--r-- | src/win/graphics/direct/Painter.cpp | 40 | ||||
-rw-r--r-- | src/win/graphics/direct/TextLayout.cpp | 39 | ||||
-rw-r--r-- | src/win/gui/InputMethod.cpp | 14 | ||||
-rw-r--r-- | src/win/gui/UiApplication.cpp | 22 | ||||
-rw-r--r-- | src/win/gui/Window.cpp | 209 | ||||
-rw-r--r-- | src/win/gui/WindowManager.cpp | 3 |
21 files changed, 343 insertions, 128 deletions
diff --git a/include/cru/common/String.hpp b/include/cru/common/String.hpp index 88dd06f0..dd3da52f 100644 --- a/include/cru/common/String.hpp +++ b/include/cru/common/String.hpp @@ -80,6 +80,8 @@ class CRU_BASE_API String { } } + String(size_type size, value_type ch = 0); + String(std::initializer_list<value_type> l); explicit String(StringView str); diff --git a/include/cru/platform/graphics/NullPainter.hpp b/include/cru/platform/graphics/NullPainter.hpp index 5586120b..7889f99e 100644 --- a/include/cru/platform/graphics/NullPainter.hpp +++ b/include/cru/platform/graphics/NullPainter.hpp @@ -42,11 +42,9 @@ class NullPainter : public Object, public virtual IPainter { CRU_UNUSED(brush) CRU_UNUSED(width) } - void FillEllipse(const Rect& outline_rect, IBrush* brush, - float width) override { + void FillEllipse(const Rect& outline_rect, IBrush* brush) override { CRU_UNUSED(outline_rect) CRU_UNUSED(brush) - CRU_UNUSED(width) } void StrokeGeometry(IGeometry* geometry, IBrush* brush, diff --git a/include/cru/platform/graphics/Painter.hpp b/include/cru/platform/graphics/Painter.hpp index 4f1724ec..552a3307 100644 --- a/include/cru/platform/graphics/Painter.hpp +++ b/include/cru/platform/graphics/Painter.hpp @@ -18,8 +18,7 @@ struct IPainter : virtual IPlatformResource { virtual void FillRectangle(const Rect& rectangle, IBrush* brush) = 0; virtual void StrokeEllipse(const Rect& outline_rect, IBrush* brush, float width) = 0; - virtual void FillEllipse(const Rect& outline_rect, IBrush* brush, - float width) = 0; + virtual void FillEllipse(const Rect& outline_rect, IBrush* brush) = 0; virtual void StrokeGeometry(IGeometry* geometry, IBrush* brush, float width) = 0; diff --git a/include/cru/platform/gui/Window.hpp b/include/cru/platform/gui/Window.hpp index bab5e8fe..9f17b976 100644 --- a/include/cru/platform/gui/Window.hpp +++ b/include/cru/platform/gui/Window.hpp @@ -85,7 +85,6 @@ struct INativeWindow : virtual IPlatformResource { // Remember to call EndDraw on return value and destroy it. virtual std::unique_ptr<graphics::IPainter> BeginPaint() = 0; - // Don't use this instance after receive this event. virtual IEvent<std::nullptr_t>* CreateEvent() = 0; virtual IEvent<std::nullptr_t>* DestroyEvent() = 0; virtual IEvent<std::nullptr_t>* PaintEvent() = 0; diff --git a/include/cru/win/WinPreConfig.hpp b/include/cru/win/WinPreConfig.hpp index 1613da95..1bd494f2 100644 --- a/include/cru/win/WinPreConfig.hpp +++ b/include/cru/win/WinPreConfig.hpp @@ -8,6 +8,7 @@ #undef CreateWindow #undef DrawText #undef CreateFont +#undef CreateEvent #include <d2d1_2.h> #include <d3d11.h> diff --git a/include/cru/win/graphics/direct/Painter.hpp b/include/cru/win/graphics/direct/Painter.hpp index b34c1563..d7b90d19 100644 --- a/include/cru/win/graphics/direct/Painter.hpp +++ b/include/cru/win/graphics/direct/Painter.hpp @@ -24,6 +24,7 @@ class D2DPainter : public DirectResource, public: Matrix GetTransform() override; void SetTransform(const platform::Matrix& matrix) override; + void ConcatTransform(const Matrix& matrix) override; void Clear(const Color& color) override; @@ -32,6 +33,9 @@ class D2DPainter : public DirectResource, void StrokeRectangle(const Rect& rectangle, IBrush* brush, float width) override; void FillRectangle(const Rect& rectangle, IBrush* brush) override; + void StrokeEllipse(const Rect& outline_rect, IBrush* brush, + float width) override; + void FillEllipse(const Rect& outline_rect, IBrush* brush) override; void StrokeGeometry(IGeometry* geometry, IBrush* brush, float width) override; void FillGeometry(IGeometry* geometry, IBrush* brush) override; @@ -40,9 +44,11 @@ class D2DPainter : public DirectResource, IBrush* brush) override; void PushLayer(const Rect& bounds) override; - void PopLayer() override; + void PushState() override; + void PopState() override; + void EndDraw() override final; protected: @@ -56,6 +62,8 @@ class D2DPainter : public DirectResource, ID2D1RenderTarget* render_target_; std::vector<Microsoft::WRL::ComPtr<ID2D1Layer>> layers_; + std::vector<Microsoft::WRL::ComPtr<ID2D1DrawingStateBlock>> + drawing_state_stack_; bool is_drawing_ = true; }; diff --git a/include/cru/win/graphics/direct/TextLayout.hpp b/include/cru/win/graphics/direct/TextLayout.hpp index 1ac56a9d..b1843dd7 100644 --- a/include/cru/win/graphics/direct/TextLayout.hpp +++ b/include/cru/win/graphics/direct/TextLayout.hpp @@ -52,6 +52,7 @@ class DWriteTextLayout : public DirectGraphicsResource, TextHitTestResult HitTest(const Point& point) override; private: + bool edit_mode_ = false; String text_; std::shared_ptr<DWriteFont> font_; float max_width_ = std::numeric_limits<float>::max(); diff --git a/include/cru/win/gui/InputMethod.hpp b/include/cru/win/gui/InputMethod.hpp index df687617..3784dcda 100644 --- a/include/cru/win/gui/InputMethod.hpp +++ b/include/cru/win/gui/InputMethod.hpp @@ -70,7 +70,7 @@ class WinInputMethodContext : public WinNativeResource, private: void OnWindowNativeMessage(WindowNativeMessageEventArgs& args); - std::u16string GetResultString(); + String GetResultString(); AutoHIMC GetHIMC(); @@ -82,6 +82,6 @@ class WinInputMethodContext : public WinNativeResource, Event<std::nullptr_t> composition_start_event_; Event<std::nullptr_t> composition_end_event_; Event<std::nullptr_t> composition_event_; - Event<std::u16string_view> text_event_; + Event<StringView> text_event_; }; } // namespace cru::platform::gui::win diff --git a/include/cru/win/gui/UiApplication.hpp b/include/cru/win/gui/UiApplication.hpp index daf72795..4b972fee 100644 --- a/include/cru/win/gui/UiApplication.hpp +++ b/include/cru/win/gui/UiApplication.hpp @@ -33,6 +33,13 @@ class WinUiApplication : public WinNativeResource, void AddOnQuitHandler(std::function<void()> handler) override; + bool IsQuitOnAllWindowClosed() override { + return is_quit_on_all_window_closed_; + } + void SetQuitOnAllWindowClosed(bool quit_on_all_window_closed) override { + is_quit_on_all_window_closed_ = quit_on_all_window_closed; + } + long long SetImmediate(std::function<void()> action) override; long long SetTimeout(std::chrono::milliseconds milliseconds, std::function<void()> action) override; @@ -45,12 +52,15 @@ class WinUiApplication : public WinNativeResource, cru::platform::graphics::IGraphicsFactory* GetGraphicsFactory() override; - cru::platform::graphics::win::direct::DirectGraphicsFactory* GetDirectFactory() { + cru::platform::graphics::win::direct::DirectGraphicsFactory* + GetDirectFactory() { return graph_factory_.get(); } ICursorManager* GetCursorManager() override; + IClipboard* GetClipboard() override; + HINSTANCE GetInstanceHandle() const { return instance_handle_; } GodWindow* GetGodWindow() const { return god_window_.get(); } @@ -60,6 +70,8 @@ class WinUiApplication : public WinNativeResource, private: HINSTANCE instance_handle_; + bool is_quit_on_all_window_closed_ = true; + std::unique_ptr<cru::platform::graphics::win::direct::DirectGraphicsFactory> graph_factory_; diff --git a/include/cru/win/gui/Window.hpp b/include/cru/win/gui/Window.hpp index 9f241b0a..5ce3ed25 100644 --- a/include/cru/win/gui/Window.hpp +++ b/include/cru/win/gui/Window.hpp @@ -13,8 +13,7 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::gui::win::WinNativeWindow") public: - WinNativeWindow(WinUiApplication* application, WindowClass* window_class, - DWORD window_style, WinNativeWindow* parent); + explicit WinNativeWindow(WinUiApplication* application); CRU_DELETE_COPY(WinNativeWindow) CRU_DELETE_MOVE(WinNativeWindow) @@ -25,19 +24,31 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { void Close() override; WinNativeWindow* GetParent() override { return parent_window_; } + void SetParent(INativeWindow* parent) override; + WindowStyleFlag GetStyleFlag() override { return style_flag_; } + void SetStyleFlag(WindowStyleFlag flag) override; + + WindowVisibilityType GetVisibility() override { return visibility_; } + void SetVisibility(WindowVisibilityType visibility) override; Size GetClientSize() override; void SetClientSize(const Size& size) override; + Rect GetClientRect() override; + void SetClientRect(const Rect& rect) override; + // Get the rect of the window containing frame. // The lefttop of the rect is relative to screen lefttop. Rect GetWindowRect() override; // Set the rect of the window containing frame. // The lefttop of the rect is relative to screen lefttop. + // TODO: Known limitation: can't calc client rect and save. void SetWindowRect(const Rect& rect) override; + bool RequestFocus() override; + Point GetMousePosition() override; bool CaptureMouse() override; @@ -48,8 +59,12 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { void SetCursor(std::shared_ptr<ICursor> cursor) override; + IEvent<std::nullptr_t>* CreateEvent() override { return &create_event_; } IEvent<std::nullptr_t>* DestroyEvent() override { return &destroy_event_; } IEvent<std::nullptr_t>* PaintEvent() override { return &paint_event_; } + IEvent<WindowVisibilityType>* VisibilityChangeEvent() override { + return &visibility_change_event_; + } IEvent<Size>* ResizeEvent() override { return &resize_event_; } IEvent<FocusChangeType>* FocusEvent() override { return &focus_event_; } IEvent<MouseEnterLeaveType>* MouseEnterLeaveEvent() override { @@ -103,6 +118,15 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { return result; } + inline RECT DipToPixel(const Rect& dip_rect) { + RECT result; + result.left = DipToPixel(dip_rect.left); + result.top = DipToPixel(dip_rect.top); + result.right = DipToPixel(dip_rect.GetRight()); + result.bottom = DipToPixel(dip_rect.GetBottom()); + return result; + } + inline float PixelToDip(const int pixel) { return static_cast<float>(pixel) * 96.0f / GetDpi(); } @@ -111,10 +135,18 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { return Point(PixelToDip(pi_point.x), PixelToDip(pi_point.y)); } + inline Rect PixelToDip(const RECT& pi_rect) { + return Rect::FromVertices(PixelToDip(pi_rect.left), PixelToDip(pi_rect.top), + PixelToDip(pi_rect.right), + PixelToDip(pi_rect.bottom)); + } + private: // Get the client rect in pixel. RECT GetClientRectPixel(); + void RecreateWindow(); + //*************** region: native messages *************** void OnDestroyInternal(); @@ -139,15 +171,12 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { private: WinUiApplication* application_; - // when delete is called first, it set this to true to indicate - // destroy message handler not to double delete this instance; - // when destroy handler is called first (by user action or method - // Close), it set this to true to indicate delete not call Close - // again. - bool sync_flag_ = false; + WindowStyleFlag style_flag_{}; + WindowVisibilityType visibility_{}; + Rect client_rect_; - HWND hwnd_; - WinNativeWindow* parent_window_; + HWND hwnd_ = nullptr; + WinNativeWindow* parent_window_ = nullptr; float dpi_; @@ -161,9 +190,11 @@ class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { std::unique_ptr<WinInputMethodContext> input_method_context_; + Event<std::nullptr_t> create_event_; Event<std::nullptr_t> destroy_event_; Event<std::nullptr_t> paint_event_; Event<Size> resize_event_; + Event<WindowVisibilityType> visibility_change_event_; Event<FocusChangeType> focus_event_; Event<MouseEnterLeaveType> mouse_enter_leave_event_; Event<Point> mouse_move_event_; diff --git a/src/common/String.cpp b/src/common/String.cpp index ca16cee7..21f3f235 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -39,6 +39,13 @@ String::String(const_pointer str, Index size) { this->capacity_ = size; } +String::String(size_type size, value_type ch) : String() { + reserve(size); + for (Index i = 0; i < size; i++) { + append(ch); + } +} + String::String(std::initializer_list<char16_t> l) : String(l.begin(), l.size()) {} diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp index 61e6aef6..7fcf70e9 100644 --- a/src/ui/controls/TextHostControlService.cpp +++ b/src/ui/controls/TextHostControlService.cpp @@ -1,6 +1,7 @@ #include "cru/ui/controls/TextHostControlService.hpp" #include "../Helper.hpp" +#include "cru/common/Base.hpp" #include "cru/common/Logger.hpp" #include "cru/common/String.hpp" #include "cru/common/StringUtil.hpp" @@ -28,6 +29,7 @@ TextControlMovePattern TextControlMovePattern::kLeft( u"Left", helper::ShortcutKeyBind(platform::gui::KeyCode::Left), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) Utf16PreviousCodePoint(text, current_position, ¤t_position); return current_position; }); @@ -35,6 +37,7 @@ TextControlMovePattern TextControlMovePattern::kRight( u"Right", helper::ShortcutKeyBind(platform::gui::KeyCode::Right), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) Utf16NextCodePoint(text, current_position, ¤t_position); return current_position; }); @@ -44,6 +47,7 @@ TextControlMovePattern TextControlMovePattern::kCtrlLeft( platform::gui::KeyModifiers::ctrl), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) return Utf16PreviousWord(text, current_position); }); TextControlMovePattern TextControlMovePattern::kCtrlRight( @@ -52,12 +56,14 @@ TextControlMovePattern TextControlMovePattern::kCtrlRight( platform::gui::KeyModifiers::ctrl), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) return Utf16NextWord(text, current_position); }); TextControlMovePattern TextControlMovePattern::kUp( u"Up", helper::ShortcutKeyBind(platform::gui::KeyCode::Up), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(text) auto text_render_object = service->GetTextRenderObject(); auto rect = text_render_object->TextSinglePoint(current_position, false); rect.top -= 0.1f; @@ -68,6 +74,7 @@ TextControlMovePattern TextControlMovePattern::kDown( u"Down", helper::ShortcutKeyBind(platform::gui::KeyCode::Down), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(text) auto text_render_object = service->GetTextRenderObject(); auto rect = text_render_object->TextSinglePoint(current_position, false); rect.top += rect.height + 0.1f; @@ -78,6 +85,7 @@ TextControlMovePattern TextControlMovePattern::kHome( u"Home(Line Begin)", helper::ShortcutKeyBind(platform::gui::KeyCode::Home), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) return Utf16BackwardUntil(text, current_position, [](char16_t c) { return c == u'\n'; }); }); @@ -85,6 +93,7 @@ TextControlMovePattern TextControlMovePattern::kEnd( u"End(Line End)", helper::ShortcutKeyBind(platform::gui::KeyCode::End), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) return Utf16ForwardUntil(text, current_position, [](char16_t c) { return c == u'\n'; }); }); @@ -93,17 +102,29 @@ TextControlMovePattern TextControlMovePattern::kCtrlHome( helper::ShortcutKeyBind(platform::gui::KeyCode::Home, platform::gui::KeyModifiers::ctrl), [](TextHostControlService* service, StringView text, - gsl::index current_position) { return 0; }); + gsl::index current_position) { + CRU_UNUSED(service) + CRU_UNUSED(text) + CRU_UNUSED(current_position) + return 0; + }); TextControlMovePattern TextControlMovePattern::kCtrlEnd( u"Ctrl+End(Document End)", helper::ShortcutKeyBind(platform::gui::KeyCode::End, platform::gui::KeyModifiers::ctrl), [](TextHostControlService* service, StringView text, - gsl::index current_position) { return text.size(); }); + gsl::index current_position) { + CRU_UNUSED(service) + CRU_UNUSED(text) + CRU_UNUSED(current_position) + return text.size(); + }); TextControlMovePattern TextControlMovePattern::kPageUp( u"PageUp", helper::ShortcutKeyBind(platform::gui::KeyCode::PageUp), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) + CRU_UNUSED(text) // TODO: Implement this. return current_position; }); @@ -111,6 +132,8 @@ TextControlMovePattern TextControlMovePattern::kPageDown( u"PageDown", helper::ShortcutKeyBind(platform::gui::KeyCode::PageDown), [](TextHostControlService* service, StringView text, gsl::index current_position) { + CRU_UNUSED(service) + CRU_UNUSED(text) // TODO: Implement this. return current_position; }); diff --git a/src/win/DebugLogger.hpp b/src/win/DebugLogger.hpp index 598ee9e8..5e78af22 100644 --- a/src/win/DebugLogger.hpp +++ b/src/win/DebugLogger.hpp @@ -13,10 +13,11 @@ class WinDebugLoggerSource : public ::cru::log::ILogSource { ~WinDebugLoggerSource() = default; - void Write(::cru::log::LogLevel level, const std::u16string& s) override { + void Write(::cru::log::LogLevel level, StringView s) override { CRU_UNUSED(level) - ::OutputDebugStringW(reinterpret_cast<const wchar_t*>(s.c_str())); + String m = s.ToString(); + ::OutputDebugStringW(reinterpret_cast<const wchar_t*>(m.c_str())); } }; } // namespace cru::platform::win diff --git a/src/win/Exception.cpp b/src/win/Exception.cpp index a7b0927a..cf55c191 100644 --- a/src/win/Exception.cpp +++ b/src/win/Exception.cpp @@ -7,10 +7,9 @@ namespace cru::platform::win { inline String HResultMakeMessage(HRESULT h_result, std::optional<String> message) { if (message.has_value()) - return fmt::format(FMT_STRING(L"HRESULT: {:#08x}. Message: {}"), h_result, - message->WinCStr()); + return Format(L"HRESULT: {}. Message: {}", h_result, message->WinCStr()); else - return fmt::format(FMT_STRING(L"HRESULT: {:#08x}."), h_result); + return Format(L"HRESULT: {}.", h_result); } HResultError::HResultError(HRESULT h_result) @@ -25,8 +24,8 @@ HResultError::HResultError(HRESULT h_result, h_result_(h_result) {} inline String Win32MakeMessage(DWORD error_code, String message) { - return fmt::format(L"Last error code: {:#04x}.\nMessage: {}\n", error_code, - message.WinCStr()); + return Format(L"Last error code: {}.\nMessage: {}\n", error_code, + message.WinCStr()); } Win32Error::Win32Error(std::string_view message) diff --git a/src/win/StdOutLogger.hpp b/src/win/StdOutLogger.hpp index bff8b30e..b3a22dd5 100644 --- a/src/win/StdOutLogger.hpp +++ b/src/win/StdOutLogger.hpp @@ -15,10 +15,11 @@ class WinStdOutLoggerSource : public ::cru::log::ILogSource { ~WinStdOutLoggerSource() = default; - void Write(::cru::log::LogLevel level, const std::u16string& s) override { + void Write(::cru::log::LogLevel level, StringView s) override { CRU_UNUSED(level) - std::fputws(reinterpret_cast<const wchar_t*>(s.c_str()), stdout); + String m = s.ToString(); + std::fputws(reinterpret_cast<const wchar_t*>(m.c_str()), stdout); } }; } // namespace cru::platform::win diff --git a/src/win/graphics/direct/Painter.cpp b/src/win/graphics/direct/Painter.cpp index 26ef92ec..5db60fd1 100644 --- a/src/win/graphics/direct/Painter.cpp +++ b/src/win/graphics/direct/Painter.cpp @@ -27,6 +27,10 @@ void D2DPainter::SetTransform(const platform::Matrix& matrix) { render_target_->SetTransform(Convert(matrix)); } +void D2DPainter::ConcatTransform(const Matrix& matrix) { + SetTransform(GetTransform() * matrix); +} + void D2DPainter::Clear(const Color& color) { CheckValidation(); render_target_->Clear(Convert(color)); @@ -54,6 +58,24 @@ void D2DPainter::FillRectangle(const Rect& rectangle, IBrush* brush) { render_target_->FillRectangle(Convert(rectangle), b->GetD2DBrushInterface()); } +void D2DPainter::StrokeEllipse(const Rect& outline_rect, IBrush* brush, + float width) { + CheckValidation(); + const auto b = CheckPlatform<ID2DBrush>(brush, GetPlatformId()); + render_target_->DrawEllipse( + D2D1::Ellipse(Convert(outline_rect.GetCenter()), + outline_rect.width / 2.0f, outline_rect.height / 2.0f), + b->GetD2DBrushInterface(), width); +} +void D2DPainter::FillEllipse(const Rect& outline_rect, IBrush* brush) { + CheckValidation(); + const auto b = CheckPlatform<ID2DBrush>(brush, GetPlatformId()); + render_target_->FillEllipse( + D2D1::Ellipse(Convert(outline_rect.GetCenter()), + outline_rect.width / 2.0f, outline_rect.height / 2.0f), + b->GetD2DBrushInterface()); +} + void D2DPainter::StrokeGeometry(IGeometry* geometry, IBrush* brush, float width) { CheckValidation(); @@ -96,6 +118,24 @@ void D2DPainter::PopLayer() { layers_.pop_back(); } +void D2DPainter::PushState() { + Microsoft::WRL::ComPtr<ID2D1Factory> factory = nullptr; + render_target_->GetFactory(&factory); + + Microsoft::WRL::ComPtr<ID2D1DrawingStateBlock> state_block; + factory->CreateDrawingStateBlock(&state_block); + render_target_->SaveDrawingState(state_block.Get()); + + drawing_state_stack_.push_back(std::move(state_block)); +} + +void D2DPainter::PopState() { + Expects(!drawing_state_stack_.empty()); + auto drawing_state = drawing_state_stack_.back(); + drawing_state_stack_.pop_back(); + render_target_->RestoreDrawingState(drawing_state.Get()); +} + void D2DPainter::EndDraw() { if (is_drawing_) { is_drawing_ = false; diff --git a/src/win/graphics/direct/TextLayout.cpp b/src/win/graphics/direct/TextLayout.cpp index 6ece8ed1..bec4a972 100644 --- a/src/win/graphics/direct/TextLayout.cpp +++ b/src/win/graphics/direct/TextLayout.cpp @@ -1,4 +1,5 @@ #include "cru/win/graphics/direct/TextLayout.hpp" +#include <dwrite.h> #include "cru/common/Logger.hpp" #include "cru/platform/Check.hpp" @@ -55,6 +56,44 @@ void DWriteTextLayout::SetMaxHeight(float max_height) { ThrowIfFailed(text_layout_->SetMaxHeight(max_height_)); } +bool DWriteTextLayout::IsEditMode() { return edit_mode_; } + +void DWriteTextLayout::SetEditMode(bool enable) { + edit_mode_ = enable; + // TODO: Implement this. +} + +Index DWriteTextLayout::GetLineIndexFromCharIndex(Index char_index) { + if (char_index < 0 || char_index >= text_.size()) { + return -1; + } + + auto line_index = 0; + for (Index i = 0; i < char_index; ++i) { + if (text_[i] == u'\n') { + line_index++; + } + } + + return line_index; +} + +float DWriteTextLayout::GetLineHeight(Index line_index) { + Index count = GetLineCount(); + std::vector<DWRITE_LINE_METRICS> line_metrics(count); + + UINT32 actual_line_count = 0; + text_layout_->GetLineMetrics(line_metrics.data(), static_cast<UINT32>(count), + &actual_line_count); + return line_metrics[line_index].height; +} + +Index DWriteTextLayout::GetLineCount() { + UINT32 line_count = 0; + text_layout_->GetLineMetrics(nullptr, 0, &line_count); + return line_count; +} + Rect DWriteTextLayout::GetTextBounds(bool includingTrailingSpace) { DWRITE_TEXT_METRICS metrics; ThrowIfFailed(text_layout_->GetMetrics(&metrics)); diff --git a/src/win/gui/InputMethod.cpp b/src/win/gui/InputMethod.cpp index cc237e88..47e17109 100644 --- a/src/win/gui/InputMethod.cpp +++ b/src/win/gui/InputMethod.cpp @@ -104,19 +104,19 @@ CompositionClauses GetCompositionClauses(HIMC imm_context, int target_start, return result; } -std::u16string GetString(HIMC imm_context) { +String GetString(HIMC imm_context) { LONG string_size = ::ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0); - std::u16string result((string_size / sizeof(char16_t)), 0); + String result((string_size / sizeof(char16_t)), 0); ::ImmGetCompositionString(imm_context, GCS_COMPSTR, result.data(), string_size); return result; } -std::u16string GetResultString(HIMC imm_context) { +String GetResultString(HIMC imm_context) { LONG string_size = ::ImmGetCompositionString(imm_context, GCS_RESULTSTR, NULL, 0); - std::u16string result((string_size / sizeof(char16_t)), 0); + String result((string_size / sizeof(char16_t)), 0); ::ImmGetCompositionString(imm_context, GCS_RESULTSTR, result.data(), string_size); return result; @@ -217,9 +217,7 @@ IEvent<std::nullptr_t>* WinInputMethodContext::CompositionEvent() { return &composition_event_; } -IEvent<std::u16string_view>* WinInputMethodContext::TextEvent() { - return &text_event_; -} +IEvent<StringView>* WinInputMethodContext::TextEvent() { return &text_event_; } void WinInputMethodContext::OnWindowNativeMessage( WindowNativeMessageEventArgs& args) { @@ -275,7 +273,7 @@ void WinInputMethodContext::OnWindowNativeMessage( } } -std::u16string WinInputMethodContext::GetResultString() { +String WinInputMethodContext::GetResultString() { auto himc = GetHIMC(); auto result = win::GetResultString(himc.Get()); return result; diff --git a/src/win/gui/UiApplication.cpp b/src/win/gui/UiApplication.cpp index 12480417..cb0f0a4c 100644 --- a/src/win/gui/UiApplication.cpp +++ b/src/win/gui/UiApplication.cpp @@ -99,24 +99,22 @@ std::vector<INativeWindow*> WinUiApplication::GetAllWindow() { return result; } -INativeWindow* WinUiApplication::CreateWindow(INativeWindow* parent, - CreateWindowFlag flag) { - WinNativeWindow* p = nullptr; - if (parent != nullptr) { - p = CheckPlatform<WinNativeWindow>(parent, GetPlatformId()); - } - return new WinNativeWindow(this, window_manager_->GetGeneralWindowClass(), - flag & CreateWindowFlags::NoCaptionAndBorder - ? WS_POPUP - : WS_OVERLAPPEDWINDOW, - p); +INativeWindow* WinUiApplication::CreateWindow() { + return new WinNativeWindow(this); } -cru::platform::graphics::IGraphicsFactory* WinUiApplication::GetGraphicsFactory() { +cru::platform::graphics::IGraphicsFactory* +WinUiApplication::GetGraphicsFactory() { return graph_factory_.get(); } ICursorManager* WinUiApplication::GetCursorManager() { return cursor_manager_.get(); } + +IClipboard* WinUiApplication::GetClipboard() { + // TODO: Implement this. + return nullptr; +} + } // namespace cru::platform::gui::win diff --git a/src/win/gui/Window.cpp b/src/win/gui/Window.cpp index 4c75a319..20f86aff 100644 --- a/src/win/gui/Window.cpp +++ b/src/win/gui/Window.cpp @@ -5,6 +5,7 @@ #include "cru/platform/Check.hpp" #include "cru/platform/gui/Base.hpp" #include "cru/platform/gui/DebugFlags.hpp" +#include "cru/platform/gui/Window.hpp" #include "cru/win/graphics/direct/WindowPainter.hpp" #include "cru/win/gui/Cursor.hpp" #include "cru/win/gui/Exception.hpp" @@ -13,104 +14,129 @@ #include "cru/win/gui/UiApplication.hpp" #include "cru/win/gui/WindowClass.hpp" -#include <imm.h> #include <windowsx.h> -#include <memory> namespace cru::platform::gui::win { -WinNativeWindow::WinNativeWindow(WinUiApplication* application, - WindowClass* window_class, DWORD window_style, - WinNativeWindow* parent) - : application_(application), 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."); - } +namespace { +inline int DipToPixel(const float dip, const float dpi) { + return static_cast<int>(dip * dpi / 96.0f); +} - const auto window_manager = application->GetWindowManager(); +inline float PixelToDip(const int pixel, const float dpi) { + return static_cast<float>(pixel) * 96.0f / dpi; +} - 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); +DWORD CalcWindowStyle(WindowStyleFlag flag) { + return flag & WindowStyleFlags::NoCaptionAndBorder ? WS_POPUP + : WS_OVERLAPPEDWINDOW; +} - if (hwnd_ == nullptr) - throw Win32Error(::GetLastError(), "Failed to create window."); +Rect CalcWindowRectFromClient(const Rect& rect, WindowStyleFlag style_flag, + float dpi) { + RECT r; + r.left = DipToPixel(rect.left, dpi); + r.top = DipToPixel(rect.top, dpi); + r.right = DipToPixel(rect.GetRight(), dpi); + r.bottom = DipToPixel(rect.GetBottom(), dpi); + if (!AdjustWindowRectEx(&r, CalcWindowStyle(style_flag), FALSE, 0)) + throw Win32Error(::GetLastError(), "Failed to invoke AdjustWindowRectEx."); - auto dpi = ::GetDpiForWindow(hwnd_); - if (dpi == 0) - throw Win32Error(::GetLastError(), "Failed to get dpi of window."); - dpi_ = static_cast<float>(dpi); - log::Debug(u"Dpi of window is {}.", dpi_); + Rect result = + Rect::FromVertices(PixelToDip(r.left, dpi), PixelToDip(r.top, dpi), + PixelToDip(r.right, dpi), PixelToDip(r.bottom, dpi)); + return result; +} - window_manager->RegisterWindow(hwnd_, this); +} // namespace - SetCursor(application->GetCursorManager()->GetSystemCursor( - cru::platform::gui::SystemCursorType::Arrow)); +WinNativeWindow::WinNativeWindow(WinUiApplication* application) + : application_(application) { + Expects(application); // application can't be null. +} - window_render_target_ = - std::make_unique<graphics::win::direct::D2DWindowRenderTarget>( - application->GetDirectFactory(), hwnd_); - window_render_target_->SetDpi(dpi_, dpi_); +WinNativeWindow::~WinNativeWindow() { Close(); } - input_method_context_ = std::make_unique<WinInputMethodContext>(this); - input_method_context_->DisableIME(); +void WinNativeWindow::Close() { + if (hwnd_) ::DestroyWindow(hwnd_); } -WinNativeWindow::~WinNativeWindow() { - if (!sync_flag_) { - sync_flag_ = true; - Close(); +void WinNativeWindow::SetStyleFlag(WindowStyleFlag flag) { + if (flag == style_flag_) return; + + style_flag_ = flag; + if (hwnd_) { + SetWindowLongPtrW(hwnd_, GWL_STYLE, + static_cast<LONG_PTR>(CalcWindowStyle(style_flag_))); } } -void WinNativeWindow::Close() { ::DestroyWindow(hwnd_); } +void WinNativeWindow::SetVisibility(WindowVisibilityType visibility) { + if (visibility == visibility_) return; + visibility_ = visibility; -bool WinNativeWindow::IsVisible() { return ::IsWindowVisible(hwnd_); } + if (!hwnd_) { + RecreateWindow(); + } -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(PixelToDip(pixel_rect.right), PixelToDip(pixel_rect.bottom)); + if (visibility == WindowVisibilityType::Show) { + ShowWindow(hwnd_, SW_SHOWNORMAL); + } else if (visibility == WindowVisibilityType::Hide) { + ShowWindow(hwnd_, SW_HIDE); + } else if (visibility == WindowVisibilityType::Minimize) { + ShowWindow(hwnd_, SW_MINIMIZE); + } } +Size WinNativeWindow::GetClientSize() { return GetClientRect().GetSize(); } + void WinNativeWindow::SetClientSize(const Size& size) { - const auto window_style = - static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = - static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); + client_rect_.SetSize(size); - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = DipToPixel(size.width); - rect.bottom = DipToPixel(size.height); - if (!AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style)) - throw Win32Error(::GetLastError(), "Failed to invoke AdjustWindowRectEx."); + if (hwnd_) { + RECT rect = + DipToPixel(CalcWindowRectFromClient(client_rect_, style_flag_, dpi_)); - 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."); + 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."); +Rect WinNativeWindow::GetClientRect() { return client_rect_; } - return Rect::FromVertices(PixelToDip(rect.left), PixelToDip(rect.top), - PixelToDip(rect.right), PixelToDip(rect.bottom)); +void WinNativeWindow::SetClientRect(const Rect& rect) { + client_rect_ = rect; + + if (hwnd_) { + RECT rect = + DipToPixel(CalcWindowRectFromClient(client_rect_, style_flag_, dpi_)); + + 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() { + if (hwnd_) { + RECT rect; + if (!::GetWindowRect(hwnd_, &rect)) + throw Win32Error(::GetLastError(), "Failed to invoke GetWindowRect."); + + return Rect::FromVertices(PixelToDip(rect.left), PixelToDip(rect.top), + PixelToDip(rect.right), PixelToDip(rect.bottom)); + } else { + return {}; + } } void WinNativeWindow::SetWindowRect(const Rect& rect) { - if (!SetWindowPos(hwnd_, nullptr, DipToPixel(rect.left), DipToPixel(rect.top), - DipToPixel(rect.GetRight()), DipToPixel(rect.GetBottom()), - SWP_NOZORDER)) - throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); + if (hwnd_) { + if (!SetWindowPos(hwnd_, nullptr, DipToPixel(rect.left), + DipToPixel(rect.top), DipToPixel(rect.GetRight()), + DipToPixel(rect.GetBottom()), SWP_NOZORDER)) + throw Win32Error(::GetLastError(), "Failed to invoke SetWindowPos."); + } } Point WinNativeWindow::GetMousePosition() { @@ -154,6 +180,8 @@ void WinNativeWindow::SetCursor(std::shared_ptr<ICursor> cursor) { cursor_ = CheckPlatform<WinCursor>(cursor, GetPlatformId()); + if (hwnd_) return; + if (!::SetClassLongPtrW(hwnd_, GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor_->GetHandle()))) { log::TagWarn(log_tag, @@ -163,7 +191,7 @@ void WinNativeWindow::SetCursor(std::shared_ptr<ICursor> cursor) { return; } - if (!IsVisible()) return; + if (GetVisibility() != WindowVisibilityType::Show) return; auto lg = [](const std::u16string_view& reason) { log::TagWarn( @@ -361,19 +389,48 @@ bool WinNativeWindow::HandleNativeWindowMessage(HWND hwnd, UINT msg, RECT WinNativeWindow::GetClientRectPixel() { RECT rect; - if (!GetClientRect(hwnd_, &rect)) + if (!::GetClientRect(hwnd_, &rect)) throw Win32Error(::GetLastError(), "Failed to invoke GetClientRect."); return rect; } +void WinNativeWindow::RecreateWindow() { + const auto window_manager = application_->GetWindowManager(); + auto window_class = window_manager->GetGeneralWindowClass(); + + hwnd_ = CreateWindowExW( + 0, window_class->GetName(), L"", CalcWindowStyle(style_flag_), + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + parent_window_ == nullptr ? nullptr : parent_window_->GetWindowHandle(), + nullptr, application_->GetInstanceHandle(), nullptr); + + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); + + auto dpi = ::GetDpiForWindow(hwnd_); + if (dpi == 0) + throw Win32Error(::GetLastError(), "Failed to get dpi of window."); + dpi_ = static_cast<float>(dpi); + log::Debug(u"Dpi of window is {}.", dpi_); + + window_manager->RegisterWindow(hwnd_, this); + + SetCursor(application_->GetCursorManager()->GetSystemCursor( + cru::platform::gui::SystemCursorType::Arrow)); + + window_render_target_ = + std::make_unique<graphics::win::direct::D2DWindowRenderTarget>( + application_->GetDirectFactory(), hwnd_); + window_render_target_->SetDpi(dpi_, dpi_); + + input_method_context_ = std::make_unique<WinInputMethodContext>(this); + input_method_context_->DisableIME(); +} + void WinNativeWindow::OnDestroyInternal() { destroy_event_.Raise(nullptr); application_->GetWindowManager()->UnregisterWindow(hwnd_); hwnd_ = nullptr; - if (!sync_flag_) { - sync_flag_ = true; - delete this; - } } void WinNativeWindow::OnPaintInternal() { @@ -399,7 +456,7 @@ void WinNativeWindow::OnSetFocusInternal() { void WinNativeWindow::OnKillFocusInternal() { has_focus_ = false; - focus_event_.Raise(FocusChangeType::Lost); + focus_event_.Raise(FocusChangeType::Lose); } void WinNativeWindow::OnMouseMoveInternal(const POINT point) { diff --git a/src/win/gui/WindowManager.cpp b/src/win/gui/WindowManager.cpp index 4e84e967..31b868b9 100644 --- a/src/win/gui/WindowManager.cpp +++ b/src/win/gui/WindowManager.cpp @@ -37,7 +37,8 @@ void WindowManager::UnregisterWindow(HWND hwnd) { const auto find_result = window_map_.find(hwnd); Expects(find_result != window_map_.end()); // The hwnd is not in the map. window_map_.erase(find_result); - if (window_map_.empty()) application_->RequestQuit(0); + if (window_map_.empty() && application_->IsQuitOnAllWindowClosed()) + application_->RequestQuit(0); } WinNativeWindow* WindowManager::FromHandle(HWND hwnd) { |