aboutsummaryrefslogtreecommitdiff
path: root/demos/InputMethod
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-05-15 14:15:31 +0800
committercrupest <crupest@outlook.com>2022-05-15 14:15:31 +0800
commit34a64e6ffefaab007578932ddbab931a25f1d56e (patch)
tree541fdb8279e829a129df62288d09916bf23c9200 /demos/InputMethod
parent8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6 (diff)
downloadcru-34a64e6ffefaab007578932ddbab931a25f1d56e.tar.gz
cru-34a64e6ffefaab007578932ddbab931a25f1d56e.tar.bz2
cru-34a64e6ffefaab007578932ddbab931a25f1d56e.zip
...
Diffstat (limited to 'demos/InputMethod')
-rw-r--r--demos/InputMethod/CMakeLists.txt12
-rw-r--r--demos/InputMethod/main.cpp149
2 files changed, 161 insertions, 0 deletions
diff --git a/demos/InputMethod/CMakeLists.txt b/demos/InputMethod/CMakeLists.txt
new file mode 100644
index 00000000..8ceeaec9
--- /dev/null
+++ b/demos/InputMethod/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_executable(CruDemoInputMethod main.cpp)
+
+if(APPLE)
+ set_target_properties(CruDemoInputMethod PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_BUNDLE_NAME demo-input-method
+ MACOSX_BUNDLE_GUI_IDENTIFIER life.crupest.cru.demo-input-method
+ )
+endif()
+
+target_link_libraries(CruDemoInputMethod PRIVATE CruDemoBase)
+
diff --git a/demos/InputMethod/main.cpp b/demos/InputMethod/main.cpp
new file mode 100644
index 00000000..abbbed2c
--- /dev/null
+++ b/demos/InputMethod/main.cpp
@@ -0,0 +1,149 @@
+#include "cru/platform/Color.h"
+#include "cru/platform/bootstrap/Bootstrap.h"
+#include "cru/platform/graphics/Factory.h"
+#include "cru/platform/graphics/Font.h"
+#include "cru/platform/graphics/Painter.h"
+#include "cru/platform/gui/InputMethod.h"
+#include "cru/platform/gui/UiApplication.h"
+#include "cru/platform/gui/Window.h"
+
+int main() {
+ using namespace cru;
+ using namespace cru::platform;
+ using namespace cru::platform::graphics;
+ using namespace cru::platform::gui;
+
+ IUiApplication* application = bootstrap::CreateUiApplication();
+
+ auto graphics_factory = application->GetGraphicsFactory();
+
+ auto window = application->CreateWindow();
+
+ auto input_method_context = window->GetInputMethodContext();
+
+ auto brush = graphics_factory->CreateSolidColorBrush();
+ brush->SetColor(colors::black);
+
+ auto odd_clause_brush = graphics_factory->CreateSolidColorBrush();
+ odd_clause_brush->SetColor(colors::yellow);
+ auto even_clause_brush = graphics_factory->CreateSolidColorBrush();
+ even_clause_brush->SetColor(colors::green);
+ auto target_clause_brush = graphics_factory->CreateSolidColorBrush();
+ target_clause_brush->SetColor(colors::blue);
+
+ std::shared_ptr<IFont> font = graphics_factory->CreateFont(String{}, 30);
+
+ float window_width = 10000;
+
+ auto prompt_text_layout =
+ graphics_factory->CreateTextLayout(font,
+ u"Ctrl+1: Enable IME\n"
+ u"Ctrl+2: Disable IME\n"
+ u"Ctrl+3: Complete composition.\n"
+ u"Ctrl+4: Cancel composition.");
+
+ std::optional<CompositionText> optional_composition_text;
+ String committed_text;
+
+ window->ResizeEvent()->AddHandler(
+ [&prompt_text_layout, &window_width](const Size& size) {
+ prompt_text_layout->SetMaxWidth(size.width);
+ window_width = size.width;
+ });
+
+ window->PaintEvent()->AddHandler([&](auto) {
+ auto painter = window->BeginPaint();
+ painter->Clear(colors::white);
+
+ painter->DrawText(Point{}, prompt_text_layout.get(), brush.get());
+
+ const auto anchor_y = prompt_text_layout->GetTextBounds().height;
+
+ auto text_layout = graphics_factory->CreateTextLayout(
+ font, committed_text + (optional_composition_text
+ ? optional_composition_text->text
+ : u""));
+ text_layout->SetMaxWidth(window_width);
+
+ if (optional_composition_text) {
+ const auto& composition_text = *optional_composition_text;
+
+ for (int i = 0; i < static_cast<int>(composition_text.clauses.size());
+ i++) {
+ const auto& clause = composition_text.clauses[i];
+ auto rects = text_layout->TextRangeRect(TextRange::FromTwoSides(
+ clause.start, clause.end, committed_text.size()));
+ const auto& b = clause.target
+ ? target_clause_brush
+ : (i % 2 ? odd_clause_brush : even_clause_brush);
+ for (auto& rect : rects) {
+ rect.top += anchor_y;
+ painter->FillRectangle(rect, b.get());
+ }
+ }
+ }
+
+ painter->DrawText(Point{0, anchor_y}, text_layout.get(), brush.get());
+
+ if (optional_composition_text) {
+ const auto& composition_text = *optional_composition_text;
+
+ const auto cursor_pos = composition_text.selection.position +
+ gsl::narrow_cast<int>(committed_text.size());
+
+ const auto cursor_lefttop =
+ text_layout->TextSinglePoint(cursor_pos, false);
+
+ painter->FillRectangle(
+ Rect{cursor_lefttop.left, cursor_lefttop.top + anchor_y, 3,
+ cursor_lefttop.height},
+ brush.get());
+ }
+
+ painter->EndDraw();
+ });
+
+ window->KeyDownEvent()->AddHandler(
+ [&input_method_context](const NativeKeyEventArgs& args) {
+ if (args.modifier & KeyModifiers::ctrl) {
+ switch (args.key) {
+ case KeyCode::N1:
+ input_method_context->EnableIME();
+ break;
+ case KeyCode::N2:
+ input_method_context->DisableIME();
+ break;
+ case KeyCode::N3:
+ input_method_context->CompleteComposition();
+ break;
+ case KeyCode::N4:
+ input_method_context->CancelComposition();
+ break;
+ default:
+ break;
+ }
+ }
+ });
+
+ input_method_context->TextEvent()->AddHandler(
+ [window, &committed_text](const StringView& c) {
+ committed_text += c;
+ window->RequestRepaint();
+ });
+
+ input_method_context->CompositionEvent()->AddHandler(
+ [window, &input_method_context, &optional_composition_text](auto) {
+ optional_composition_text = input_method_context->GetCompositionText();
+ window->RequestRepaint();
+ });
+
+ input_method_context->CompositionEndEvent()->AddHandler(
+ [window, &optional_composition_text](auto) {
+ optional_composition_text = std::nullopt;
+ window->RequestRepaint();
+ });
+
+ window->SetVisibility(WindowVisibilityType::Show);
+
+ return application->Run();
+}