diff options
-rw-r--r-- | include/cru/common/format.hpp | 122 | ||||
-rw-r--r-- | include/cru/platform/graph_base.hpp | 28 | ||||
-rw-r--r-- | include/cru/platform/native/input_method.hpp | 9 | ||||
-rw-r--r-- | src/win/native/input_method.cpp | 120 |
4 files changed, 171 insertions, 108 deletions
diff --git a/include/cru/common/format.hpp b/include/cru/common/format.hpp index ecc4b30b..4405c8c5 100644 --- a/include/cru/common/format.hpp +++ b/include/cru/common/format.hpp @@ -1,104 +1,68 @@ #pragma once #include "pre_config.hpp" +#include <sstream> #include <string> #include <string_view> namespace cru::util { namespace details { template <typename T> -struct TypeTag {}; - -constexpr std::wstring_view PlaceHolder(TypeTag<std::wstring>) { - return std::wstring_view(L"{}"); -} - -constexpr std::string_view PlaceHolder(TypeTag<std::string>) { - return std::string_view("{}"); -} - -template <typename TString> -void FormatInternal(TString& string) { - const auto find_result = string.find(PlaceHolder(TypeTag<TString>{})); - if (find_result != TString::npos) +struct FormatTrait {}; + +template <> +struct FormatTrait<std::string_view> { + static constexpr std::string_view placeholder = "{}"; + using ResultType = std::string; + using StreamType = std::stringstream; +}; + +template <> +struct FormatTrait<std::wstring_view> { + static constexpr std::wstring_view placeholder = L"{}"; + using ResultType = std::wstring; + using StreamType = std::wstringstream; +}; + +template <typename TStringView> +void FormatInternal(typename FormatTrait<TStringView>::StreamType& stream, + const TStringView& string) { + const auto find_result = string.find(FormatTrait<TStringView>::placeholder); + if (find_result != TStringView::npos) throw std::invalid_argument("There is more placeholders than args."); + stream << string; } -template <typename TString, typename T, typename... TRest> -void FormatInternal(TString& string, const T& arg, const TRest&... args) { - const auto find_result = string.find(PlaceHolder(TypeTag<TString>{})); - if (find_result == TString::npos) +template <typename TStringView, typename T, typename... TRest> +void FormatInternal(typename FormatTrait<TStringView>::StreamType& stream, + const TStringView& string, const T& arg, + const TRest&... args) { + const auto find_result = string.find(FormatTrait<TStringView>::placeholder); + if (find_result == TStringView::npos) throw std::invalid_argument("There is less placeholders than args."); - string.replace(find_result, 2, FormatToString(arg, TypeTag<TString>{})); - FormatInternal<TString>(string, args...); + stream << string.substr(0, find_result); + stream << arg; + + FormatInternal(stream, string.substr(find_result + 2), args...); +} + +template <typename TStringView, typename... T> +auto FormatTemplate(const TStringView& format, const T&... args) -> + typename FormatTrait<TStringView>::ResultType { + typename FormatTrait<TStringView>::StreamType stream; + FormatInternal(stream, format, args...); + return stream.str(); } } // namespace details template <typename... T> std::wstring Format(const std::wstring_view& format, const T&... args) { - std::wstring result(format); - details::FormatInternal<std::wstring>(result, args...); - return result; + return details::FormatTemplate(format, args...); } template <typename... T> std::string Format(const std::string_view& format, const T&... args) { - std::string result(format); - details::FormatInternal<std::string>(result, args...); - return result; -} - -#define CRU_FORMAT_NUMBER(type) \ - inline std::string FormatToString(const type number, \ - details::TypeTag<std::string>) { \ - return std::to_string(number); \ - } \ - inline std::wstring FormatToString(const type number, \ - details::TypeTag<std::wstring>) { \ - return std::to_wstring(number); \ - } - -CRU_FORMAT_NUMBER(int) -CRU_FORMAT_NUMBER(short) -CRU_FORMAT_NUMBER(long) -CRU_FORMAT_NUMBER(long long) -CRU_FORMAT_NUMBER(unsigned int) -CRU_FORMAT_NUMBER(unsigned short) -CRU_FORMAT_NUMBER(unsigned long) -CRU_FORMAT_NUMBER(unsigned long long) -CRU_FORMAT_NUMBER(float) -CRU_FORMAT_NUMBER(double) - -#undef CRU_FORMAT_NUMBER - -inline std::wstring_view FormatToString(const std::wstring& string, - details::TypeTag<std::wstring>) { - return string; -} - -inline std::string_view FormatToString(const std::string& string, - details::TypeTag<std::string>) { - return string; -} - -inline std::wstring_view FormatToString(const std::wstring_view& string, - details::TypeTag<std::wstring>) { - return string; -} - -inline std::string_view FormatToString(const std::string_view& string, - details::TypeTag<std::string>) { - return string; -} - -inline std::wstring_view FormatToString(const wchar_t* string, - details::TypeTag<std::wstring>) { - return std::wstring_view(string); -} - -inline std::string_view FormatToString(const char* string, - details::TypeTag<std::string>) { - return std::string_view(string); + return details::FormatTemplate(format, args...); } } // namespace cru::util diff --git a/include/cru/platform/graph_base.hpp b/include/cru/platform/graph_base.hpp index 5840ce18..98ac3993 100644 --- a/include/cru/platform/graph_base.hpp +++ b/include/cru/platform/graph_base.hpp @@ -1,5 +1,5 @@ #pragma once -#include "cru/common/pre_config.hpp" +#include "cru/common/base.hpp" #include <cstdint> #include <optional> @@ -214,30 +214,18 @@ constexpr bool operator!=(const Ellipse& left, const Ellipse& right) { } struct TextRange final { - constexpr static std::optional<TextRange> FromTwoSides(unsigned first, - unsigned second) { - if (first > second) - return std::make_optional<TextRange>(second, first - second); - if (first < second) - return std::make_optional<TextRange>(first, second - first); - return std::nullopt; - } - - constexpr static std::pair<unsigned, unsigned> ToTwoSides( - std::optional<TextRange> text_range, unsigned default_position = 0) { - if (text_range.has_value()) - return std::make_pair( - text_range.value().position, - text_range.value().position + text_range.value().count); - return std::make_pair(default_position, default_position); + constexpr static TextRange FromTwoSides(gsl::index start, gsl::index end) { + return TextRange(start, end - start); } constexpr TextRange() = default; - constexpr TextRange(const unsigned position, const unsigned count) + constexpr TextRange(const gsl::index position, const gsl::index count) : position(position), count(count) {} - unsigned position = 0; - unsigned count = 0; + gsl::index GetEnd() const { return position + count; } + + gsl::index position = 0; + gsl::index count = 0; }; struct Color { diff --git a/include/cru/platform/native/input_method.hpp b/include/cru/platform/native/input_method.hpp index 00017502..56e2fb27 100644 --- a/include/cru/platform/native/input_method.hpp +++ b/include/cru/platform/native/input_method.hpp @@ -7,15 +7,18 @@ #include <vector> namespace cru::platform::native { -struct CompositionUnderline { +struct CompositionClause { int start; int end; + bool target; }; +using CompositionClauses = std::vector<CompositionClause>; + struct CompositionText { std::string text; - std::vector<CompositionUnderline> underlines; - int caret_position; + CompositionClauses clauses; + TextRange selection; }; struct IInputMethodContext : virtual INativeResource { diff --git a/src/win/native/input_method.cpp b/src/win/native/input_method.cpp index 7153b50d..dace5827 100644 --- a/src/win/native/input_method.cpp +++ b/src/win/native/input_method.cpp @@ -39,6 +39,102 @@ AutoHIMC::~AutoHIMC() { } } +// copied from chromium +namespace { +// Determines whether or not the given attribute represents a target +// (a.k.a. a selection). +bool IsTargetAttribute(char attribute) { + return (attribute == ATTR_TARGET_CONVERTED || + attribute == ATTR_TARGET_NOTCONVERTED); +} +// Helper function for ImeInput::GetCompositionInfo() method, to get the target +// range that's selected by the user in the current composition string. +void GetCompositionTargetRange(HIMC imm_context, int* target_start, + int* target_end) { + int attribute_size = + ::ImmGetCompositionString(imm_context, GCS_COMPATTR, NULL, 0); + if (attribute_size > 0) { + int start = 0; + int end = 0; + std::vector<char> attribute_data(attribute_size); + ::ImmGetCompositionString(imm_context, GCS_COMPATTR, attribute_data.data(), + attribute_size); + for (start = 0; start < attribute_size; ++start) { + if (IsTargetAttribute(attribute_data[start])) break; + } + for (end = start; end < attribute_size; ++end) { + if (!IsTargetAttribute(attribute_data[end])) break; + } + if (start == attribute_size) { + // This composition clause does not contain any target clauses, + // i.e. this clauses is an input clause. + // We treat the whole composition as a target clause. + start = 0; + end = attribute_size; + } + *target_start = start; + *target_end = end; + } +} +// Helper function for ImeInput::GetCompositionInfo() method, to get underlines +// information of the current composition string. +CompositionClauses GetCompositionClauses(HIMC imm_context, int target_start, + int target_end) { + CompositionClauses result; + int clause_size = + ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, NULL, 0); + int clause_length = clause_size / sizeof(std::uint32_t); + result.reserve(clause_length - 1); + if (clause_length) { + std::vector<std::uint32_t> clause_data(clause_length); + ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, clause_data.data(), + clause_size); + for (int i = 0; i < clause_length - 1; ++i) { + CompositionClause clause; + clause.start = clause_data[i]; + clause.end = clause_data[i + 1]; + clause.target = false; + // Use thick underline for the target clause. + if (clause.start >= target_start && clause.end <= target_end) { + clause.target = true; + } + result.push_back(clause); + } + } + return result; +} + +std::wstring GetString(HIMC imm_context) { + LONG string_size = + ::ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0); + std::wstring result((string_size / sizeof(wchar_t)) + 1, 0); + ::ImmGetCompositionString(imm_context, GCS_COMPSTR, result.data(), + string_size); + return result; +} + +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); + + int w_length = static_cast<int>(w_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); + + auto clauses = + GetCompositionClauses(imm_context, w_target_start, w_target_end); + + int cursor = ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0); + + // TODO: Finish this. +} + +} // namespace + WinInputMethodContext::WinInputMethodContext( gsl::not_null<WinNativeWindow*> window) : native_window_resolver_(window->GetResolver()) { @@ -51,29 +147,41 @@ WinInputMethodContext::WinInputMethodContext( WinInputMethodContext::~WinInputMethodContext() {} void WinInputMethodContext::EnableIME() { - // TODO! + const auto native_window = Resolve(native_window_resolver_.get()); + if (native_window == nullptr) return; + const auto hwnd = native_window->GetWindowHandle(); + + if (::ImmAssociateContextEx(hwnd, nullptr, IACE_DEFAULT) == FALSE) { + log::Warn("WinInputMethodContext: Failed to enable ime."); + } } void WinInputMethodContext::DisableIME() { - // TODO! + const auto native_window = Resolve(native_window_resolver_.get()); + if (native_window == nullptr) return; + const auto hwnd = native_window->GetWindowHandle(); + + if (::ImmAssociateContextEx(hwnd, nullptr, 0) == FALSE) { + log::Warn("WinInputMethodContext: Failed to disable ime."); + } } void WinInputMethodContext::CompleteComposition() { - // TODO! + // TODO: Not implemented. } void WinInputMethodContext::CancelComposition() { - // TODO! + // TODO: Not implemented. } const CompositionText& WinInputMethodContext::GetCompositionText() { - // TODO! + // TODO: Not implemented. } void WinInputMethodContext::SetCandidateWindowPosition(const Point& point) { auto optional_himc = TryGetHIMC(); if (!optional_himc.has_value()) return; - auto himc = std::move(optional_himc).value(); + auto himc = *std::move(optional_himc); ::CANDIDATEFORM form; form.dwIndex = 1; |