Skip to content

Commit

Permalink
Respect the text input length restriction with IME composition.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawnCZek committed Mar 16, 2024
1 parent 68f6f84 commit e022074
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 11 deletions.
5 changes: 4 additions & 1 deletion Include/RmlUi/Core/TextInputMethodContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ class RMLUICORE_API TextInputMethodContext {
/// @param[in] end The first character *after* the range.
virtual void SetText(StringView text, int start, int end) = 0;

/// Update the range of the text being composed (e.g., for visual feedback).
/// Update the range of the text being composed.
/// @param[in] start The first character in the range.
/// @param[in] end The first character *after* the range.
virtual void SetCompositionRange(int start, int end) = 0;

/// Commit the current composition.
virtual void CommitComposition() = 0;
};

} // namespace Rml
Expand Down
8 changes: 4 additions & 4 deletions Samples/basic/ime/data/ime.rml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
input[type="text"], textarea {
width: 100%;
border: 1px gray;
margin-bottom: 10dp;
margin-bottom: 10dp;
}

.note {
Expand All @@ -36,10 +36,10 @@
<body>
<form>
<label for="text">Input text</label>
<input id="text" type="text" name="text" />
<input id="text" type="text" name="text" maxlength=100 />

<label for="text_area">Type long text</label>
<textarea rows=4 id="text_area" name="text_area"></textarea>
<label for="text_area">Type long text</label>
<textarea rows=4 id="text_area" name="text_area"></textarea>
</form>

<p class="note">Note: the emoji keyboard and clipboard history use IME, too.</p>
Expand Down
50 changes: 44 additions & 6 deletions Source/Core/Elements/WidgetTextInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,17 @@ class WidgetTextInputIMEContext final : public TextInputMethodContext {
WidgetTextInputIMEContext(WidgetTextInput* _owner);
~WidgetTextInputIMEContext() = default;

virtual void GetScreenBounds(Vector2f& position, Vector2f& size) const override;
virtual void GetSelectionRange(int& start, int& end) const override;
virtual void SetSelectionRange(int start, int end) override;
virtual void SetCursorPosition(int position) override;
virtual void SetText(StringView text, int start, int end) override;
virtual void SetCompositionRange(int start, int end) override;
void GetScreenBounds(Vector2f& position, Vector2f& size) const override;
void GetSelectionRange(int& start, int& end) const override;
void SetSelectionRange(int start, int end) override;
void SetCursorPosition(int position) override;
void SetText(StringView text, int start, int end) override;
void SetCompositionRange(int start, int end) override;
void CommitComposition() override;

private:
WidgetTextInput* owner;
String composition;
};

WidgetTextInputIMEContext::WidgetTextInputIMEContext(WidgetTextInput* _owner) : owner(_owner) {}
Expand Down Expand Up @@ -129,13 +131,49 @@ void WidgetTextInputIMEContext::SetText(StringView text, int start, int end)
value.replace(start, end - start, text.begin(), text.size());

owner->parent->SetValue(value);

composition = String(text);
}

void WidgetTextInputIMEContext::SetCompositionRange(int start, int end)
{
owner->SetIMERange(start, end);
}

void WidgetTextInputIMEContext::CommitComposition()
{
const int start_byte = owner->ime_composition_begin_index;
const int end_byte = owner->ime_composition_end_index;

// No composition to commit.
if (start_byte == 0 && end_byte == 0)
return;

String value = owner->GetAttributeValue();

// If the text input has a length restriction, we have to shorten the composition string.
if (owner->GetMaxLength() >= 0)
{
int start = StringUtilities::ConvertByteOffsetToCharacterOffset(value, start_byte);
int end = StringUtilities::ConvertByteOffsetToCharacterOffset(value, end_byte);

int value_length = (int)StringUtilities::LengthUTF8(value);
int composition_length = (int)StringUtilities::LengthUTF8(composition);

// The requested text value would exceed the length restriction after replacing the original value.
if (value_length + composition_length - (start - end) > owner->GetMaxLength())
{
int new_length = owner->GetMaxLength() - (value_length - composition_length);
composition.erase(StringUtilities::ConvertCharacterOffsetToByteOffset(composition, new_length));
}
}

RMLUI_ASSERTMSG(end_byte >= start_byte, "Invalid end character offset.");
value.replace(start_byte, end_byte - start_byte, composition.data(), composition.size());

owner->parent->SetValue(value);
}

WidgetTextInput::WidgetTextInput(ElementFormControl* _parent) :
internal_dimensions(0, 0), scroll_offset(0, 0), cursor_position(0, 0), cursor_size(0, 0)
{
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/TextInputMethodEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ void DefaultTextInputMethodEditor::IMEConfirmComposition(StringView composition)
RMLUI_ASSERT(IsComposing());

SetCompositionString(composition);

RMLUI_ASSERT(!context.expired());
SharedPtr<TextInputMethodContext> _context = context.lock();

_context->SetCompositionRange(composition_range_start, composition_range_end);
_context->CommitComposition();

// Move the cursor to the end of the string.
SetCursorPosition(composition_range_end - composition_range_start, true);

Expand Down

0 comments on commit e022074

Please sign in to comment.