Skip to content

Commit

Permalink
Move the ownership of the IME editor to backends.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawnCZek committed Jun 28, 2024
1 parent d944506 commit 9abf64c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 68 deletions.
12 changes: 10 additions & 2 deletions Backends/RmlUi_Backend_Win32_GL2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static void DetachFromNative(HWND window_handle, HDC device_context, HGLRC rende
struct BackendData {
SystemInterface_Win32 system_interface;
RenderInterface_GL2 render_interface;
TextInputMethodEditor_Win32 text_input_method_editor;

HINSTANCE instance_handle = nullptr;
std::wstring instance_name;
Expand Down Expand Up @@ -158,13 +159,20 @@ bool Backend::Initialize(const char* window_name, int width, int height, bool al
::SetForegroundWindow(window_handle);
::SetFocus(window_handle);

// Provide a backend-specific text input handler to manage the IME.
Rml::SetTextInputHandler(&data->text_input_method_editor);

return true;
}

void Backend::Shutdown()
{
RMLUI_ASSERT(data);

// As we forcefully override the global text input handler, we must reset it before the data is destroyed to avoid any potential use-after-free.
if (Rml::GetTextInputHandler() == &data->text_input_method_editor)
Rml::SetTextInputHandler(nullptr);

DetachFromNative(data->window_handle, data->device_context, data->render_context);

::DestroyWindow(data->window_handle);
Expand Down Expand Up @@ -307,7 +315,7 @@ static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message,
if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, true))
return 0;
// Otherwise, hand the event over to the context by calling the input handler as normal.
if (!RmlWin32::WindowProcedure(context, window_handle, message, w_param, l_param))
if (!RmlWin32::WindowProcedure(context, data->text_input_method_editor, window_handle, message, w_param, l_param))
return 0;
// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, false))
Expand All @@ -318,7 +326,7 @@ static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message,
default:
{
// Submit it to the platform handler for default input handling.
if (!RmlWin32::WindowProcedure(data->context, window_handle, message, w_param, l_param))
if (!RmlWin32::WindowProcedure(data->context, data->text_input_method_editor, window_handle, message, w_param, l_param))
return 0;
}
break;
Expand Down
12 changes: 10 additions & 2 deletions Backends/RmlUi_Backend_Win32_VK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ static bool CreateVulkanSurface(VkInstance instance, VkSurfaceKHR* out_surface);
struct BackendData {
SystemInterface_Win32 system_interface;
RenderInterface_VK render_interface;
TextInputMethodEditor_Win32 text_input_method_editor;

HINSTANCE instance_handle = nullptr;
std::wstring instance_name;
Expand Down Expand Up @@ -160,13 +161,20 @@ bool Backend::Initialize(const char* window_name, int width, int height, bool al
::SetForegroundWindow(window_handle);
::SetFocus(window_handle);

// Provide a backend-specific text input handler to manage the IME.
Rml::SetTextInputHandler(&data->text_input_method_editor);

return true;
}

void Backend::Shutdown()
{
RMLUI_ASSERT(data);

// As we forcefully override the global text input handler, we must reset it before the data is destroyed to avoid any potential use-after-free.
if (Rml::GetTextInputHandler() == &data->text_input_method_editor)
Rml::SetTextInputHandler(nullptr);

data->render_interface.Shutdown();

::DestroyWindow(data->window_handle);
Expand Down Expand Up @@ -315,7 +323,7 @@ static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message,
if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, true))
return 0;
// Otherwise, hand the event over to the context by calling the input handler as normal.
if (!RmlWin32::WindowProcedure(context, window_handle, message, w_param, l_param))
if (!RmlWin32::WindowProcedure(context, data->text_input_method_editor, window_handle, message, w_param, l_param))
return 0;
// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
if (key_down_callback && !key_down_callback(context, rml_key, rml_modifier, native_dp_ratio, false))
Expand All @@ -326,7 +334,7 @@ static LRESULT CALLBACK WindowProcedureHandler(HWND window_handle, UINT message,
default:
{
// Submit it to the platform handler for default input handling.
if (!RmlWin32::WindowProcedure(data->context, window_handle, message, w_param, l_param))
if (!RmlWin32::WindowProcedure(data->context, data->text_input_method_editor, window_handle, message, w_param, l_param))
return 0;
}
break;
Expand Down
77 changes: 14 additions & 63 deletions Backends/RmlUi_Platform_Win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,54 +42,6 @@
#pragma comment(lib, "imm32")
#endif

class TextInputMethodEditor final : public Rml::TextInputHandler {
public:
TextInputMethodEditor();

void OnFocus(Rml::SharedPtr<Rml::TextInputContext> input_context) override;
void OnBlur(Rml::TextInputContext* input_context) override;

/// Check that a composition is currently active.
/// @return True if we are composing, false otherwise.
bool IsComposing() const;

void StartComposition();
void CancelComposition();

/// Set the composition string.
/// @param[in] composition A string to be set.
void SetComposition(Rml::StringView composition);

/// End the current composition by confirming the composition string.
/// @param[in] composition A string to confirm.
void ConfirmComposition(Rml::StringView composition);

/// Set the cursor position within the composition.
/// @param[in] cursor_pos A character position of the cursor within the composition string.
/// @param[in] update Update the cursor position within active input contexts.
void SetCursorPosition(int cursor_pos, bool update);

private:
void EndComposition();
void SetCompositionString(Rml::StringView composition);

void UpdateCursorPosition();

private:
// An actively used text input method context.
Rml::WeakPtr<Rml::TextInputContext> input_context;

// A flag to mark a composition is currently active.
bool composing;
// Character position of the cursor in the composition string.
int cursor_pos;

// Composition range (character position) relative to the text input value.
int composition_range_start;
int composition_range_end;
};
static TextInputMethodEditor text_input_method_editor;

SystemInterface_Win32::SystemInterface_Win32()
{
LARGE_INTEGER time_ticks_per_second;
Expand All @@ -106,8 +58,6 @@ SystemInterface_Win32::SystemInterface_Win32()
cursor_cross = LoadCursor(nullptr, IDC_CROSS);
cursor_text = LoadCursor(nullptr, IDC_IBEAM);
cursor_unavailable = LoadCursor(nullptr, IDC_NO);

Rml::SetTextInputHandler(&text_input_method_editor);
}

SystemInterface_Win32::~SystemInterface_Win32() = default;
Expand Down Expand Up @@ -264,7 +214,8 @@ static std::wstring IMEGetCompositionString(HIMC context, bool finalize)
#endif
}

bool RmlWin32::WindowProcedure(Rml::Context* context, HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param)
bool RmlWin32::WindowProcedure(Rml::Context* context, TextInputMethodEditor_Win32& text_input_method_editor, HWND window_handle, UINT message,
WPARAM w_param, LPARAM l_param)
{
if (!context)
return true;
Expand Down Expand Up @@ -654,31 +605,31 @@ Rml::Input::KeyIdentifier RmlWin32::ConvertKey(int win32_key_code)
return Rml::Input::KI_UNKNOWN;
}

TextInputMethodEditor::TextInputMethodEditor() : composing(false), cursor_pos(-1), composition_range_start(0), composition_range_end(0) {}
TextInputMethodEditor_Win32::TextInputMethodEditor_Win32() : composing(false), cursor_pos(-1), composition_range_start(0), composition_range_end(0) {}

void TextInputMethodEditor::OnFocus(Rml::SharedPtr<Rml::TextInputContext> _input_context)
void TextInputMethodEditor_Win32::OnFocus(Rml::SharedPtr<Rml::TextInputContext> _input_context)
{
input_context = _input_context;
}

void TextInputMethodEditor::OnBlur(Rml::TextInputContext* _input_context)
void TextInputMethodEditor_Win32::OnBlur(Rml::TextInputContext* _input_context)
{
if (input_context.lock().get() == _input_context)
input_context.reset();
}

bool TextInputMethodEditor::IsComposing() const
bool TextInputMethodEditor_Win32::IsComposing() const
{
return composing;
}

void TextInputMethodEditor::StartComposition()
void TextInputMethodEditor_Win32::StartComposition()
{
RMLUI_ASSERT(!composing);
composing = true;
}

void TextInputMethodEditor::EndComposition()
void TextInputMethodEditor_Win32::EndComposition()
{
if (Rml::SharedPtr<Rml::TextInputContext> _input_context = input_context.lock())
_input_context->SetCompositionRange(0, 0);
Expand All @@ -690,7 +641,7 @@ void TextInputMethodEditor::EndComposition()
composition_range_end = 0;
}

void TextInputMethodEditor::CancelComposition()
void TextInputMethodEditor_Win32::CancelComposition()
{
RMLUI_ASSERT(IsComposing());

Expand All @@ -705,7 +656,7 @@ void TextInputMethodEditor::CancelComposition()
EndComposition();
}

void TextInputMethodEditor::SetComposition(Rml::StringView composition)
void TextInputMethodEditor_Win32::SetComposition(Rml::StringView composition)
{
RMLUI_ASSERT(IsComposing());

Expand All @@ -719,7 +670,7 @@ void TextInputMethodEditor::SetComposition(Rml::StringView composition)
_input_context->SetCompositionRange(composition_range_start, composition_range_end);
}

void TextInputMethodEditor::ConfirmComposition(Rml::StringView composition)
void TextInputMethodEditor_Win32::ConfirmComposition(Rml::StringView composition)
{
RMLUI_ASSERT(IsComposing());

Expand All @@ -737,7 +688,7 @@ void TextInputMethodEditor::ConfirmComposition(Rml::StringView composition)
EndComposition();
}

void TextInputMethodEditor::SetCursorPosition(int _cursor_pos, bool update)
void TextInputMethodEditor_Win32::SetCursorPosition(int _cursor_pos, bool update)
{
RMLUI_ASSERT(IsComposing());

Expand All @@ -747,7 +698,7 @@ void TextInputMethodEditor::SetCursorPosition(int _cursor_pos, bool update)
UpdateCursorPosition();
}

void TextInputMethodEditor::SetCompositionString(Rml::StringView composition)
void TextInputMethodEditor_Win32::SetCompositionString(Rml::StringView composition)
{
if (input_context.expired())
return;
Expand All @@ -764,7 +715,7 @@ void TextInputMethodEditor::SetCompositionString(Rml::StringView composition)
composition_range_end = composition_range_start + (int)length;
}

void TextInputMethodEditor::UpdateCursorPosition()
void TextInputMethodEditor_Win32::UpdateCursorPosition()
{
// Cursor position update happens before a composition is set; ignore this event.
if (composition_range_start == 0 && composition_range_end == 0)
Expand Down
58 changes: 57 additions & 1 deletion Backends/RmlUi_Platform_Win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@

#include "RmlUi_Include_Windows.h"
#include <RmlUi/Core/Input.h>
#include <RmlUi/Core/StringUtilities.h>
#include <RmlUi/Core/SystemInterface.h>
#include <RmlUi/Core/TextInputHandler.h>
#include <RmlUi/Core/Types.h>
#include <string>

Expand Down Expand Up @@ -69,6 +71,8 @@ class SystemInterface_Win32 : public Rml::SystemInterface {
HCURSOR cursor_unavailable = nullptr;
};

class TextInputMethodEditor_Win32;

/**
Optional helper functions for the Win32 plaform.
*/
Expand All @@ -80,7 +84,8 @@ std::wstring ConvertToUTF16(const Rml::String& str);

// Window event handler to submit default input behavior to the context.
// @return True if the event is still propagating, false if it was handled by the context.
bool WindowProcedure(Rml::Context* context, HWND window_handle, UINT message, WPARAM w_param, LPARAM l_param);
bool WindowProcedure(Rml::Context* context, TextInputMethodEditor_Win32& text_input_method_editor, HWND window_handle, UINT message, WPARAM w_param,
LPARAM l_param);

// Converts the key from Win32 key code to RmlUi key.
Rml::Input::KeyIdentifier ConvertKey(int win32_key_code);
Expand All @@ -90,4 +95,55 @@ int GetKeyModifierState();

} // namespace RmlWin32

/**
Custom backend implementation of TextInputHandler to handle the system's Input Method Editor (IME).
This version supports only one active text input context.
*/
class TextInputMethodEditor_Win32 final : public Rml::TextInputHandler {
public:
TextInputMethodEditor_Win32();

void OnFocus(Rml::SharedPtr<Rml::TextInputContext> input_context) override;
void OnBlur(Rml::TextInputContext* input_context) override;

/// Check that a composition is currently active.
/// @return True if we are composing, false otherwise.
bool IsComposing() const;

void StartComposition();
void CancelComposition();

/// Set the composition string.
/// @param[in] composition A string to be set.
void SetComposition(Rml::StringView composition);

/// End the current composition by confirming the composition string.
/// @param[in] composition A string to confirm.
void ConfirmComposition(Rml::StringView composition);

/// Set the cursor position within the composition.
/// @param[in] cursor_pos A character position of the cursor within the composition string.
/// @param[in] update Update the cursor position within active input contexts.
void SetCursorPosition(int cursor_pos, bool update);

private:
void EndComposition();
void SetCompositionString(Rml::StringView composition);

void UpdateCursorPosition();

private:
// An actively used text input method context.
Rml::WeakPtr<Rml::TextInputContext> input_context;

// A flag to mark a composition is currently active.
bool composing;
// Character position of the cursor in the composition string.
int cursor_pos;

// Composition range (character position) relative to the text input value.
int composition_range_start;
int composition_range_end;
};

#endif

0 comments on commit 9abf64c

Please sign in to comment.