aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-11-29 23:03:02 +0800
committerYuqian Yang <crupest@crupest.life>2025-11-29 23:03:02 +0800
commit7d20efa165289db22c7ff620f1c59f422a025820 (patch)
tree13ac01b68878f83eade98937ffb462f9394d23c5
parentc9a461c52b37156f14944caa085bb794c184e5e3 (diff)
downloadcru-7d20efa165289db22c7ff620f1c59f422a025820.tar.gz
cru-7d20efa165289db22c7ff620f1c59f422a025820.tar.bz2
cru-7d20efa165289db22c7ff620f1c59f422a025820.zip
Impl sdl input method context.
-rw-r--r--include/cru/platform/gui/InputMethod.h6
-rw-r--r--include/cru/platform/gui/sdl/InputMethod.h42
-rw-r--r--include/cru/platform/gui/sdl/Window.h7
-rw-r--r--src/platform/gui/sdl/CMakeLists.txt1
-rw-r--r--src/platform/gui/sdl/InputMethod.cpp66
-rw-r--r--src/platform/gui/sdl/UiApplication.cpp36
-rw-r--r--src/platform/gui/sdl/Window.cpp41
7 files changed, 162 insertions, 37 deletions
diff --git a/include/cru/platform/gui/InputMethod.h b/include/cru/platform/gui/InputMethod.h
index 2d5d0b76..e2c34432 100644
--- a/include/cru/platform/gui/InputMethod.h
+++ b/include/cru/platform/gui/InputMethod.h
@@ -64,3 +64,9 @@ struct IInputMethodContext : virtual IPlatformResource {
virtual IEvent<const std::string&>* TextEvent() = 0;
};
} // namespace cru::platform::gui
+
+#define CRU_DEFINE_CRU_PLATFORM_GUI_I_INPUT_METHOD_OVERRIDE_EVENTS() \
+ CRU_DEFINE_EVENT_OVERRIDE(CompositionStart, std::nullptr_t) \
+ CRU_DEFINE_EVENT_OVERRIDE(CompositionEnd, std::nullptr_t) \
+ CRU_DEFINE_EVENT_OVERRIDE(Composition, std::nullptr_t) \
+ CRU_DEFINE_EVENT_OVERRIDE(Text, const std::string&)
diff --git a/include/cru/platform/gui/sdl/InputMethod.h b/include/cru/platform/gui/sdl/InputMethod.h
new file mode 100644
index 00000000..77a78999
--- /dev/null
+++ b/include/cru/platform/gui/sdl/InputMethod.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Base.h"
+
+#include <cru/platform/gui/InputMethod.h>
+
+#include <SDL3/SDL_events.h>
+#include <SDL3/SDL_video.h>
+
+namespace cru::platform::gui::sdl {
+class SdlWindow;
+
+class SdlInputMethodContext : public SdlResource,
+ public virtual IInputMethodContext {
+ friend SdlWindow;
+
+ public:
+ explicit SdlInputMethodContext(SdlWindow* window);
+
+ bool ShouldManuallyDrawCompositionText() override;
+ void EnableIME() override;
+ void DisableIME() override;
+
+ /**
+ * Note: SDL doesn't support this. So it actually calls CancelComposition().
+ */
+ void CompleteComposition() override;
+ void CancelComposition() override;
+
+ CompositionText GetCompositionText() override;
+
+ void SetCandidateWindowPosition(const Point& point) override;
+
+ CRU_DEFINE_CRU_PLATFORM_GUI_I_INPUT_METHOD_OVERRIDE_EVENTS()
+
+ private:
+ bool HandleEvent(const SDL_Event* event);
+
+ private:
+ SdlWindow* window_;
+};
+} // namespace cru::platform::gui::sdl
diff --git a/include/cru/platform/gui/sdl/Window.h b/include/cru/platform/gui/sdl/Window.h
index 58dd9785..aaa0c725 100644
--- a/include/cru/platform/gui/sdl/Window.h
+++ b/include/cru/platform/gui/sdl/Window.h
@@ -6,10 +6,10 @@
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_video.h>
-#include <optional>
namespace cru::platform::gui::sdl {
class SdlUiApplication;
+class SdlInputMethodContext;
class SdlWindow : public SdlResource, public virtual INativeWindow {
CRU_DEFINE_CLASS_LOG_TAG("cru::platform::gui::xcb::SdlWindow")
@@ -79,7 +79,8 @@ class SdlWindow : public SdlResource, public virtual INativeWindow {
IInputMethodContext* GetInputMethodContext() override;
public:
- std::optional<SDL_Window*> GetSdlWindow();
+ SDL_Window* GetSdlWindow();
+ SDL_WindowID GetSdlWindowId();
SdlUiApplication* GetSdlUiApplication();
float GetDisplayScale();
Thickness GetBorderThickness();
@@ -104,5 +105,7 @@ class SdlWindow : public SdlResource, public virtual INativeWindow {
WindowStyleFlag style_;
std::string title_;
std::shared_ptr<ICursor> cursor_;
+
+ std::unique_ptr<SdlInputMethodContext> input_context_;
};
} // namespace cru::platform::gui::sdl
diff --git a/src/platform/gui/sdl/CMakeLists.txt b/src/platform/gui/sdl/CMakeLists.txt
index fac9d859..c01e8bbf 100644
--- a/src/platform/gui/sdl/CMakeLists.txt
+++ b/src/platform/gui/sdl/CMakeLists.txt
@@ -2,6 +2,7 @@ add_library(CruPlatformGuiSdl
Base.cpp
Cursor.cpp
Input.cpp
+ InputMethod.cpp
UiApplication.cpp
Window.cpp
)
diff --git a/src/platform/gui/sdl/InputMethod.cpp b/src/platform/gui/sdl/InputMethod.cpp
new file mode 100644
index 00000000..0cd22544
--- /dev/null
+++ b/src/platform/gui/sdl/InputMethod.cpp
@@ -0,0 +1,66 @@
+#include "cru/platform/gui/sdl/InputMethod.h"
+#include "SDL3/SDL_events.h"
+#include "cru/platform/gui/sdl/Base.h"
+#include "cru/platform/gui/sdl/Window.h"
+
+#include <SDL3/SDL_keyboard.h>
+
+namespace cru::platform::gui::sdl {
+SdlInputMethodContext::SdlInputMethodContext(SdlWindow* window)
+ : window_(window) {}
+
+bool SdlInputMethodContext::ShouldManuallyDrawCompositionText() {
+ return false;
+}
+
+void SdlInputMethodContext::EnableIME() {
+ if (auto sdl_window = window_->GetSdlWindow()) {
+ CheckSdlReturn(SDL_StartTextInput(sdl_window));
+ }
+}
+void SdlInputMethodContext::DisableIME() {
+ if (auto sdl_window = window_->GetSdlWindow()) {
+ CheckSdlReturn(SDL_StopTextInput(sdl_window));
+ }
+}
+
+void SdlInputMethodContext::CompleteComposition() { CancelComposition(); }
+
+void SdlInputMethodContext::CancelComposition() {
+ if (auto sdl_window = window_->GetSdlWindow()) {
+ CheckSdlReturn(SDL_ClearComposition(sdl_window));
+ }
+}
+
+CompositionText SdlInputMethodContext::GetCompositionText() { return {}; }
+
+void SdlInputMethodContext::SetCandidateWindowPosition(const Point& point) {
+ if (auto sdl_window = window_->GetSdlWindow()) {
+ SDL_Rect rect{static_cast<int>(point.x), static_cast<int>(point.y), 1, 1};
+ CheckSdlReturn(SDL_SetTextInputArea(sdl_window, &rect, 0));
+ }
+}
+
+namespace {
+std::optional<SDL_WindowID> GetEventWindowId(const SDL_Event& event) {
+ switch (event.type) {
+ case SDL_EVENT_TEXT_INPUT:
+ return event.text.windowID;
+ default:
+ return std::nullopt;
+ }
+}
+} // namespace
+
+bool SdlInputMethodContext::HandleEvent(const SDL_Event* event) {
+ if (window_->GetSdlWindowId() != GetEventWindowId(*event)) return false;
+
+ if (event->type == SDL_EVENT_TEXT_INPUT) {
+ std::string text(event->text.text);
+ TextEvent_.Raise(text);
+ return true;
+ }
+ return false;
+}
+
+} // namespace cru::platform::gui::sdl
diff --git a/src/platform/gui/sdl/UiApplication.cpp b/src/platform/gui/sdl/UiApplication.cpp
index d2275a37..6d52b5f7 100644
--- a/src/platform/gui/sdl/UiApplication.cpp
+++ b/src/platform/gui/sdl/UiApplication.cpp
@@ -157,40 +157,10 @@ long long SdlUiApplication::SetTimer(std::chrono::milliseconds milliseconds,
return timers_.Add(std::move(action), milliseconds, repeat);
}
-namespace {
-std::optional<SDL_WindowID> GetEventWindowId(const SDL_Event& event) {
- switch (event.type) {
- case SDL_EVENT_WINDOW_MOVED:
- case SDL_EVENT_WINDOW_RESIZED:
- case SDL_EVENT_WINDOW_SHOWN:
- case SDL_EVENT_WINDOW_HIDDEN:
- case SDL_EVENT_WINDOW_MINIMIZED:
- case SDL_EVENT_WINDOW_FOCUS_GAINED:
- case SDL_EVENT_WINDOW_FOCUS_LOST:
- case SDL_EVENT_WINDOW_MOUSE_ENTER:
- case SDL_EVENT_WINDOW_MOUSE_LEAVE:
- case SDL_EVENT_WINDOW_DESTROYED:
- return event.window.windowID;
- case SDL_EVENT_MOUSE_BUTTON_DOWN:
- case SDL_EVENT_MOUSE_BUTTON_UP:
- return event.button.windowID;
- case SDL_EVENT_MOUSE_WHEEL:
- return event.wheel.windowID;
- case SDL_EVENT_KEY_DOWN:
- case SDL_EVENT_KEY_UP:
- return event.key.windowID;
- default:
- return std::nullopt;
- }
-}
-} // namespace
-
bool SdlUiApplication::DispatchEvent(const SDL_Event& event) {
- if (auto window_id = GetEventWindowId(event)) {
- for (auto window : windows_) {
- if (window->sdl_window_id_ == *window_id && window->HandleEvent(&event)) {
- return true;
- }
+ for (auto window : windows_) {
+ if (window->HandleEvent(&event)) {
+ return true;
}
}
return false;
diff --git a/src/platform/gui/sdl/Window.cpp b/src/platform/gui/sdl/Window.cpp
index f4b0422e..a4dea5ad 100644
--- a/src/platform/gui/sdl/Window.cpp
+++ b/src/platform/gui/sdl/Window.cpp
@@ -8,6 +8,7 @@
#include "cru/platform/gui/sdl/Base.h"
#include "cru/platform/gui/sdl/Cursor.h"
#include "cru/platform/gui/sdl/Input.h"
+#include "cru/platform/gui/sdl/InputMethod.h"
#include "cru/platform/gui/sdl/UiApplication.h"
#include <SDL3/SDL_events.h>
@@ -25,6 +26,8 @@ SdlWindow::SdlWindow(SdlUiApplication* application)
client_rect_(100, 100, 400, 200),
parent_(nullptr) {
application->RegisterWindow(this);
+
+ input_context_ = std::make_unique<SdlInputMethodContext>(this);
}
SdlWindow::~SdlWindow() { application_->UnregisterWindow(this); }
@@ -182,9 +185,13 @@ std::unique_ptr<graphics::IPainter> SdlWindow::BeginPaint() {
NotImplemented();
}
-IInputMethodContext* SdlWindow::GetInputMethodContext() { NotImplemented(); }
+IInputMethodContext* SdlWindow::GetInputMethodContext() {
+ return input_context_.get();
+}
+
+SDL_Window* SdlWindow::GetSdlWindow() { return sdl_window_; }
-std::optional<SDL_Window*> SdlWindow::GetSdlWindow() { return sdl_window_; }
+SDL_WindowID SdlWindow::GetSdlWindowId() { return sdl_window_id_; }
SdlUiApplication* SdlWindow::GetSdlUiApplication() { return application_; }
@@ -280,9 +287,39 @@ NativeMouseButtonEventArgs ConvertMouseButtonEvent(
NativeKeyEventArgs ConvertKeyEvent(const SDL_KeyboardEvent& event) {
return {ConvertKeyScanCode(event.scancode), ConvertKeyModifier(event.mod)};
}
+
+std::optional<SDL_WindowID> GetEventWindowId(const SDL_Event& event) {
+ switch (event.type) {
+ case SDL_EVENT_WINDOW_MOVED:
+ case SDL_EVENT_WINDOW_RESIZED:
+ case SDL_EVENT_WINDOW_SHOWN:
+ case SDL_EVENT_WINDOW_HIDDEN:
+ case SDL_EVENT_WINDOW_MINIMIZED:
+ case SDL_EVENT_WINDOW_FOCUS_GAINED:
+ case SDL_EVENT_WINDOW_FOCUS_LOST:
+ case SDL_EVENT_WINDOW_MOUSE_ENTER:
+ case SDL_EVENT_WINDOW_MOUSE_LEAVE:
+ case SDL_EVENT_WINDOW_DESTROYED:
+ return event.window.windowID;
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
+ case SDL_EVENT_MOUSE_BUTTON_UP:
+ return event.button.windowID;
+ case SDL_EVENT_MOUSE_WHEEL:
+ return event.wheel.windowID;
+ case SDL_EVENT_KEY_DOWN:
+ case SDL_EVENT_KEY_UP:
+ return event.key.windowID;
+ default:
+ return std::nullopt;
+ }
+}
} // namespace
bool SdlWindow::HandleEvent(const SDL_Event* event) {
+ if (input_context_->HandleEvent(event)) return true;
+
+ if (sdl_window_id_ != GetEventWindowId(*event)) return false;
+
switch (event->type) {
case SDL_EVENT_WINDOW_MOVED: {
client_rect_.left = event->window.data1;