Fix chat text render and chat shiftlock issues

Fixes 106, Fixes #127, Refs #116
This commit is contained in:
komaruworld committed 2026-09-12 14:45:29 +03:00
1 parent c523048518
commit 0548ebd253
13 files changed
+206 -16

No files matched your search

+4 -1
View File
@@ -526,7 +526,10 @@ RobloxInputDispatchResult RobloxInputRouter::HandleEvent(
RobloxInputSnapshot RobloxInputRouter::Snapshot() const {
std::lock_guard<std::mutex> lock(mutex_);
return snapshot_;
RobloxInputSnapshot snapshot = snapshot_;
const RobloxTextEditorSnapshot text = text_editor_.Snapshot();
snapshot.text_focus_generation = text.focused ? text.generation : 0;
return snapshot;
}
RobloxInputDispatchResult RobloxInputRouter::HandleMouseMotionLocked(
+2 -4
View File
@@ -14,8 +14,6 @@ namespace mocktail {
namespace runtime {
namespace {
constexpr int kMinimumNativeWidth = 32;
constexpr int kMinimumNativeHeight = 18;
constexpr char kPasswordBullet[] = "\xE2\x80\xA2";
void SecureClear(std::string* value) {
@@ -83,8 +81,8 @@ bool ValidateAndCountUtf8(const char* text, std::size_t size,
bool NativeGeometryIntersectsViewport(
const RobloxTextDisplayUpdate& update,
const RobloxTextOverlayViewport& viewport) {
if (!viewport.valid() || update.area_width < kMinimumNativeWidth ||
update.area_height < kMinimumNativeHeight) {
if (!viewport.valid() || update.area_width <= 0 ||
update.area_height <= 0) {
return false;
}
const int64_t right = static_cast<int64_t>(update.area_x) +
@@ -142,6 +142,9 @@ class ProductionTextInputBackend final
return runtime_->EndTextFocusSession(textbox_handle, generation,
notify_native);
}
bool IsTextFocusSessionActive(uint64_t generation) override {
return runtime_->Snapshot().text_focus_generation == generation;
}
Status ReplaceFocusedTextFromEngine(uint64_t generation,
std::string authoritative_utf8) override {
return runtime_->ReplaceFocusedTextFromEngine(
@@ -559,6 +562,44 @@ struct RobloxTextInputJniBridge::State {
return CompleteTerminalOnMainThread("queue");
}
// RbxKeyboard hides its own editor after Enter/Escape. The native side
// need not echo hideKeyboard, so reconcile the host editor before polling
// properties or replaying delayed text updates from that focus session.
if (applied_active &&
!backend->IsTextFocusSessionActive(applied_generation)) {
const uint64_t ended_generation = applied_generation;
if (!backend->RequestHideTextInput(ended_generation)) {
for (Command& command : pending) command.ClearSensitiveData();
{
std::lock_guard<std::mutex> lock(mutex);
EnterTerminalLocked("SDL text-input completion hide was rejected");
}
return CompleteTerminalOnMainThread("host-focus-end");
}
{
std::lock_guard<std::mutex> lock(mutex);
if (active_generation == ended_generation) {
desired_active = false;
active_handle = 0;
active_info = {};
active_geometry = {};
has_active_geometry = false;
geometry_refresh_pumps = 0;
}
}
for (auto it = pending.begin(); it != pending.end();) {
if (it->generation == ended_generation) {
it->ClearSensitiveData();
it = pending.erase(it);
} else {
++it;
}
}
applied_active = false;
applied_generation = 0;
applied_handle = 0;
}
AppendGeometryRefresh(&pending);
bool success = true;
@@ -27,6 +27,7 @@ class RobloxTextInputJniBridgeBackend {
virtual Status EndTextFocusSession(int64_t textbox_handle,
uint64_t generation,
bool notify_native) = 0;
virtual bool IsTextFocusSessionActive(uint64_t generation) = 0;
virtual Status ReplaceFocusedTextFromEngine(
uint64_t generation, std::string authoritative_utf8) = 0;
virtual Status QueryCurrentTextBoxInfo(
+4 -5
View File
@@ -431,13 +431,12 @@ Status RobloxTextSurfaceOverlay::RasterizeLocked() {
static_cast<std::size_t>(width * height * 4), 0);
const int frame_width = static_cast<int>(width);
const int frame_height = static_cast<int>(height);
// Use Roblox's horizontal text bounds directly. Inventing padding from
// the field's width or position shifts text away from its native origin
// and makes long lines scroll before they reach the field's actual edge.
// Use Roblox's text bounds directly. Extra padding shifts the native text
// origin, scrolls long lines too early and clips compact chat fields.
const int clip_left = 0;
const int clip_right = frame_width;
const int clip_top = 3;
const int clip_bottom = std::max(clip_top, frame_height - 3);
const int clip_top = 0;
const int clip_bottom = frame_height;
const int available_width = std::max(1, clip_right - clip_left);
const bool wrapped_layout =
presentation.multiline || presentation.text_wrapped;