aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-04-17 00:49:46 +0800
committercrupest <crupest@outlook.com>2020-04-17 00:49:46 +0800
commitf81940e3ea386a6dfdda2ff4a42d8cede35f2c8a (patch)
treed00c4f933e2a78fe8cf563e7d2bcc7a4cb588dfa
parent39c0b5ad6814db9e46ac2cb4ab64a37e0e19749b (diff)
downloadcru-f81940e3ea386a6dfdda2ff4a42d8cede35f2c8a.tar.gz
cru-f81940e3ea386a6dfdda2ff4a42d8cede35f2c8a.tar.bz2
cru-f81940e3ea386a6dfdda2ff4a42d8cede35f2c8a.zip
...
-rw-r--r--include/cru/common/format.hpp122
-rw-r--r--include/cru/platform/graph_base.hpp28
-rw-r--r--include/cru/platform/native/input_method.hpp9
-rw-r--r--src/win/native/input_method.cpp120
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;