From 5c805e494425a88da1813902b1ad8a1ab351e30d Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 5 Jul 2020 23:06:02 +0800 Subject: ... --- demos/input_method/main.cpp | 20 +-- demos/main/main.cpp | 8 +- drafts/String.cpp | 129 ++++++++++++++++ drafts/String.hpp | 75 +++++++++ include/cru/common/Base.hpp | 2 +- include/cru/common/Logger.hpp | 37 +---- include/cru/common/PreConfig.hpp | 1 + include/cru/common/StringUtil.hpp | 79 ++++++++-- include/cru/platform/Check.hpp | 10 +- include/cru/platform/Resource.hpp | 2 +- include/cru/platform/graph/Factory.hpp | 4 +- include/cru/platform/graph/TextLayout.hpp | 6 +- include/cru/platform/native/InputMethod.hpp | 21 +-- include/cru/ui/ClickDetector.hpp | 2 +- include/cru/ui/Control.hpp | 2 +- include/cru/ui/UiEvent.hpp | 6 +- include/cru/ui/UiHost.hpp | 2 +- include/cru/ui/Window.hpp | 4 +- include/cru/ui/controls/Button.hpp | 4 +- include/cru/ui/controls/Container.hpp | 4 +- include/cru/ui/controls/FlexLayout.hpp | 4 +- include/cru/ui/controls/StackLayout.hpp | 4 +- include/cru/ui/controls/TextBlock.hpp | 8 +- include/cru/ui/controls/TextBox.hpp | 4 +- include/cru/ui/render/BorderRenderObject.hpp | 2 +- include/cru/ui/render/FlexLayoutRenderObject.hpp | 2 +- include/cru/ui/render/LayoutHelper.hpp | 4 +- include/cru/ui/render/RenderObject.hpp | 2 +- include/cru/ui/render/StackLayoutRenderObject.hpp | 2 +- include/cru/ui/render/TextRenderObject.hpp | 6 +- include/cru/win/Exception.hpp | 9 +- include/cru/win/String.hpp | 75 --------- include/cru/win/graph/direct/Factory.hpp | 4 +- include/cru/win/graph/direct/Font.hpp | 3 +- include/cru/win/graph/direct/Resource.hpp | 4 +- include/cru/win/graph/direct/TextLayout.hpp | 11 +- include/cru/win/native/Cursor.hpp | 2 +- include/cru/win/native/GodWindow.hpp | 2 +- include/cru/win/native/InputMethod.hpp | 10 +- include/cru/win/native/Resource.hpp | 4 +- include/cru/win/native/Window.hpp | 2 +- src/common/Logger.cpp | 34 ++-- src/common/StringUtil.cpp | 179 ++++++++++++++++------ src/ui/ClickDetector.cpp | 14 +- src/ui/RoutedEventDispatch.hpp | 21 ++- src/ui/UiHost.cpp | 17 +- src/ui/UiManager.cpp | 2 +- src/ui/Window.cpp | 2 +- src/ui/controls/TextBlock.cpp | 4 +- src/ui/controls/TextControlService.hpp | 15 +- src/ui/render/BorderRenderObject.cpp | 10 +- src/ui/render/FlexLayoutRenderObject.cpp | 8 +- src/ui/render/LayoutHelper.cpp | 4 +- src/ui/render/RenderObject.cpp | 8 +- src/ui/render/ScrollRenderObject.cpp | 1 + src/ui/render/StackLayoutRenderObject.cpp | 8 +- src/ui/render/TextRenderObject.cpp | 10 +- src/win/CMakeLists.txt | 2 - src/win/DebugLogger.hpp | 6 +- src/win/Exception.cpp | 12 +- src/win/String.cpp | 129 ---------------- src/win/graph/direct/Factory.cpp | 10 +- src/win/graph/direct/Font.cpp | 12 +- src/win/graph/direct/TextLayout.cpp | 51 +++--- src/win/native/Cursor.cpp | 2 +- src/win/native/GodWindow.cpp | 2 +- src/win/native/InputMethod.cpp | 72 ++++----- src/win/native/Window.cpp | 23 ++- test/CMakeLists.txt | 2 +- test/common/CMakeLists.txt | 6 + test/common/StringUtilTest.cpp | 53 +++++++ test/win/CMakeLists.txt | 6 - test/win/String.cpp | 53 ------- 73 files changed, 721 insertions(+), 639 deletions(-) create mode 100644 drafts/String.cpp create mode 100644 drafts/String.hpp delete mode 100644 include/cru/win/String.hpp delete mode 100644 src/win/String.cpp create mode 100644 test/common/CMakeLists.txt create mode 100644 test/common/StringUtilTest.cpp delete mode 100644 test/win/CMakeLists.txt delete mode 100644 test/win/String.cpp diff --git a/demos/input_method/main.cpp b/demos/input_method/main.cpp index b109f5e7..215eba16 100644 --- a/demos/input_method/main.cpp +++ b/demos/input_method/main.cpp @@ -31,19 +31,19 @@ int main() { auto target_clause_brush = graph_factory->CreateSolidColorBrush(); target_clause_brush->SetColor(colors::blue); - std::shared_ptr font = graph_factory->CreateFont("等线", 30); + std::shared_ptr font = graph_factory->CreateFont(u"等线", 30); float window_width = 10000; auto prompt_text_layout = graph_factory->CreateTextLayout(font, - "Alt+F1: Enable IME\n" - "Alt+F2: Disable IME\n" - "Alt+F3: Complete composition.\n" - "Alt+F4: Cancel composition."); + u"Alt+F1: Enable IME\n" + u"Alt+F2: Disable IME\n" + u"Alt+F3: Complete composition.\n" + u"Alt+F4: Cancel composition."); std::optional optional_composition_text; - std::string committed_text; + std::u16string committed_text; window->ResizeEvent()->AddHandler( [&prompt_text_layout, &window_width](const Size& size) { @@ -60,9 +60,9 @@ int main() { const auto anchor_y = prompt_text_layout->GetTextBounds().height; auto text_layout = graph_factory->CreateTextLayout( - font, - committed_text + - (optional_composition_text ? optional_composition_text->text : "")); + font, committed_text + (optional_composition_text + ? optional_composition_text->text + : u"")); text_layout->SetMaxWidth(window_width); if (optional_composition_text) { @@ -123,7 +123,7 @@ int main() { }); input_method_context->TextEvent()->AddHandler( - [window, &committed_text](const std::string_view& c) { + [window, &committed_text](const std::u16string_view& c) { committed_text += c; window->RequestRepaint(); }); diff --git a/demos/main/main.cpp b/demos/main/main.cpp index 832ef75e..a0d4cd88 100644 --- a/demos/main/main.cpp +++ b/demos/main/main.cpp @@ -34,15 +34,15 @@ int main() { const auto button = Button::Create(); const auto text_block1 = TextBlock::Create(); - text_block1->SetText("Hello World!"); + text_block1->SetText(u"Hello World!"); button->SetChild(text_block1); flex_layout->AddChild(button, 0); const auto text_block2 = TextBlock::Create(); - text_block2->SetText("Hello World!"); + text_block2->SetText(u"Hello World!"); const auto text_block3 = TextBlock::Create(); - text_block3->SetText("Overlapped text"); + text_block3->SetText(u"Overlapped text"); const auto stack_layout = StackLayout::Create(); stack_layout->AddChild(text_block2, 0); @@ -50,7 +50,7 @@ int main() { flex_layout->AddChild(stack_layout, 1); const auto text_block4 = TextBlock::Create(); - text_block4->SetText("Hello World!!!"); + text_block4->SetText(u"Hello World!!!"); flex_layout->AddChild(text_block4, 2); window->GetUiHost()->GetNativeWindowResolver()->Resolve()->SetVisible(true); diff --git a/drafts/String.cpp b/drafts/String.cpp new file mode 100644 index 00000000..eb585523 --- /dev/null +++ b/drafts/String.cpp @@ -0,0 +1,129 @@ +#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 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."); + } +} + +Index IndexUtf8ToUtf16(const std::string_view& utf8_string, Index utf8_index, + const std::wstring_view& utf16_string) { + if (utf8_index >= static_cast(utf8_string.length())) + return utf16_string.length(); + + Index cp_index = 0; + Utf8Iterator iter{utf8_string}; + while (iter.CurrentPosition() <= utf8_index) { + iter.Next(); + cp_index++; + } + + Utf16Iterator result_iter{utf16_string}; + for (Index i = 0; i < cp_index - 1; i++) { + if (result_iter.Next() == k_code_point_end) break; + } + + return result_iter.CurrentPosition(); +} + +Index IndexUtf16ToUtf8(const std::wstring_view& utf16_string, Index utf16_index, + const std::string_view& utf8_string) { + if (utf16_index >= static_cast(utf16_string.length())) + return utf8_string.length(); + + Index cp_index = 0; + Utf16Iterator iter{utf16_string}; + while (iter.CurrentPosition() <= utf16_index) { + iter.Next(); + cp_index++; + } + + Utf8Iterator result_iter{utf8_string}; + for (Index 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/drafts/String.hpp b/drafts/String.hpp new file mode 100644 index 00000000..ac07f57b --- /dev/null +++ b/drafts/String.hpp @@ -0,0 +1,75 @@ +/* +Because the text encoding problem on Windows, here I write some functions +related to text encoding. The utf-8 and utf-16 conversion function is provided +by win32 api. However win32 api does not provide any function about charactor +iteration or index by code point. (At least I haven't found.) I don't use icu +because it is not easy to build it on Windows and the bundled version in Windows +(https://docs.microsoft.com/en-us/windows/win32/intl/international-components-for-unicode--icu-) +is only available after Windows 10 Creators Update. + +Luckily, both utf-8 and utf-16 encoding are easy to learn and program with if we +only do simple iteration rather than do much sophisticated work about +complicated error situations. (And I learn the internal of the encoding by the +way.) +*/ + +#pragma once +#include "WinPreConfig.hpp" + +#include "cru/common/StringUtil.hpp" + +#include +#include +#include +#include + +namespace cru::platform::win { +std::string ToUtf8String(const std::wstring_view& string); +std::wstring ToUtf16String(const std::string_view& string); + +inline bool IsSurrogatePair(wchar_t c) { return c >= 0xD800 && c <= 0xDFFF; } + +inline bool IsSurrogatePairLeading(wchar_t c) { + return c >= 0xD800 && c <= 0xDBFF; +} + +inline bool IsSurrogatePairTrailing(wchar_t c) { + return c >= 0xDC00 && c <= 0xDFFF; +} + +class Utf16Iterator : public Object { + static_assert( + sizeof(wchar_t) == 2, + "Emmm, according to my knowledge, wchar_t should be 2-length on " + "Windows. If not, Utf16 will be broken."); + + public: + Utf16Iterator(const std::wstring_view& string) : string_(string) {} + + CRU_DEFAULT_COPY(Utf16Iterator) + CRU_DEFAULT_MOVE(Utf16Iterator) + + ~Utf16Iterator() = default; + + public: + void SetToHead() { position_ = 0; } + + // Advance current position and get next code point. Return k_code_point_end + // if there is no next code unit(point). Throw TextEncodeException if decoding + // fails. + CodePoint Next(); + + Index CurrentPosition() const { return this->position_; } + + private: + std::wstring_view string_; + Index position_ = 0; +}; + +Index IndexUtf8ToUtf16(const std::string_view& utf8_string, Index utf8_index, + const std::wstring_view& utf16_string); + +Index IndexUtf16ToUtf8(const std::wstring_view& utf16_string, Index utf16_index, + const std::string_view& utf8_string); + +} // namespace cru::platform::win diff --git a/include/cru/common/Base.hpp b/include/cru/common/Base.hpp index 409c2b0e..93d6f9a6 100644 --- a/include/cru/common/Base.hpp +++ b/include/cru/common/Base.hpp @@ -47,5 +47,5 @@ using Index = gsl::index; #define CRU_DEFINE_CLASS_LOG_TAG(tag) \ private: \ - constexpr static std::string_view log_tag = tag; + constexpr static std::u16string_view log_tag = tag; } // namespace cru diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp index f76e4626..f83ba8dc 100644 --- a/include/cru/common/Logger.hpp +++ b/include/cru/common/Logger.hpp @@ -15,30 +15,7 @@ enum class LogLevel { Debug, Info, Warn, Error }; struct ILogSource : virtual Interface { // Write the string s. LogLevel is just a helper. It has no effect on the // content to write. - virtual void Write(LogLevel level, std::string_view s) = 0; -}; - -class StdioLogSource : public virtual ILogSource { - public: - StdioLogSource() = default; - - CRU_DELETE_COPY(StdioLogSource) - CRU_DELETE_MOVE(StdioLogSource) - - ~StdioLogSource() override = default; - - void Write(LogLevel level, std::string_view s) override { - // TODO: Emmm... Since it is buggy to use narrow char in UTF-8 on Windows. I - // think this implementation might be broken. (However, I didn't test it.) - // Maybe, I should detect Windows and use wide char (And I didn't test this - // either) or other more complicated implementation. Currently, I settled - // with this. - if (level == LogLevel::Error) { - std::cerr << s; - } else { - std::cout << s; - } - } + virtual void Write(LogLevel level, const std::u16string& s) = 0; }; class Logger : public Object { @@ -58,8 +35,8 @@ class Logger : public Object { void RemoveSource(ILogSource* source); public: - void Log(LogLevel level, std::string_view s); - void Log(LogLevel level, std::string_view tag, std::string_view s); + void Log(LogLevel level, std::u16string_view s); + void Log(LogLevel level, std::u16string_view tag, std::u16string_view s); public: std::list> sources_; @@ -92,7 +69,7 @@ void Error(TArgs&&... args) { } template -void TagDebug([[maybe_unused]] std::string_view tag, +void TagDebug([[maybe_unused]] std::u16string_view tag, [[maybe_unused]] TArgs&&... args) { #ifdef CRU_DEBUG Logger::GetInstance()->Log(LogLevel::Debug, tag, @@ -101,19 +78,19 @@ void TagDebug([[maybe_unused]] std::string_view tag, } template -void TagInfo(std::string_view tag, TArgs&&... args) { +void TagInfo(std::u16string_view tag, TArgs&&... args) { Logger::GetInstance()->Log(LogLevel::Info, tag, fmt::format(std::forward(args)...)); } template -void TagWarn(std::string_view tag, TArgs&&... args) { +void TagWarn(std::u16string_view tag, TArgs&&... args) { Logger::GetInstance()->Log(LogLevel::Warn, tag, fmt::format(std::forward(args)...)); } template -void TagError(std::string_view tag, TArgs&&... args) { +void TagError(std::u16string_view tag, TArgs&&... args) { Logger::GetInstance()->Log(LogLevel::Error, tag, fmt::format(std::forward(args)...)); } diff --git a/include/cru/common/PreConfig.hpp b/include/cru/common/PreConfig.hpp index 4bccef1d..802f17f8 100644 --- a/include/cru/common/PreConfig.hpp +++ b/include/cru/common/PreConfig.hpp @@ -6,3 +6,4 @@ #endif #define _CRT_SECURE_NO_WARNINGS +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp index a44ae6b4..714f1d49 100644 --- a/include/cru/common/StringUtil.hpp +++ b/include/cru/common/StringUtil.hpp @@ -3,37 +3,90 @@ namespace cru { using CodePoint = std::int32_t; -constexpr CodePoint k_code_point_end = -1; +constexpr CodePoint k_invalid_code_point = -1; class TextEncodeException : public std::runtime_error { public: using runtime_error::runtime_error; }; -class Utf8Iterator : public Object { +inline bool IsSurrogatePair(char16_t c) { return c >= 0xD800 && c <= 0xDFFF; } + +inline bool IsSurrogatePairLeading(char16_t c) { + return c >= 0xD800 && c <= 0xDBFF; +} + +inline bool IsSurrogatePairTrailing(char16_t c) { + return c >= 0xDC00 && c <= 0xDFFF; +} + +class Utf16Iterator : public Object { public: - explicit Utf8Iterator(const std::string_view& string) : string_(string) {} - Utf8Iterator(const std::string_view& string, Index position) - : string_(string), position_(position) {} + explicit Utf16Iterator(std::u16string_view string) + : string_(std::move(string)) {} + Utf16Iterator(std::u16string_view string, Index position) + : string_(std::move(string)), position_(position) {} - CRU_DEFAULT_COPY(Utf8Iterator) - CRU_DEFAULT_MOVE(Utf8Iterator) + CRU_DEFAULT_COPY(Utf16Iterator) + CRU_DEFAULT_MOVE(Utf16Iterator) - ~Utf8Iterator() = default; + ~Utf16Iterator() = default; public: - void SetToHead() { position_ = 0; } + void SetPositionToHead() { position_ = 0; } void SetPosition(Index position) { position_ = position; } - // Advance current position and get next code point. Return k_code_point_end - // if there is no next code unit(point). Throw TextEncodeException if decoding - // fails. + // Backward current position and get previous code point. Return + // k_invalid_code_point if reach head. Throw TextEncodeException if encounter + // encoding problem. + CodePoint Previous(); + + // Advance current position and get next code point. Return + // k_invalid_code_point if reach tail. Throw TextEncodeException if encounter + // encoding problem. CodePoint Next(); Index CurrentPosition() const { return this->position_; } private: - std::string_view string_; + std::u16string_view string_; Index position_ = 0; }; + +Index PreviousIndex(std::u16string_view string, Index current); +Index NextIndex(std::u16string_view string, Index current); + +std::string ToUtf8(const std::u16string& s); +inline std::string ToUtf8(std::u16string_view s) { + return ToUtf8(std::u16string{s}); +} + +// class Utf8Iterator : public Object { +// public: +// explicit Utf8Iterator(const std::string_view& string) : string_(string) {} +// Utf8Iterator(const std::string_view& string, Index position) +// : string_(string), position_(position) {} + +// CRU_DEFAULT_COPY(Utf8Iterator) +// CRU_DEFAULT_MOVE(Utf8Iterator) + +// ~Utf8Iterator() = default; + +// public: +// void SetToHead() { position_ = 0; } +// void SetPosition(Index position) { position_ = position; } + +// // Advance current position and get next code point. Return +// k_invalid_code_point +// // if there is no next code unit(point). Throw TextEncodeException if +// decoding +// // fails. +// CodePoint Next(); + +// Index CurrentPosition() const { return this->position_; } + +// private: +// std::string_view string_; +// Index position_ = 0; +// }; } // namespace cru diff --git a/include/cru/platform/Check.hpp b/include/cru/platform/Check.hpp index f4bbcfe8..d3180582 100644 --- a/include/cru/platform/Check.hpp +++ b/include/cru/platform/Check.hpp @@ -2,6 +2,8 @@ #include "Exception.hpp" #include "Resource.hpp" +#include "cru/common/StringUtil.hpp" + #include #include #include @@ -9,14 +11,14 @@ namespace cru::platform { template TTarget* CheckPlatform(INativeResource* resource, - const std::string_view& target_platform) { + const std::u16string_view& target_platform) { Expects(resource); const auto result = dynamic_cast(resource); if (result == nullptr) { throw UnsupportPlatformException(fmt::format( "Try to convert resource to target platform failed. Platform id of " "resource to convert: {} . Target platform id: {} .", - resource->GetPlatformId(), target_platform)); + ToUtf8(resource->GetPlatformId()), ToUtf8(target_platform))); } return result; } @@ -24,7 +26,7 @@ TTarget* CheckPlatform(INativeResource* resource, template std::shared_ptr CheckPlatform( const std::shared_ptr& resource, - const std::string_view& target_platform) { + const std::u16string_view& target_platform) { static_assert(std::is_base_of_v, "TSource must be a subclass of INativeResource."); Expects(resource); @@ -33,7 +35,7 @@ std::shared_ptr CheckPlatform( throw UnsupportPlatformException(fmt::format( "Try to convert resource to target platform failed. Platform id of " "resource to convert: {} . Target platform id: {} .", - resource->GetPlatformId(), target_platform)); + ToUtf8(resource->GetPlatformId()), ToUtf8(target_platform))); } return result; } diff --git a/include/cru/platform/Resource.hpp b/include/cru/platform/Resource.hpp index 72cfaf52..7a85d9c1 100644 --- a/include/cru/platform/Resource.hpp +++ b/include/cru/platform/Resource.hpp @@ -5,6 +5,6 @@ namespace cru::platform { struct INativeResource : virtual Interface { - virtual std::string_view GetPlatformId() const = 0; + virtual std::u16string_view GetPlatformId() const = 0; }; } // namespace cru::platform diff --git a/include/cru/platform/graph/Factory.hpp b/include/cru/platform/graph/Factory.hpp index 0a425d15..b4e68f12 100644 --- a/include/cru/platform/graph/Factory.hpp +++ b/include/cru/platform/graph/Factory.hpp @@ -16,10 +16,10 @@ struct IGraphFactory : virtual INativeResource { virtual std::unique_ptr CreateGeometryBuilder() = 0; - virtual std::unique_ptr CreateFont(const std::string_view& font_family, + virtual std::unique_ptr CreateFont(std::u16string font_family, float font_size) = 0; virtual std::unique_ptr CreateTextLayout( - std::shared_ptr font, std::string text) = 0; + std::shared_ptr font, std::u16string text) = 0; }; } // namespace cru::platform::graph diff --git a/include/cru/platform/graph/TextLayout.hpp b/include/cru/platform/graph/TextLayout.hpp index 4086ac56..7dd20987 100644 --- a/include/cru/platform/graph/TextLayout.hpp +++ b/include/cru/platform/graph/TextLayout.hpp @@ -6,8 +6,8 @@ namespace cru::platform::graph { struct ITextLayout : virtual IGraphResource { - virtual std::string GetText() = 0; - virtual void SetText(std::string new_text) = 0; + virtual std::u16string GetText() = 0; + virtual void SetText(std::u16string new_text) = 0; virtual std::shared_ptr GetFont() = 0; virtual void SetFont(std::shared_ptr font) = 0; @@ -17,7 +17,7 @@ struct ITextLayout : virtual IGraphResource { virtual Rect GetTextBounds() = 0; virtual std::vector TextRangeRect(const TextRange& text_range) = 0; - virtual Point TextSinglePoint(gsl::index position, bool trailing) = 0; + virtual Point TextSinglePoint(Index position, bool trailing) = 0; virtual TextHitTestResult HitTest(const Point& point) = 0; }; } // namespace cru::platform::graph diff --git a/include/cru/platform/native/InputMethod.hpp b/include/cru/platform/native/InputMethod.hpp index 1ede15b2..1c5b287e 100644 --- a/include/cru/platform/native/InputMethod.hpp +++ b/include/cru/platform/native/InputMethod.hpp @@ -18,28 +18,11 @@ struct CompositionClause { using CompositionClauses = std::vector; struct CompositionText { - std::string text; + std::u16string text; CompositionClauses clauses; TextRange selection; }; -inline std::ostream& operator<<(std::ostream& stream, - const CompositionText& composition_text) { - stream << "text: " << composition_text.text << "\n" - << "clauses:\n"; - for (int i = 0; i < static_cast(composition_text.clauses.size()); i++) { - const auto& clause = composition_text.clauses[i]; - stream << "\t" << i << ". start:" << clause.start << " end:" << clause.end; - if (clause.target) { - stream << " target"; - } - stream << "\n"; - } - stream << "selection: position:" << composition_text.selection.position - << " count:" << composition_text.selection.count; - return stream; -} - struct IInputMethodContext : virtual INativeResource { // Return true if you should draw composition text manually. Return false if // system will take care of that for you. @@ -67,7 +50,7 @@ struct IInputMethodContext : virtual INativeResource { // Triggered every time composition text changes. virtual IEvent* CompositionEvent() = 0; - virtual IEvent* TextEvent() = 0; + virtual IEvent* TextEvent() = 0; }; struct IInputMethodManager : virtual INativeResource { diff --git a/include/cru/ui/ClickDetector.hpp b/include/cru/ui/ClickDetector.hpp index 3977fb8e..4ffe5d05 100644 --- a/include/cru/ui/ClickDetector.hpp +++ b/include/cru/ui/ClickDetector.hpp @@ -36,7 +36,7 @@ enum class ClickState { }; class ClickDetector : public Object { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::ClickDetector") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::ClickDetector") public: explicit ClickDetector(Control* control); diff --git a/include/cru/ui/Control.hpp b/include/cru/ui/Control.hpp index 347163be..bd86bc2f 100644 --- a/include/cru/ui/Control.hpp +++ b/include/cru/ui/Control.hpp @@ -22,7 +22,7 @@ class Control : public Object { ~Control() override = default; public: - virtual std::string_view GetControlType() const = 0; + virtual std::u16string_view GetControlType() const = 0; //*************** region: tree *************** public: diff --git a/include/cru/ui/UiEvent.hpp b/include/cru/ui/UiEvent.hpp index 39f26aee..5adace8a 100644 --- a/include/cru/ui/UiEvent.hpp +++ b/include/cru/ui/UiEvent.hpp @@ -212,7 +212,7 @@ class KeyEventArgs : public UiEventArgs { class CharEventArgs : public UiEventArgs { public: - CharEventArgs(Object* sender, Object* original_sender, std::string c) + CharEventArgs(Object* sender, Object* original_sender, std::u16string c) : UiEventArgs(sender, original_sender), c_(std::move(c)) {} CharEventArgs(const CharEventArgs& other) = default; CharEventArgs(CharEventArgs&& other) = default; @@ -220,9 +220,9 @@ class CharEventArgs : public UiEventArgs { CharEventArgs& operator=(CharEventArgs&& other) = default; ~CharEventArgs() override = default; - std::string GetChar() const { return c_; } + std::u16string GetChar() const { return c_; } private: - std::string c_; + std::u16string c_; }; } // namespace cru::ui::event diff --git a/include/cru/ui/UiHost.hpp b/include/cru/ui/UiHost.hpp index 1a5c6302..b1658ef6 100644 --- a/include/cru/ui/UiHost.hpp +++ b/include/cru/ui/UiHost.hpp @@ -32,7 +32,7 @@ struct AfterLayoutEventArgs {}; // 4. Delete Window when deleting_ is false and IsRetainAfterDestroy is false in // OnNativeDestroy. class UiHost : public Object, public SelfResolvable { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::UiHost") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::UiHost") public: // This will create root window render object and attach it to window. diff --git a/include/cru/ui/Window.hpp b/include/cru/ui/Window.hpp index eb2ecfbb..450ea97b 100644 --- a/include/cru/ui/Window.hpp +++ b/include/cru/ui/Window.hpp @@ -6,7 +6,7 @@ class Window final : public ContentControl { friend UiHost; public: - static constexpr std::string_view control_type = "Window"; + static constexpr std::u16string_view control_type = u"Window"; public: static Window* CreateOverlapped(); @@ -24,7 +24,7 @@ class Window final : public ContentControl { ~Window() override; public: - std::string_view GetControlType() const final; + std::u16string_view GetControlType() const final; render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/controls/Button.hpp b/include/cru/ui/controls/Button.hpp index 8a11409c..a4f727d6 100644 --- a/include/cru/ui/controls/Button.hpp +++ b/include/cru/ui/controls/Button.hpp @@ -7,7 +7,7 @@ namespace cru::ui::controls { class Button : public ContentControl { public: - static constexpr std::string_view control_type = "Button"; + static constexpr std::u16string_view control_type = u"Button"; static Button* Create() { return new Button(); } @@ -21,7 +21,7 @@ class Button : public ContentControl { Button& operator=(Button&& other) = delete; ~Button() override; - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/controls/Container.hpp b/include/cru/ui/controls/Container.hpp index e3d78365..304d402c 100644 --- a/include/cru/ui/controls/Container.hpp +++ b/include/cru/ui/controls/Container.hpp @@ -3,7 +3,7 @@ namespace cru::ui::controls { class Container : public ContentControl { - static constexpr std::string_view control_type = "Container"; + static constexpr std::u16string_view control_type = u"Container"; protected: Container(); @@ -15,7 +15,7 @@ class Container : public ContentControl { ~Container() override; public: - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/controls/FlexLayout.hpp b/include/cru/ui/controls/FlexLayout.hpp index 3d6087c2..87162569 100644 --- a/include/cru/ui/controls/FlexLayout.hpp +++ b/include/cru/ui/controls/FlexLayout.hpp @@ -4,7 +4,7 @@ namespace cru::ui::controls { class FlexLayout : public LayoutControl { public: - static constexpr std::string_view control_type = "FlexLayout"; + static constexpr std::u16string_view control_type = u"FlexLayout"; static FlexLayout* Create() { return new FlexLayout(); } @@ -18,7 +18,7 @@ class FlexLayout : public LayoutControl { FlexLayout& operator=(FlexLayout&& other) = delete; ~FlexLayout() override; - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/controls/StackLayout.hpp b/include/cru/ui/controls/StackLayout.hpp index d5998cc4..c0b95044 100644 --- a/include/cru/ui/controls/StackLayout.hpp +++ b/include/cru/ui/controls/StackLayout.hpp @@ -4,7 +4,7 @@ namespace cru::ui::controls { class StackLayout : public LayoutControl { public: - static constexpr std::string_view control_type = "StackLayout"; + static constexpr std::u16string_view control_type = u"StackLayout"; static StackLayout* Create() { return new StackLayout(); } @@ -17,7 +17,7 @@ class StackLayout : public LayoutControl { ~StackLayout() override; - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/controls/TextBlock.hpp b/include/cru/ui/controls/TextBlock.hpp index 1b1b4a5c..8a9a3bff 100644 --- a/include/cru/ui/controls/TextBlock.hpp +++ b/include/cru/ui/controls/TextBlock.hpp @@ -7,7 +7,7 @@ class TextControlService; class TextBlock : public NoChildControl { public: - static constexpr std::string_view control_type = "TextBlock"; + static constexpr std::u16string_view control_type = u"TextBlock"; static TextBlock* Create() { return new TextBlock(); } @@ -21,12 +21,12 @@ class TextBlock : public NoChildControl { TextBlock& operator=(TextBlock&& other) = delete; ~TextBlock() override; - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; - std::string GetText() const; - void SetText(std::string text); + std::u16string GetText() const; + void SetText(std::u16string text); gsl::not_null GetTextRenderObject(); render::ScrollRenderObject* GetScrollRenderObject() { return nullptr; } diff --git a/include/cru/ui/controls/TextBox.hpp b/include/cru/ui/controls/TextBox.hpp index 3d4de7c0..5976f6da 100644 --- a/include/cru/ui/controls/TextBox.hpp +++ b/include/cru/ui/controls/TextBox.hpp @@ -10,7 +10,7 @@ class TextControlService; class TextBox : public NoChildControl { public: - static constexpr std::string_view control_type = "TextBox"; + static constexpr std::u16string_view control_type = u"TextBox"; static TextBox* Create() { return new TextBox(); } @@ -23,7 +23,7 @@ class TextBox : public NoChildControl { ~TextBox() override; - std::string_view GetControlType() const final { return control_type; } + std::u16string_view GetControlType() const final { return control_type; } render::RenderObject* GetRenderObject() const override; diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp index 94e888d4..587f051a 100644 --- a/include/cru/ui/render/BorderRenderObject.hpp +++ b/include/cru/ui/render/BorderRenderObject.hpp @@ -3,7 +3,7 @@ namespace cru::ui::render { class BorderRenderObject : public RenderObject { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::render::BorderRenderObject") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::BorderRenderObject") public: BorderRenderObject(); diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp index 87a41c7e..ee29d1e4 100644 --- a/include/cru/ui/render/FlexLayoutRenderObject.hpp +++ b/include/cru/ui/render/FlexLayoutRenderObject.hpp @@ -74,7 +74,7 @@ namespace cru::ui::render { // and just fill the rest space with blank. // class FlexLayoutRenderObject : public LayoutRenderObject { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::render::FlexLayoutRenderObject") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::FlexLayoutRenderObject") public: FlexLayoutRenderObject() = default; diff --git a/include/cru/ui/render/LayoutHelper.hpp b/include/cru/ui/render/LayoutHelper.hpp index 3469ccf0..518dc5a3 100644 --- a/include/cru/ui/render/LayoutHelper.hpp +++ b/include/cru/ui/render/LayoutHelper.hpp @@ -9,6 +9,6 @@ float CalculateAnchorByAlignment(Alignment alignment, float start_point, MeasureLength StackLayoutCalculateChildMaxLength( MeasureLength parent_preferred_size, MeasureLength parent_max_size, - MeasureLength child_min_size, std::string_view log_tag, - std::string_view exceeds_message); + MeasureLength child_min_size, std::u16string_view log_tag, + std::u16string_view exceeds_message); } // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index 2e784afc..f820f029 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -37,7 +37,7 @@ namespace cru::ui::render { class RenderObject : public Object { friend WindowRenderObject; - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::render::RenderObject") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") protected: enum class ChildMode { diff --git a/include/cru/ui/render/StackLayoutRenderObject.hpp b/include/cru/ui/render/StackLayoutRenderObject.hpp index 534d7f22..303241c5 100644 --- a/include/cru/ui/render/StackLayoutRenderObject.hpp +++ b/include/cru/ui/render/StackLayoutRenderObject.hpp @@ -23,7 +23,7 @@ namespace cru::ui::render { // to min size. class StackLayoutRenderObject : public LayoutRenderObject { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::render:StackLayoutRenderObject") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render:StackLayoutRenderObject") public: StackLayoutRenderObject() = default; diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp index 77a92b4f..32d96797 100644 --- a/include/cru/ui/render/TextRenderObject.hpp +++ b/include/cru/ui/render/TextRenderObject.hpp @@ -18,7 +18,7 @@ namespace cru::ui::render { // If the result layout box is bigger than actual text box, then text is center // aligned. class TextRenderObject : public RenderObject { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::render::TextRenderObject") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::TextRenderObject") public: constexpr static float default_caret_width = 2; @@ -34,8 +34,8 @@ class TextRenderObject : public RenderObject { TextRenderObject& operator=(TextRenderObject&& other) = delete; ~TextRenderObject() override; - std::string GetText() const; - void SetText(std::string new_text); + std::u16string GetText() const; + void SetText(std::u16string new_text); std::shared_ptr GetBrush() const { return brush_; } void SetBrush(std::shared_ptr new_brush); diff --git a/include/cru/win/Exception.hpp b/include/cru/win/Exception.hpp index 234aea69..3a95aa5d 100644 --- a/include/cru/win/Exception.hpp +++ b/include/cru/win/Exception.hpp @@ -10,7 +10,7 @@ namespace cru::platform::win { class HResultError : public platform::PlatformException { public: explicit HResultError(HRESULT h_result); - explicit HResultError(HRESULT h_result, const std::string_view& message); + explicit HResultError(HRESULT h_result, std::string_view message); CRU_DEFAULT_COPY(HResultError) CRU_DEFAULT_MOVE(HResultError) @@ -27,8 +27,7 @@ inline void ThrowIfFailed(const HRESULT h_result) { if (FAILED(h_result)) throw HResultError(h_result); } -inline void ThrowIfFailed(const HRESULT h_result, - const std::string_view& message) { +inline void ThrowIfFailed(const HRESULT h_result, std::string_view message) { if (FAILED(h_result)) throw HResultError(h_result, message); } @@ -36,8 +35,8 @@ class Win32Error : public platform::PlatformException { public: // ::GetLastError is automatically called to get the error code. // The same as Win32Error(::GetLastError(), message) - explicit Win32Error(const std::string_view& message); - Win32Error(DWORD error_code, const std::string_view& message); + explicit Win32Error(std::string_view message); + Win32Error(DWORD error_code, std::string_view message); CRU_DEFAULT_COPY(Win32Error) CRU_DEFAULT_MOVE(Win32Error) diff --git a/include/cru/win/String.hpp b/include/cru/win/String.hpp deleted file mode 100644 index ac07f57b..00000000 --- a/include/cru/win/String.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -Because the text encoding problem on Windows, here I write some functions -related to text encoding. The utf-8 and utf-16 conversion function is provided -by win32 api. However win32 api does not provide any function about charactor -iteration or index by code point. (At least I haven't found.) I don't use icu -because it is not easy to build it on Windows and the bundled version in Windows -(https://docs.microsoft.com/en-us/windows/win32/intl/international-components-for-unicode--icu-) -is only available after Windows 10 Creators Update. - -Luckily, both utf-8 and utf-16 encoding are easy to learn and program with if we -only do simple iteration rather than do much sophisticated work about -complicated error situations. (And I learn the internal of the encoding by the -way.) -*/ - -#pragma once -#include "WinPreConfig.hpp" - -#include "cru/common/StringUtil.hpp" - -#include -#include -#include -#include - -namespace cru::platform::win { -std::string ToUtf8String(const std::wstring_view& string); -std::wstring ToUtf16String(const std::string_view& string); - -inline bool IsSurrogatePair(wchar_t c) { return c >= 0xD800 && c <= 0xDFFF; } - -inline bool IsSurrogatePairLeading(wchar_t c) { - return c >= 0xD800 && c <= 0xDBFF; -} - -inline bool IsSurrogatePairTrailing(wchar_t c) { - return c >= 0xDC00 && c <= 0xDFFF; -} - -class Utf16Iterator : public Object { - static_assert( - sizeof(wchar_t) == 2, - "Emmm, according to my knowledge, wchar_t should be 2-length on " - "Windows. If not, Utf16 will be broken."); - - public: - Utf16Iterator(const std::wstring_view& string) : string_(string) {} - - CRU_DEFAULT_COPY(Utf16Iterator) - CRU_DEFAULT_MOVE(Utf16Iterator) - - ~Utf16Iterator() = default; - - public: - void SetToHead() { position_ = 0; } - - // Advance current position and get next code point. Return k_code_point_end - // if there is no next code unit(point). Throw TextEncodeException if decoding - // fails. - CodePoint Next(); - - Index CurrentPosition() const { return this->position_; } - - private: - std::wstring_view string_; - Index position_ = 0; -}; - -Index IndexUtf8ToUtf16(const std::string_view& utf8_string, Index utf8_index, - const std::wstring_view& utf16_string); - -Index IndexUtf16ToUtf8(const std::wstring_view& utf16_string, Index utf16_index, - const std::string_view& utf8_string); - -} // namespace cru::platform::win diff --git a/include/cru/win/graph/direct/Factory.hpp b/include/cru/win/graph/direct/Factory.hpp index 763d4b2b..e70454f5 100644 --- a/include/cru/win/graph/direct/Factory.hpp +++ b/include/cru/win/graph/direct/Factory.hpp @@ -38,11 +38,11 @@ class DirectGraphFactory : public DirectResource, public virtual IGraphFactory { std::unique_ptr CreateGeometryBuilder() override; - std::unique_ptr CreateFont(const std::string_view& font_family, + std::unique_ptr CreateFont(std::u16string font_family, float font_size) override; std::unique_ptr CreateTextLayout(std::shared_ptr font, - std::string text) override; + std::u16string text) override; private: Microsoft::WRL::ComPtr d3d11_device_; diff --git a/include/cru/win/graph/direct/Font.hpp b/include/cru/win/graph/direct/Font.hpp index ecf9fd81..2195f3e4 100644 --- a/include/cru/win/graph/direct/Font.hpp +++ b/include/cru/win/graph/direct/Font.hpp @@ -11,7 +11,7 @@ class DWriteFont : public DirectGraphResource, public virtual IFont, public virtual IComResource { public: - DWriteFont(DirectGraphFactory* factory, const std::string_view& font_family, + DWriteFont(DirectGraphFactory* factory, std::u16string font_family, float font_size); CRU_DELETE_COPY(DWriteFont) @@ -27,6 +27,7 @@ class DWriteFont : public DirectGraphResource, float GetFontSize() override; private: + std::u16string font_family_; Microsoft::WRL::ComPtr text_format_; }; } // namespace cru::platform::graph::win::direct diff --git a/include/cru/win/graph/direct/Resource.hpp b/include/cru/win/graph/direct/Resource.hpp index d0a30dbd..6162ebd8 100644 --- a/include/cru/win/graph/direct/Resource.hpp +++ b/include/cru/win/graph/direct/Resource.hpp @@ -10,7 +10,7 @@ class DirectGraphFactory; class DirectResource : public Object, public virtual INativeResource { public: - static constexpr std::string_view k_platform_id = "Windows Direct"; + static constexpr std::u16string_view k_platform_id = u"Windows Direct"; protected: DirectResource() = default; @@ -22,7 +22,7 @@ class DirectResource : public Object, public virtual INativeResource { ~DirectResource() override = default; public: - std::string_view GetPlatformId() const final { return k_platform_id; } + std::u16string_view GetPlatformId() const final { return k_platform_id; } }; class DirectGraphResource : public DirectResource, diff --git a/include/cru/win/graph/direct/TextLayout.hpp b/include/cru/win/graph/direct/TextLayout.hpp index 40c63dbe..c53cf655 100644 --- a/include/cru/win/graph/direct/TextLayout.hpp +++ b/include/cru/win/graph/direct/TextLayout.hpp @@ -15,7 +15,7 @@ class DWriteTextLayout : public DirectGraphResource, public virtual IComResource { public: DWriteTextLayout(DirectGraphFactory* factory, std::shared_ptr font, - std::string text); + std::u16string text); CRU_DELETE_COPY(DWriteTextLayout) CRU_DELETE_MOVE(DWriteTextLayout) @@ -28,8 +28,8 @@ class DWriteTextLayout : public DirectGraphResource, } public: - std::string GetText() override; - void SetText(std::string new_text) override; + std::u16string GetText() override; + void SetText(std::u16string new_text) override; std::shared_ptr GetFont() override; void SetFont(std::shared_ptr font) override; @@ -41,12 +41,11 @@ class DWriteTextLayout : public DirectGraphResource, // Return empty vector if text_range.count is 0. Text range could be in // reverse direction, it should be normalized first in implementation. std::vector TextRangeRect(const TextRange& text_range) override; - Point TextSinglePoint(gsl::index position, bool trailing) override; + Point TextSinglePoint(Index position, bool trailing) override; TextHitTestResult HitTest(const Point& point) override; private: - std::string text_; - std::wstring w_text_; + std::u16string text_; std::shared_ptr font_; float max_width_ = std::numeric_limits::max(); float max_height_ = std::numeric_limits::max(); diff --git a/include/cru/win/native/Cursor.hpp b/include/cru/win/native/Cursor.hpp index 44a6a362..373b9170 100644 --- a/include/cru/win/native/Cursor.hpp +++ b/include/cru/win/native/Cursor.hpp @@ -7,7 +7,7 @@ namespace cru::platform::native::win { class WinCursor : public WinNativeResource, public virtual ICursor { - CRU_DEFINE_CLASS_LOG_TAG("cru::platform::native::win::WinCursor") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinCursor") public: WinCursor(HCURSOR handle, bool auto_destroy); diff --git a/include/cru/win/native/GodWindow.hpp b/include/cru/win/native/GodWindow.hpp index 0820bdb3..8b20e01f 100644 --- a/include/cru/win/native/GodWindow.hpp +++ b/include/cru/win/native/GodWindow.hpp @@ -5,7 +5,7 @@ namespace cru::platform::native::win { class GodWindow : public Object { - CRU_DEFINE_CLASS_LOG_TAG("cru::platform::native::win::GodWindow") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::GodWindow") public: explicit GodWindow(WinUiApplication* application); diff --git a/include/cru/win/native/InputMethod.hpp b/include/cru/win/native/InputMethod.hpp index 45422ace..113f460d 100644 --- a/include/cru/win/native/InputMethod.hpp +++ b/include/cru/win/native/InputMethod.hpp @@ -12,7 +12,7 @@ namespace cru::platform::native::win { class AutoHIMC : public Object { - CRU_DEFINE_CLASS_LOG_TAG("cru::platform::native::win::AutoHIMC") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::AutoHIMC") public: explicit AutoHIMC(HWND hwnd); @@ -35,7 +35,7 @@ class AutoHIMC : public Object { class WinInputMethodContext : public WinNativeResource, public virtual IInputMethodContext { - CRU_DEFINE_CLASS_LOG_TAG("cru::platform::native::win::WinInputMethodContext") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinInputMethodContext") public: WinInputMethodContext(gsl::not_null window); @@ -65,12 +65,12 @@ class WinInputMethodContext : public WinNativeResource, IEvent* CompositionEvent() override; - IEvent* TextEvent() override; + IEvent* TextEvent() override; private: void OnWindowNativeMessage(WindowNativeMessageEventArgs& args); - std::string GetResultString(); + std::u16string GetResultString(); std::optional TryGetHIMC(); @@ -82,7 +82,7 @@ class WinInputMethodContext : public WinNativeResource, Event composition_start_event_; Event composition_end_event_; Event composition_event_; - Event text_event_; + Event text_event_; }; class WinInputMethodManager : public WinNativeResource, diff --git a/include/cru/win/native/Resource.hpp b/include/cru/win/native/Resource.hpp index 7afaca0f..0de0e1a8 100644 --- a/include/cru/win/native/Resource.hpp +++ b/include/cru/win/native/Resource.hpp @@ -6,7 +6,7 @@ namespace cru::platform::native::win { class WinNativeResource : public Object, public virtual INativeResource { public: - static constexpr std::string_view k_platform_id = "Windows"; + static constexpr std::u16string_view k_platform_id = u"Windows"; protected: WinNativeResource() = default; @@ -18,6 +18,6 @@ class WinNativeResource : public Object, public virtual INativeResource { ~WinNativeResource() override = default; public: - std::string_view GetPlatformId() const final { return k_platform_id; } + std::u16string_view GetPlatformId() const final { return k_platform_id; } }; } // namespace cru::platform::native::win diff --git a/include/cru/win/native/Window.hpp b/include/cru/win/native/Window.hpp index 521a0a06..3e0b11cd 100644 --- a/include/cru/win/native/Window.hpp +++ b/include/cru/win/native/Window.hpp @@ -8,7 +8,7 @@ namespace cru::platform::native::win { class WinNativeWindow : public WinNativeResource, public virtual INativeWindow { - CRU_DEFINE_CLASS_LOG_TAG("cru::platform::native::win::WinNativeWindow") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::platform::native::win::WinNativeWindow") public: WinNativeWindow(WinUiApplication* application, WindowClass* window_class, diff --git a/src/common/Logger.cpp b/src/common/Logger.cpp index da55047a..dfa25347 100644 --- a/src/common/Logger.cpp +++ b/src/common/Logger.cpp @@ -10,7 +10,6 @@ namespace cru::log { namespace { Logger *CreateLogger() { const auto logger = new Logger(); - logger->AddSource(std::make_unique()); return logger; } } // namespace @@ -31,39 +30,43 @@ void Logger::RemoveSource(ILogSource *source) { } namespace { -std::string_view LogLevelToString(LogLevel level) { +std::u16string_view LogLevelToString(LogLevel level) { switch (level) { case LogLevel::Debug: - return "DEBUG"; + return u"DEBUG"; case LogLevel::Info: - return "INFO"; + return u"INFO"; case LogLevel::Warn: - return "WARN"; + return u"WARN"; case LogLevel::Error: - return "ERROR"; + return u"ERROR"; default: std::terminate(); } } + +std::u16string GetLogTime() { + auto time = std::time(nullptr); + auto calendar = std::localtime(&time); + return fmt::format(u"{}:{}:{}", calendar->tm_hour, calendar->tm_min, + calendar->tm_sec); +} } // namespace -void Logger::Log(LogLevel level, std::string_view s) { +void Logger::Log(LogLevel level, std::u16string_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(), + source->Write(level, fmt::format(u"[{}] {}: {}\n", GetLogTime(), LogLevelToString(level), s)); } } -void Logger::Log(LogLevel level, std::string_view tag, std::string_view s) { +void Logger::Log(LogLevel level, std::u16string_view tag, + std::u16string_view s) { #ifndef CRU_DEBUG if (level == LogLevel::Debug) { return; @@ -71,10 +74,7 @@ void Logger::Log(LogLevel level, std::string_view tag, std::string_view s) { #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(), + source->Write(level, fmt::format(u"[{}] {} {}: {}\n", GetLogTime(), LogLevelToString(level), tag, s)); } } diff --git a/src/common/StringUtil.cpp b/src/common/StringUtil.cpp index 31a5effd..0b6332ce 100644 --- a/src/common/StringUtil.cpp +++ b/src/common/StringUtil.cpp @@ -1,5 +1,7 @@ #include "cru/common/StringUtil.hpp" +#include + namespace cru { template inline std::enable_if_t, CodePoint> ExtractBits( @@ -7,63 +9,154 @@ inline std::enable_if_t, CodePoint> ExtractBits( return static_cast(n & ((1u << number_of_bit) - 1)); } -CodePoint Utf8Iterator::Next() { - if (position_ == static_cast(string_.length())) - return k_code_point_end; +CodePoint Utf16Iterator::Previous() { + if (position_ <= 0) return k_invalid_code_point; - const auto cu0 = static_cast(string_[position_++]); + const auto cu0 = static_cast(string_[--position_]); - auto read_next_folowing_code = [this]() -> CodePoint { - if (this->position_ == static_cast(string_.length())) + if (cu0 < 0xd800u || cu0 >= 0xe000u) { // 1-length code point + return static_cast(cu0); + } else if (cu0 < 0xdc00u || cu0 > 0xdfffu) { // 2-length code point + if (position_ <= 0) { throw TextEncodeException( - "Unexpected end when read continuing byte of multi-byte code point."); + "Unexpected end when reading first code unit of surrogate pair " + "during backward."); + } + const auto cu1 = static_cast(string_[--position_]); #ifdef CRU_DEBUG - const auto u = static_cast(string_[position_]); - if (!(u & (1u << 7)) || (u & (1u << 6))) { + if (cu1 <= 0xdbffu) { throw TextEncodeException( - "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " - "multi-byte code point."); + "Unexpected bad-range first code unit of surrogate pair during " + "backward."); } #endif - return ExtractBits(string_[position_++]); - }; + const auto s0 = ExtractBits(cu1) << 10; + const auto s1 = ExtractBits(cu0); - 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 + return s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-range second code unit of surrogate pair during " + "backward."); + } +} - 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 { +CodePoint Utf16Iterator::Next() { + if (position_ >= static_cast(string_.length())) + return k_invalid_code_point; + + 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 bad-format (0b10xxxxxx) begin byte of a code point."); + "Unexpected end when reading second code unit of surrogate pair " + "during forward."); } + 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 during " + "forward."); + } +#endif + + const auto s0 = ExtractBits(cu0) << 10; + const auto s1 = ExtractBits(cu1); + + return s0 + s1 + 0x10000; + } else { - return static_cast(cu0); + throw TextEncodeException( + "Unexpected bad-format first code unit of surrogate pair during " + "forward."); } } + +Index PreviousIndex(std::u16string_view string, Index current) { + Utf16Iterator iterator{string, current}; + iterator.Previous(); + return iterator.CurrentPosition(); +} + +Index NextIndex(std::u16string_view string, Index current) { + Utf16Iterator iterator{string, current}; + iterator.Next(); + return iterator.CurrentPosition(); +} + +std::string ToUtf8(const std::u16string& s) { + // TODO: Implement this by myself. + return std::wstring_convert, char16_t>{} + .to_bytes(s); +} + +// CodePoint Utf8Iterator::Next() { +// if (position_ == static_cast(string_.length())) +// return k_invalid_code_point; + +// 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); +// } +// } + } // namespace cru diff --git a/src/ui/ClickDetector.cpp b/src/ui/ClickDetector.cpp index 93e6f303..09f208cd 100644 --- a/src/ui/ClickDetector.cpp +++ b/src/ui/ClickDetector.cpp @@ -45,7 +45,7 @@ ClickDetector::ClickDetector(Control* control) { this->state_ == ClickState::Hover) { if (!this->control_->CaptureMouse()) { log::TagDebug(log_tag, - "Failed to capture mouse when begin click."); + u"Failed to capture mouse when begin click."); return; } this->down_point_ = args.GetPoint(); @@ -107,21 +107,21 @@ void ClickDetector::SetTriggerButton(MouseButton trigger_button) { void ClickDetector::SetState(ClickState state) { #ifdef CRU_DEBUG - auto to_string = [](ClickState state) -> std::string_view { + auto to_string = [](ClickState state) -> std::u16string_view { switch (state) { case ClickState::None: - return "None"; + return u"None"; case ClickState::Hover: - return "Hover"; + return u"Hover"; case ClickState::Press: - return "Press"; + return u"Press"; case ClickState::PressInactive: - return "PressInvactive"; + return u"PressInvactive"; default: UnreachableCode(); } }; - log::TagDebug(log_tag, "Click state changed, new state: {}.", + log::TagDebug(log_tag, u"Click state changed, new state: {}.", to_string(state)); #endif diff --git a/src/ui/RoutedEventDispatch.hpp b/src/ui/RoutedEventDispatch.hpp index 5ff21a74..9337e9ec 100644 --- a/src/ui/RoutedEventDispatch.hpp +++ b/src/ui/RoutedEventDispatch.hpp @@ -20,7 +20,7 @@ namespace cru::ui { // "original_sender", which is unchanged. And "args" will be perfectly forwarded // as the rest arguments. template -void DispatchEvent(const std::string_view& event_name, +void DispatchEvent(const std::u16string_view& event_name, Control* const original_sender, event::RoutedEvent* (Control::*event_ptr)(), Control* const last_receiver, Args&&... args) { @@ -30,7 +30,7 @@ void DispatchEvent(const std::string_view& event_name, #ifdef CRU_DEBUG bool do_log = true; - if (event_name == "MouseMove") do_log = false; + if (event_name == u"MouseMove") do_log = false; #endif if (original_sender == last_receiver) { @@ -56,14 +56,14 @@ void DispatchEvent(const std::string_view& event_name, #ifdef CRU_DEBUG if (do_log) { - std::string log = "Dispatch routed event "; + std::u16string log = u"Dispatch routed event "; log += event_name; - log += ". Path (parent first): "; + log += u". Path (parent first): "; auto i = receive_list.crbegin(); const auto end = --receive_list.crend(); for (; i != end; ++i) { log += (*i)->GetControlType(); - log += " -> "; + log += u" -> "; } log += (*i)->GetControlType(); log::Debug(log); @@ -89,8 +89,8 @@ void DispatchEvent(const std::string_view& event_name, #ifdef CRU_DEBUG if (do_log) log::Debug( - "Routed event is short-circuit in TUNNEL at {}-st control (count " - "from parent).", + u"Routed event is short-circuit in TUNNEL at {}-st control (count " + u"from parent).", count); #endif break; @@ -110,9 +110,8 @@ void DispatchEvent(const std::string_view& event_name, #ifdef CRU_DEBUG if (do_log) log::Debug( - "Routed event is short-circuit in BUBBLE at {}-st control " - "(count " - "from parent).", + u"Routed event is short-circuit in BUBBLE at {}-st control " + u"(count from parent).", count); #endif break; @@ -128,7 +127,7 @@ void DispatchEvent(const std::string_view& event_name, } #ifdef CRU_DEBUG - if (do_log) log::Debug("Routed event dispatch finished."); + if (do_log) log::Debug(u"Routed event dispatch finished."); #endif } } // namespace cru::ui diff --git a/src/ui/UiHost.cpp b/src/ui/UiHost.cpp index 975d0ffe..5eee3925 100644 --- a/src/ui/UiHost.cpp +++ b/src/ui/UiHost.cpp @@ -14,9 +14,11 @@ using platform::native::IUiApplication; namespace event_names { #ifdef CRU_DEBUG -#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = #name; +// clang-format off +#define CRU_DEFINE_EVENT_NAME(name) constexpr const char16_t* name = u#name; +// clang-format on #else -#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = ""; +#define CRU_DEFINE_EVENT_NAME(name) constexpr const char16_t* name = u""; #endif CRU_DEFINE_EVENT_NAME(LoseFocus) @@ -146,7 +148,7 @@ void UiHost::InvalidatePaint() { } void UiHost::InvalidateLayout() { - log::TagDebug(log_tag, "A relayout is requested."); + log::TagDebug(log_tag, u"A relayout is requested."); if (!need_layout_) { platform::native::IUiApplication::GetInstance()->InvokeLater( [resolver = this->CreateResolver()] { @@ -171,7 +173,7 @@ void UiHost::Relayout() { render::MeasureSize::NotSpecified()); root_render_object_->Layout(Point{}); after_layout_event_.Raise(AfterLayoutEventArgs{}); - log::TagDebug(log_tag, "A relayout is finished."); + log::TagDebug(log_tag, u"A relayout is finished."); } bool UiHost::RequestFocusFor(Control* control) { @@ -368,6 +370,11 @@ void UiHost::UpdateCursor() { Control* UiHost::HitTest(const Point& point) { const auto render_object = root_render_object_->HitTest(point); - return render_object ? render_object->GetAttachedControl() : nullptr; + if (render_object) { + const auto control = render_object->GetAttachedControl(); + Ensures(control); + return control; + } + return window_control_; } } // namespace cru::ui diff --git a/src/ui/UiManager.cpp b/src/ui/UiManager.cpp index b8effdfd..b50a9775 100644 --- a/src/ui/UiManager.cpp +++ b/src/ui/UiManager.cpp @@ -30,7 +30,7 @@ UiManager* UiManager::GetInstance() { UiManager::UiManager() { const auto factory = GetGraphFactory(); - theme_resource_.default_font = factory->CreateFont("等线", 24.0f); + theme_resource_.default_font = factory->CreateFont(u"等线", 24.0f); const auto black_brush = std::shared_ptr( CreateSolidColorBrush(factory, colors::black)); diff --git a/src/ui/Window.cpp b/src/ui/Window.cpp index de7044dd..dca95ebb 100644 --- a/src/ui/Window.cpp +++ b/src/ui/Window.cpp @@ -17,7 +17,7 @@ Window::~Window() { managed_ui_host_.reset(); } -std::string_view Window::GetControlType() const { return control_type; } +std::u16string_view Window::GetControlType() const { return control_type; } render::RenderObject* Window::GetRenderObject() const { return render_object_; } diff --git a/src/ui/controls/TextBlock.cpp b/src/ui/controls/TextBlock.cpp index 5ec15796..7f7ee38b 100644 --- a/src/ui/controls/TextBlock.cpp +++ b/src/ui/controls/TextBlock.cpp @@ -30,11 +30,11 @@ render::RenderObject* TextBlock::GetRenderObject() const { return text_render_object_.get(); } -std::string TextBlock::GetText() const { +std::u16string TextBlock::GetText() const { return text_render_object_->GetText(); } -void TextBlock::SetText(std::string text) { +void TextBlock::SetText(std::u16string text) { text_render_object_->SetText(std::move(text)); } diff --git a/src/ui/controls/TextControlService.hpp b/src/ui/controls/TextControlService.hpp index 94d9ebf8..93a48c44 100644 --- a/src/ui/controls/TextControlService.hpp +++ b/src/ui/controls/TextControlService.hpp @@ -19,7 +19,7 @@ constexpr int k_default_caret_blink_duration = 500; // ``` template class TextControlService : public Object { - CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::TextControlService") + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::controls::TextControlService") public: TextControlService(gsl::not_null control) : control_(control) {} @@ -126,22 +126,21 @@ class TextControlService : public Object { const auto text_render_object = this->GetTextRenderObject(); text_render_object->SetSelectionRange(TextRange{start, 0}); text_render_object->SetCaretPosition(start); - log::TagDebug(log_tag, "Text selection started, position: {}.", position); + log::TagDebug(log_tag, u"Text selection started, position: {}.", start); } void UpdateSelection(Index new_end) { - if (!old_selection.has_value()) return; - const auto text_render_object = this->GetTextRenderObject(); const auto old_selection = text_render_object->GetSelectionRange(); + if (!old_selection.has_value()) return; const auto old_start = old_selection->GetStart(); this->GetTextRenderObject()->SetSelectionRange( TextRange::FromTwoSides(old_start, new_end)); text_render_object->SetCaretPosition(new_end); - log::TagDebug(log_tag, "Text selection updated, range: {}, {}.", old_start, + log::TagDebug(log_tag, u"Text selection updated, range: {}, {}.", old_start, new_end); if (const auto scroll_render_object = this->GetScrollRenderObject()) { - //TODO: Implement this. + // TODO: Implement this. } } @@ -194,9 +193,9 @@ class TextControlService : public Object { } } - void KeyDownHandler(event::KeyEventArgs& args) {} + void KeyDownHandler(event::KeyEventArgs& args) { CRU_UNUSED(args); } - void KeyUpHandler(event::KeyEventArgs& args) {} + void KeyUpHandler(event::KeyEventArgs& args) { CRU_UNUSED(args); } void LoseFocusHandler(event::FocusChangeEventArgs& args) { if (!args.IsWindow()) this->AbortSelection(); diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp index f2e3eb9b..b7e1e709 100644 --- a/src/ui/render/BorderRenderObject.cpp +++ b/src/ui/render/BorderRenderObject.cpp @@ -57,7 +57,7 @@ void BorderRenderObject::OnDrawCore(platform::graph::IPainter* painter) { background_brush_.get()); if (is_border_enabled_) { if (border_brush_ == nullptr) { - log::TagWarn(log_tag, "Border is enabled but border brush is null."); + log::TagWarn(log_tag, u"Border is enabled but border brush is null."); } else { painter->FillGeometry(geometry_.get(), border_brush_.get()); } @@ -94,8 +94,8 @@ Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement, const auto max_width = requirement.max.width.GetLengthOrMax(); if (coerced_space_size.width > max_width) { log::TagWarn(log_tag, - "(Measure) Horizontal length of padding, border and margin " - "is bigger than required max length."); + u"(Measure) Horizontal length of padding, border and margin " + u"is bigger than required max length."); coerced_space_size.width = max_width; } content_requirement.max.width = max_width - coerced_space_size.width; @@ -110,8 +110,8 @@ Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement, const auto max_height = requirement.max.height.GetLengthOrMax(); if (coerced_space_size.height > max_height) { log::TagWarn(log_tag, - "(Measure) Vertical length of padding, border and margin is " - "bigger than required max length."); + u"(Measure) Vertical length of padding, border and margin is " + u"bigger than required max length."); coerced_space_size.height = max_height; } content_requirement.max.height = max_height - coerced_space_size.height; diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp index 8e6b41fe..ade230b5 100644 --- a/src/ui/render/FlexLayoutRenderObject.cpp +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -88,7 +88,7 @@ Size FlexLayoutMeasureContentImpl( const MeasureRequirement& requirement, const MeasureSize& preferred_size, const std::vector& children, const std::vector& layout_data, - std::string_view log_tag) { + std::u16string_view log_tag) { Expects(children.size() == layout_data.size()); direction_tag_t direction_tag; @@ -111,8 +111,8 @@ Size FlexLayoutMeasureContentImpl( StackLayoutCalculateChildMaxLength( preferred_cross_length, max_cross_length, GetCross(child->GetMinSize(), direction_tag), log_tag, - "(Measure) Child's min cross size is bigger than parent's max " - "cross size.")); + u"(Measure) Child's min cross size is bigger than parent's max " + u"cross size.")); } // step 1. @@ -315,7 +315,7 @@ Size FlexLayoutMeasureContentImpl( total_length > max_main_length.GetLengthOrUndefined()) { log::TagWarn( log_tag, - "(Measure) Children's main axis length exceeds required max length."); + u"(Measure) Children's main axis length exceeds required max length."); total_length = max_main_length.GetLengthOrUndefined(); } else if (min_main_length.IsSpecified() && total_length < min_main_length.GetLengthOrUndefined()) { diff --git a/src/ui/render/LayoutHelper.cpp b/src/ui/render/LayoutHelper.cpp index 9f94b84d..9ad2d862 100644 --- a/src/ui/render/LayoutHelper.cpp +++ b/src/ui/render/LayoutHelper.cpp @@ -19,8 +19,8 @@ float CalculateAnchorByAlignment(Alignment alignment, float start_point, MeasureLength StackLayoutCalculateChildMaxLength( MeasureLength parent_preferred_size, MeasureLength parent_max_size, - MeasureLength child_min_size, std::string_view log_tag, - std::string_view exceeds_message) { + MeasureLength child_min_size, std::u16string_view log_tag, + std::u16string_view exceeds_message) { if (parent_max_size.GetLengthOrMax() < child_min_size.GetLengthOr0()) { log::TagWarn(log_tag, exceeds_message); return parent_max_size; diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index 66b62e6e..30433868 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -152,8 +152,8 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement, const auto max_width = requirement.max.width.GetLengthOrMax(); if (coerced_space_size.width > max_width) { log::TagWarn(log_tag, - "(Measure) Horizontal length of padding and margin is " - "bigger than required max length."); + u"(Measure) Horizontal length of padding and margin is " + u"bigger than required max length."); coerced_space_size.width = max_width; } content_requirement.max.width = max_width - coerced_space_size.width; @@ -168,8 +168,8 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement, const auto max_height = requirement.max.height.GetLengthOrMax(); if (coerced_space_size.height > max_height) { log::TagWarn(log_tag, - "(Measure) Vertical length of padding and margin is bigger " - "than required max length."); + u"(Measure) Vertical length of padding and margin is bigger " + u"than required max length."); coerced_space_size.height = max_height; } content_requirement.max.height = max_height - coerced_space_size.height; diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 77367970..d3996b98 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -69,6 +69,7 @@ void ScrollRenderObject::SetScrollOffset(const Point& offset) { void ScrollToContain(const Rect& rect) { // TODO: Implement this. + CRU_UNUSED(rect); throw std::runtime_error("Not implemented."); } diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp index 168ff379..75ab0ee3 100644 --- a/src/ui/render/StackLayoutRenderObject.cpp +++ b/src/ui/render/StackLayoutRenderObject.cpp @@ -15,13 +15,13 @@ Size StackLayoutRenderObject::OnMeasureContent( MeasureSize{StackLayoutCalculateChildMaxLength( preferred_size.width, requirement.max.width, child->GetMinSize().width, log_tag, - "(Measure) Child's min width is bigger than " - "parent's max width."), + u"(Measure) Child's min width is bigger than " + u"parent's max width."), StackLayoutCalculateChildMaxLength( preferred_size.height, requirement.max.height, child->GetMinSize().height, log_tag, - "(Measure) Child's min height is bigger than " - "parent's max height.")}, + u"(Measure) Child's min height is bigger than " + u"parent's max height.")}, MeasureSize::NotSpecified()}, MeasureSize::NotSpecified()); const auto size = child->GetSize(); diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index 87a3c352..8a4a4ba1 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -28,16 +28,16 @@ TextRenderObject::TextRenderObject( caret_brush.swap(caret_brush_); const auto graph_factory = GetGraphFactory(); - text_layout_ = graph_factory->CreateTextLayout(font_, ""); + text_layout_ = graph_factory->CreateTextLayout(font_, u""); } TextRenderObject::~TextRenderObject() = default; -std::string TextRenderObject::GetText() const { +std::u16string TextRenderObject::GetText() const { return text_layout_->GetText(); } -void TextRenderObject::SetText(std::string new_text) { +void TextRenderObject::SetText(std::u16string new_text) { text_layout_->SetText(std::move(new_text)); } @@ -171,7 +171,7 @@ Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, if (requirement.max.width.IsSpecified() && text_size.width > requirement.max.width.GetLengthOrUndefined()) { log::TagWarn(log_tag, - "(Measure) Text actual width exceeds the required max width."); + u"(Measure) Text actual width exceeds the required max width."); result.width = requirement.max.width.GetLengthOrUndefined(); } else { result.width = std::max(result.width, preferred_size.width.GetLengthOr0()); @@ -182,7 +182,7 @@ Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, text_size.height > requirement.max.height.GetLengthOrUndefined()) { log::TagWarn( log_tag, - "(Measure) Text actual height exceeds the required max height."); + u"(Measure) Text actual height exceeds the required max height."); result.height = requirement.max.height.GetLengthOrUndefined(); } else { result.height = diff --git a/src/win/CMakeLists.txt b/src/win/CMakeLists.txt index f4062411..75b0a7ca 100644 --- a/src/win/CMakeLists.txt +++ b/src/win/CMakeLists.txt @@ -5,11 +5,9 @@ add_library(cru_win_base STATIC Exception.cpp HeapDebug.cpp - String.cpp ) target_sources(cru_win_base PUBLIC ${CRU_WIN_BASE_INCLUDE_DIR}/Exception.hpp - ${CRU_WIN_BASE_INCLUDE_DIR}/String.hpp ${CRU_WIN_BASE_INCLUDE_DIR}/WinPreConfig.hpp ) target_compile_definitions(cru_win_base PUBLIC UNICODE _UNICODE) # use unicode diff --git a/src/win/DebugLogger.hpp b/src/win/DebugLogger.hpp index 4ca1567b..598ee9e8 100644 --- a/src/win/DebugLogger.hpp +++ b/src/win/DebugLogger.hpp @@ -1,7 +1,6 @@ #include "cru/win/WinPreConfig.hpp" #include "cru/common/Logger.hpp" -#include "cru/win/String.hpp" namespace cru::platform::win { @@ -14,11 +13,10 @@ class WinDebugLoggerSource : public ::cru::log::ILogSource { ~WinDebugLoggerSource() = default; - void Write(::cru::log::LogLevel level, std::string_view s) override { + void Write(::cru::log::LogLevel level, const std::u16string& s) override { CRU_UNUSED(level) - const std::wstring&& ws = ToUtf16String(s); - ::OutputDebugStringW(ws.data()); + ::OutputDebugStringW(reinterpret_cast(s.c_str())); } }; } // namespace cru::platform::win diff --git a/src/win/Exception.cpp b/src/win/Exception.cpp index 9f9fb03d..8d2eee23 100644 --- a/src/win/Exception.cpp +++ b/src/win/Exception.cpp @@ -5,8 +5,8 @@ namespace cru::platform::win { -inline std::string HResultMakeMessage(HRESULT h_result, - std::optional message) { +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); @@ -19,20 +19,20 @@ HResultError::HResultError(HRESULT h_result) h_result_(h_result) {} HResultError::HResultError(HRESULT h_result, - const std::string_view& additional_message) + 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) { + std::string_view message) { return fmt::format("Last error code: {:#04x}.\nMessage: {}\n", error_code, message); } -Win32Error::Win32Error(const std::string_view& message) +Win32Error::Win32Error(std::string_view message) : Win32Error(::GetLastError(), message) {} -Win32Error::Win32Error(DWORD error_code, const std::string_view& message) +Win32Error::Win32Error(DWORD error_code, 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 deleted file mode 100644 index eb585523..00000000 --- a/src/win/String.cpp +++ /dev/null @@ -1,129 +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 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."); - } -} - -Index IndexUtf8ToUtf16(const std::string_view& utf8_string, Index utf8_index, - const std::wstring_view& utf16_string) { - if (utf8_index >= static_cast(utf8_string.length())) - return utf16_string.length(); - - Index cp_index = 0; - Utf8Iterator iter{utf8_string}; - while (iter.CurrentPosition() <= utf8_index) { - iter.Next(); - cp_index++; - } - - Utf16Iterator result_iter{utf16_string}; - for (Index i = 0; i < cp_index - 1; i++) { - if (result_iter.Next() == k_code_point_end) break; - } - - return result_iter.CurrentPosition(); -} - -Index IndexUtf16ToUtf8(const std::wstring_view& utf16_string, Index utf16_index, - const std::string_view& utf8_string) { - if (utf16_index >= static_cast(utf16_string.length())) - return utf8_string.length(); - - Index cp_index = 0; - Utf16Iterator iter{utf16_string}; - while (iter.CurrentPosition() <= utf16_index) { - iter.Next(); - cp_index++; - } - - Utf8Iterator result_iter{utf8_string}; - for (Index 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/graph/direct/Factory.cpp b/src/win/graph/direct/Factory.cpp index d9213994..03e64e13 100644 --- a/src/win/graph/direct/Factory.cpp +++ b/src/win/graph/direct/Factory.cpp @@ -19,8 +19,8 @@ void InitializeCom() { } if (hresult == S_FALSE) { log::Debug( - "Try to call CoInitializeEx, but it seems COM is already " - "initialized."); + u"Try to call CoInitializeEx, but it seems COM is already " + u"initialized."); } } @@ -95,12 +95,12 @@ std::unique_ptr DirectGraphFactory::CreateGeometryBuilder() { } std::unique_ptr DirectGraphFactory::CreateFont( - const std::string_view& font_family, float font_size) { - return std::make_unique(this, font_family, font_size); + std::u16string font_family, float font_size) { + return std::make_unique(this, std::move(font_family), font_size); } std::unique_ptr DirectGraphFactory::CreateTextLayout( - std::shared_ptr font, std::string text) { + std::shared_ptr font, std::u16string text) { return std::make_unique(this, std::move(font), std::move(text)); } diff --git a/src/win/graph/direct/Font.cpp b/src/win/graph/direct/Font.cpp index edbbc59d..34de5b71 100644 --- a/src/win/graph/direct/Font.cpp +++ b/src/win/graph/direct/Font.cpp @@ -2,15 +2,14 @@ #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) { +DWriteFont::DWriteFont(DirectGraphFactory* factory, std::u16string font_family, + float font_size) + : DirectGraphResource(factory), font_family_(std::move(font_family)) { // Get locale std::array buffer; if (!::GetUserDefaultLocaleName(buffer.data(), @@ -18,10 +17,9 @@ DWriteFont::DWriteFont(DirectGraphFactory* factory, 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, + reinterpret_cast(font_family_.c_str()), 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)); diff --git a/src/win/graph/direct/TextLayout.cpp b/src/win/graph/direct/TextLayout.cpp index 16db77f0..7b5b230f 100644 --- a/src/win/graph/direct/TextLayout.cpp +++ b/src/win/graph/direct/TextLayout.cpp @@ -5,38 +5,33 @@ #include "cru/win/graph/direct/Exception.hpp" #include "cru/win/graph/direct/Factory.hpp" #include "cru/win/graph/direct/Font.hpp" -#include "cru/win/String.hpp" #include namespace cru::platform::graph::win::direct { -using cru::platform::win::IndexUtf16ToUtf8; -using cru::platform::win::IndexUtf8ToUtf16; - DWriteTextLayout::DWriteTextLayout(DirectGraphFactory* factory, std::shared_ptr font, - std::string text) + std::u16string text) : DirectGraphResource(factory), text_(std::move(text)) { Expects(font); font_ = CheckPlatform(font, GetPlatformId()); - w_text_ = cru::platform::win::ToUtf16String(text_); - ThrowIfFailed(factory->GetDWriteFactory()->CreateTextLayout( - w_text_.c_str(), static_cast(w_text_.size()), - font_->GetComInterface(), max_width_, max_height_, &text_layout_)); + reinterpret_cast(text_.c_str()), + static_cast(text_.size()), font_->GetComInterface(), max_width_, + max_height_, &text_layout_)); } DWriteTextLayout::~DWriteTextLayout() = default; -std::string DWriteTextLayout::GetText() { return text_; } +std::u16string DWriteTextLayout::GetText() { return text_; } -void DWriteTextLayout::SetText(std::string new_text) { +void DWriteTextLayout::SetText(std::u16string new_text) { text_.swap(new_text); - w_text_ = cru::platform::win::ToUtf16String(text_); ThrowIfFailed(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( - w_text_.c_str(), static_cast(w_text_.size()), - font_->GetComInterface(), max_width_, max_height_, &text_layout_)); + reinterpret_cast(text_.c_str()), + static_cast(text_.size()), font_->GetComInterface(), max_width_, + max_height_, &text_layout_)); } std::shared_ptr DWriteTextLayout::GetFont() { @@ -46,8 +41,9 @@ std::shared_ptr DWriteTextLayout::GetFont() { void DWriteTextLayout::SetFont(std::shared_ptr font) { font_ = CheckPlatform(font, GetPlatformId()); ThrowIfFailed(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( - w_text_.c_str(), static_cast(w_text_.size()), - font_->GetComInterface(), max_width_, max_height_, &text_layout_)); + reinterpret_cast(text_.c_str()), + static_cast(text_.size()), font_->GetComInterface(), max_width_, + max_height_, &text_layout_)); } void DWriteTextLayout::SetMaxWidth(float max_width) { @@ -73,12 +69,6 @@ std::vector DWriteTextLayout::TextRangeRect( } const auto text_range = text_range_arg.Normalize(); - // TODO: This can be faster with one iteration. - const int start_index = - IndexUtf8ToUtf16(text_, static_cast(text_range.position), w_text_); - const int end_index = IndexUtf8ToUtf16( - text_, static_cast(text_range.position + text_range.count), w_text_); - DWRITE_TEXT_METRICS text_metrics; ThrowIfFailed(text_layout_->GetMetrics(&text_metrics)); const auto metrics_count = @@ -87,9 +77,9 @@ std::vector DWriteTextLayout::TextRangeRect( std::vector hit_test_metrics(metrics_count); UINT32 actual_count; ThrowIfFailed(text_layout_->HitTestTextRange( - static_cast(start_index), - static_cast(end_index - start_index), 0, 0, - hit_test_metrics.data(), metrics_count, &actual_count)); + static_cast(text_range.position), + static_cast(text_range.count), 0, 0, hit_test_metrics.data(), + metrics_count, &actual_count)); hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, hit_test_metrics.cend()); @@ -105,14 +95,11 @@ std::vector DWriteTextLayout::TextRangeRect( return result; } -Point DWriteTextLayout::TextSinglePoint(gsl::index position, bool trailing) { - const auto index = - IndexUtf8ToUtf16(text_, static_cast(position), w_text_); - +Point DWriteTextLayout::TextSinglePoint(Index position, bool trailing) { DWRITE_HIT_TEST_METRICS metrics; FLOAT left; FLOAT top; - ThrowIfFailed(text_layout_->HitTestTextPosition(static_cast(index), + ThrowIfFailed(text_layout_->HitTestTextPosition(static_cast(position), static_cast(trailing), &left, &top, &metrics)); return Point{left, top}; @@ -126,10 +113,8 @@ TextHitTestResult DWriteTextLayout::HitTest(const Point& point) { ThrowIfFailed(text_layout_->HitTestPoint(point.x, point.y, &trailing, &inside, &metrics)); - const auto index = - IndexUtf16ToUtf8(w_text_, static_cast(metrics.textPosition), text_); TextHitTestResult result; - result.position = index; + result.position = metrics.textPosition; result.trailing = trailing != 0; result.insideText = inside != 0; return result; diff --git a/src/win/native/Cursor.cpp b/src/win/native/Cursor.cpp index a6ab5bfc..429f6e7c 100644 --- a/src/win/native/Cursor.cpp +++ b/src/win/native/Cursor.cpp @@ -16,7 +16,7 @@ WinCursor::~WinCursor() { if (!::DestroyCursor(handle_)) { // This is not a fetal error but might still need notice because it may // cause leak. - log::TagWarn(log_tag, "Failed to destroy a cursor. Last error code: {}", + log::TagWarn(log_tag, u"Failed to destroy a cursor. Last error code: {}", ::GetLastError()); } } diff --git a/src/win/native/GodWindow.cpp b/src/win/native/GodWindow.cpp index 8ef2788e..b1e7275e 100644 --- a/src/win/native/GodWindow.cpp +++ b/src/win/native/GodWindow.cpp @@ -45,7 +45,7 @@ GodWindow::GodWindow(WinUiApplication* application) { GodWindow::~GodWindow() { if (!::DestroyWindow(hwnd_)) { // Although this could be "safely" ignore. - log::TagWarn(log_tag, "Failed to destroy god window."); + log::TagWarn(log_tag, u"Failed to destroy god window."); } } diff --git a/src/win/native/InputMethod.cpp b/src/win/native/InputMethod.cpp index d3d989f3..5fc6c934 100644 --- a/src/win/native/InputMethod.cpp +++ b/src/win/native/InputMethod.cpp @@ -2,9 +2,9 @@ #include "DpiUtil.hpp" #include "cru/common/Logger.hpp" +#include "cru/common/StringUtil.hpp" #include "cru/platform/Check.hpp" #include "cru/win/Exception.hpp" -#include "cru/win/String.hpp" #include "cru/win/native/Window.hpp" #include @@ -35,7 +35,7 @@ AutoHIMC& AutoHIMC::operator=(AutoHIMC&& other) { AutoHIMC::~AutoHIMC() { if (handle_) { if (!::ImmReleaseContext(hwnd_, handle_)) - log::TagWarn(log_tag, "Failed to release HIMC."); + log::TagWarn(log_tag, u"Failed to release HIMC."); } } @@ -104,19 +104,19 @@ CompositionClauses GetCompositionClauses(HIMC imm_context, int target_start, return result; } -std::wstring GetString(HIMC imm_context) { +std::u16string GetString(HIMC imm_context) { LONG string_size = ::ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0); - std::wstring result((string_size / sizeof(wchar_t)), 0); + std::u16string result((string_size / sizeof(char16_t)), 0); ::ImmGetCompositionString(imm_context, GCS_COMPSTR, result.data(), string_size); return result; } -std::wstring GetResultString(HIMC imm_context) { +std::u16string GetResultString(HIMC imm_context) { LONG string_size = ::ImmGetCompositionString(imm_context, GCS_RESULTSTR, NULL, 0); - std::wstring result((string_size / sizeof(wchar_t)), 0); + std::u16string result((string_size / sizeof(char16_t)), 0); ::ImmGetCompositionString(imm_context, GCS_RESULTSTR, result.data(), string_size); return result; @@ -126,25 +126,17 @@ CompositionText GetCompositionInfo(HIMC imm_context) { // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and // convert them into underlines and selection range respectively. - auto w_text = GetString(imm_context); + auto text = GetString(imm_context); - int w_length = static_cast(w_text.length()); + int length = static_cast(text.length()); // Find out the range selected by the user. - int w_target_start = w_length; - int w_target_end = w_length; - GetCompositionTargetRange(imm_context, &w_target_start, &w_target_end); + int target_start = length; + int target_end = length; + GetCompositionTargetRange(imm_context, &target_start, &target_end); - auto clauses = - GetCompositionClauses(imm_context, w_target_start, w_target_end); + auto clauses = GetCompositionClauses(imm_context, target_start, target_end); - int w_cursor = ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0); - - auto text = platform::win::ToUtf8String(w_text); - for (auto& clause : clauses) { - clause.start = platform::win::IndexUtf16ToUtf8(w_text, clause.start, text); - clause.end = platform::win::IndexUtf16ToUtf8(w_text, clause.end, text); - } - int cursor = platform::win::IndexUtf16ToUtf8(w_text, w_cursor, text); + int cursor = ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0); return CompositionText{std::move(text), std::move(clauses), TextRange{cursor}}; @@ -169,7 +161,7 @@ void WinInputMethodContext::EnableIME() { const auto hwnd = native_window->GetWindowHandle(); if (::ImmAssociateContextEx(hwnd, nullptr, IACE_DEFAULT) == FALSE) { - log::TagWarn(log_tag, "Failed to enable ime."); + log::TagWarn(log_tag, u"Failed to enable ime."); } } @@ -181,11 +173,12 @@ void WinInputMethodContext::DisableIME() { AutoHIMC himc{hwnd}; if (!::ImmNotifyIME(himc.Get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0)) { - log::TagWarn(log_tag, "Failed to complete composition before disable ime."); + log::TagWarn(log_tag, + u"Failed to complete composition before disable ime."); } if (::ImmAssociateContextEx(hwnd, nullptr, 0) == FALSE) { - log::TagWarn(log_tag, "Failed to disable ime."); + log::TagWarn(log_tag, u"Failed to disable ime."); } } @@ -195,7 +188,7 @@ void WinInputMethodContext::CompleteComposition() { auto himc = *std::move(optional_himc); if (!::ImmNotifyIME(himc.Get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0)) { - log::TagWarn(log_tag, "Failed to complete composition."); + log::TagWarn(log_tag, u"Failed to complete composition."); } } @@ -205,7 +198,7 @@ void WinInputMethodContext::CancelComposition() { auto himc = *std::move(optional_himc); if (!::ImmNotifyIME(himc.Get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0)) { - log::TagWarn(log_tag, "Failed to complete composition."); + log::TagWarn(log_tag, u"Failed to complete composition."); } } @@ -229,7 +222,7 @@ void WinInputMethodContext::SetCandidateWindowPosition(const Point& point) { if (!::ImmSetCandidateWindow(himc.Get(), &form)) log::TagDebug(log_tag, - "Failed to set input method candidate window position."); + u"Failed to set input method candidate window position."); } IEvent* WinInputMethodContext::CompositionStartEvent() { @@ -244,7 +237,7 @@ IEvent* WinInputMethodContext::CompositionEvent() { return &composition_event_; } -IEvent* WinInputMethodContext::TextEvent() { +IEvent* WinInputMethodContext::TextEvent() { return &text_event_; } @@ -253,18 +246,17 @@ void WinInputMethodContext::OnWindowNativeMessage( const auto& message = args.GetWindowMessage(); switch (message.msg) { case WM_CHAR: { - const auto c = static_cast(message.w_param); - if (platform::win::IsSurrogatePair(c)) { + const auto c = static_cast(message.w_param); + if (IsSurrogatePair(c)) { // I don't think this will happen because normal key strike without ime // should only trigger ascci character. If it is a charater from // supplementary planes, it should be handled with ime messages. log::TagWarn(log_tag, - "A WM_CHAR message for character from supplementary " - "planes is ignored."); + u"A WM_CHAR message for character from supplementary " + u"planes is ignored."); } else { - wchar_t s[1] = {c}; - auto str = platform::win::ToUtf8String({s, 1}); - text_event_.Raise(str); + char16_t s[1] = {c}; + text_event_.Raise({s, 1}); } args.HandleWithResult(0); break; @@ -272,8 +264,6 @@ void WinInputMethodContext::OnWindowNativeMessage( case WM_IME_COMPOSITION: { composition_event_.Raise(nullptr); auto composition_text = GetCompositionText(); - log::TagDebug(log_tag, "WM_IME_COMPOSITION composition text:\n{}", - composition_text); if (message.l_param & GCS_RESULTSTR) { auto result_string = GetResultString(); text_event_.Raise(result_string); @@ -291,13 +281,13 @@ void WinInputMethodContext::OnWindowNativeMessage( } } -std::string WinInputMethodContext::GetResultString() { +std::u16string WinInputMethodContext::GetResultString() { auto optional_himc = TryGetHIMC(); - if (!optional_himc.has_value()) return ""; + if (!optional_himc.has_value()) return u""; auto himc = *std::move(optional_himc); - auto w_result = win::GetResultString(himc.Get()); - return platform::win::ToUtf8String(w_result); + auto result = win::GetResultString(himc.Get()); + return result; } std::optional WinInputMethodContext::TryGetHIMC() { diff --git a/src/win/native/Window.cpp b/src/win/native/Window.cpp index 22505bd6..81642451 100644 --- a/src/win/native/Window.cpp +++ b/src/win/native/Window.cpp @@ -5,7 +5,6 @@ #include "WindowManager.hpp" #include "cru/common/Logger.hpp" #include "cru/platform/Check.hpp" -#include "cru/win/String.hpp" #include "cru/win/native/Cursor.hpp" #include "cru/win/native/Exception.hpp" #include "cru/win/native/Keyboard.hpp" @@ -124,7 +123,7 @@ bool WinNativeWindow::ReleaseMouse() { } void WinNativeWindow::RequestRepaint() { - log::TagDebug(log_tag, "A repaint is requested."); + log::TagDebug(log_tag, u"A repaint is requested."); if (!::InvalidateRect(hwnd_, nullptr, FALSE)) throw Win32Error(::GetLastError(), "Failed to invalidate window."); if (!::UpdateWindow(hwnd_)) @@ -145,43 +144,43 @@ void WinNativeWindow::SetCursor(std::shared_ptr cursor) { if (!::SetClassLongPtrW(hwnd_, GCLP_HCURSOR, reinterpret_cast(cursor_->GetHandle()))) { log::TagWarn(log_tag, - "Failed to set cursor because failed to set class long. Last " - "error code: {}.", + u"Failed to set cursor because failed to set class long. Last " + u"error code: {}.", ::GetLastError()); return; } if (!IsVisible()) return; - auto lg = [](const std::string_view& reason) { + auto lg = [](const std::u16string_view& reason) { log::TagWarn( log_tag, - "Failed to set cursor because {} when window is visible. (We need to " - "update cursor if it is inside the window.) Last error code: {}.", + u"Failed to set cursor because {} when window is visible. (We need to " + u"update cursor if it is inside the window.) Last error code: {}.", reason, ::GetLastError()); }; ::POINT point; if (!::GetCursorPos(&point)) { - lg("failed to get cursor pos"); + lg(u"failed to get cursor pos"); return; } ::RECT rect; if (!::GetClientRect(hwnd_, &rect)) { - lg("failed to get window's client rect"); + lg(u"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"); + lg(u"failed to call ClientToScreen on lefttop"); return; } if (!::ClientToScreen(hwnd_, &rightbottom)) { - lg("failed to call ClientToScreen on rightbottom"); + lg(u"failed to call ClientToScreen on rightbottom"); return; } @@ -354,7 +353,7 @@ void WinNativeWindow::OnDestroyInternal() { void WinNativeWindow::OnPaintInternal() { paint_event_.Raise(nullptr); ValidateRect(hwnd_, nullptr); - log::TagDebug(log_tag, "A repaint is finished."); + log::TagDebug(log_tag, u"A repaint is finished."); } void WinNativeWindow::OnResizeInternal(const int new_width, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d41e3467..c534b909 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(GTest CONFIG REQUIRED) include(GoogleTest) -add_subdirectory(win) +add_subdirectory(common) add_library(cru_test_base INTERFACE) target_link_libraries(cru_test_base INTERFACE GTest::gtest GTest::gtest_main) diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt new file mode 100644 index 00000000..c1fd078d --- /dev/null +++ b/test/common/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(cru_base_test + StringUtilTest.cpp +) +target_link_libraries(cru_base_test PRIVATE cru_base cru_test_base) + +gtest_discover_tests(cru_base_test) diff --git a/test/common/StringUtilTest.cpp b/test/common/StringUtilTest.cpp new file mode 100644 index 00000000..e454505b --- /dev/null +++ b/test/common/StringUtilTest.cpp @@ -0,0 +1,53 @@ +#include "cru/common/StringUtil.hpp" + +#include + +using cru::k_invalid_code_point; + +// TEST(WinString, Utf8Iterator) { +// using cru::platform::win::Utf8Iterator; +// std::string_view text = "aπ你🤣!"; +// Utf8Iterator i{text}; +// ASSERT_EQ(i.Next(), 0x0061); +// ASSERT_EQ(i.Next(), 0x03C0); +// ASSERT_EQ(i.Next(), 0x4F60); +// ASSERT_EQ(i.Next(), 0x1F923); +// ASSERT_EQ(i.Next(), 0x0021); +// ASSERT_EQ(i.Next(), k_invalid_code_point); +// } + +TEST(WinString, Utf16Iterator) { + using cru::Utf16Iterator; + std::u16string_view text = u"aπ你🤣!"; + Utf16Iterator i{text}; + ASSERT_EQ(i.Next(), 0x0061); + ASSERT_EQ(i.Next(), 0x03C0); + ASSERT_EQ(i.Next(), 0x4F60); + ASSERT_EQ(i.Next(), 0x1F923); + ASSERT_EQ(i.Next(), 0x0021); + ASSERT_EQ(i.Next(), k_invalid_code_point); +} + +// TEST(WinString, IndexUtf8ToUtf16) { +// using cru::platform::win::IndexUtf8ToUtf16; +// std::string_view utf8_string = "aπ你🤣!"; +// std::wstring_view utf16_string = L"aπ你🤣!"; +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 0, utf16_string), 0); +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 1, utf16_string), 1); +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 3, utf16_string), 2); +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 6, utf16_string), 3); +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 10, utf16_string), 5); +// ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 11, utf16_string), 6); +// } + +// TEST(WinString, IndexUtf16ToUtf8) { +// using cru::platform::win::IndexUtf16ToUtf8; +// std::string_view utf8_string = "aπ你🤣!"; +// std::wstring_view utf16_string = L"aπ你🤣!"; +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 0, utf8_string), 0); +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 1, utf8_string), 1); +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 2, utf8_string), 3); +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 3, utf8_string), 6); +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 5, utf8_string), 10); +// ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 6, utf8_string), 11); +// } diff --git a/test/win/CMakeLists.txt b/test/win/CMakeLists.txt deleted file mode 100644 index 138af96b..00000000 --- a/test/win/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_executable(cru_test_win_base - String.cpp -) -target_link_libraries(cru_test_win_base PRIVATE cru_win_base cru_test_base) - -gtest_discover_tests(cru_test_win_base) diff --git a/test/win/String.cpp b/test/win/String.cpp deleted file mode 100644 index a666dc81..00000000 --- a/test/win/String.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "cru/win/String.hpp" - -#include - -using cru::platform::win::k_code_point_end; - -TEST(WinString, Utf8Iterator) { - using cru::platform::win::Utf8Iterator; - std::string_view text = "aπ你🤣!"; - Utf8Iterator i{text}; - ASSERT_EQ(i.Next(), 0x0061); - ASSERT_EQ(i.Next(), 0x03C0); - ASSERT_EQ(i.Next(), 0x4F60); - ASSERT_EQ(i.Next(), 0x1F923); - ASSERT_EQ(i.Next(), 0x0021); - ASSERT_EQ(i.Next(), k_code_point_end); -} - -TEST(WinString, Utf16Iterator) { - using cru::platform::win::Utf16Iterator; - std::wstring_view text = L"aπ你🤣!"; - Utf16Iterator i{text}; - ASSERT_EQ(i.Next(), 0x0061); - ASSERT_EQ(i.Next(), 0x03C0); - ASSERT_EQ(i.Next(), 0x4F60); - ASSERT_EQ(i.Next(), 0x1F923); - ASSERT_EQ(i.Next(), 0x0021); - ASSERT_EQ(i.Next(), k_code_point_end); -} - -TEST(WinString, IndexUtf8ToUtf16) { - using cru::platform::win::IndexUtf8ToUtf16; - std::string_view utf8_string = "aπ你🤣!"; - std::wstring_view utf16_string = L"aπ你🤣!"; - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 0, utf16_string), 0); - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 1, utf16_string), 1); - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 3, utf16_string), 2); - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 6, utf16_string), 3); - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 10, utf16_string), 5); - ASSERT_EQ(IndexUtf8ToUtf16(utf8_string, 11, utf16_string), 6); -} - -TEST(WinString, IndexUtf16ToUtf8) { - using cru::platform::win::IndexUtf16ToUtf8; - std::string_view utf8_string = "aπ你🤣!"; - std::wstring_view utf16_string = L"aπ你🤣!"; - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 0, utf8_string), 0); - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 1, utf8_string), 1); - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 2, utf8_string), 3); - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 3, utf8_string), 6); - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 5, utf8_string), 10); - ASSERT_EQ(IndexUtf16ToUtf8(utf16_string, 6, utf8_string), 11); -} -- cgit v1.2.3