Prevent excessive calls to deleteBackward method which may cause crashes on Android (#2248)

* Pass the number of characters to delete from a TextField

* Include TextFieldEx.h reference in axmoil-ui.h

* Remove logging calls used for testing

* Update parameter name
This commit is contained in:
RH 2024-11-29 02:12:21 +11:00 committed by GitHub
parent 1e8fbb991d
commit c37bcf8977
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 65 additions and 42 deletions

View File

@ -24,6 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
****************************************************************************/ ****************************************************************************/
#include <algorithm>
#include "2d/TextFieldTTF.h" #include "2d/TextFieldTTF.h"
#include "base/Director.h" #include "base/Director.h"
@ -310,7 +312,7 @@ void TextFieldTTF::insertText(const char* text, size_t len)
detachWithIME(); detachWithIME();
} }
void TextFieldTTF::deleteBackward() void TextFieldTTF::deleteBackward(size_t numChars)
{ {
size_t len = _inputText.length(); size_t len = _inputText.length();
if (!len) if (!len)
@ -319,23 +321,33 @@ void TextFieldTTF::deleteBackward()
return; return;
} }
// get the delete byte number // Length of characters to delete is based on input editor, but the actual
size_t deleteLen = 1; // default, erase 1 byte // length of the displayed text may be less
numChars = std::min(numChars, len);
while (0x80 == (0xC0 & _inputText.at(len - deleteLen))) size_t totalDeleteLen = 0;
for (auto i = 0; i < numChars; ++i)
{ {
++deleteLen; // get the delete byte number
size_t deleteLen = 1; // default, erase 1 byte
// Calculate the actual number of bytes to delete for a specific character
while (0x80 == (0xC0 & _inputText.at(len - totalDeleteLen - deleteLen)))
{
++deleteLen;
}
totalDeleteLen += deleteLen;
} }
if (_delegate && if (_delegate &&
_delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - deleteLen, static_cast<int>(deleteLen))) _delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - totalDeleteLen, static_cast<int>(totalDeleteLen)))
{ {
// delegate doesn't want to delete backwards // delegate doesn't want to delete backwards
return; return;
} }
// if all text deleted, show placeholder string // if all text deleted, show placeholder string
if (len <= deleteLen) if (len <= totalDeleteLen)
{ {
_inputText = ""; _inputText = "";
_charCount = 0; _charCount = 0;
@ -362,7 +374,7 @@ void TextFieldTTF::deleteBackward()
} }
else else
{ {
std::string text(_inputText.c_str(), len - deleteLen); std::string text(_inputText.c_str(), len - totalDeleteLen);
setString(text); setString(text);
} }
} }

View File

@ -261,7 +261,7 @@ protected:
virtual void didAttachWithIME() override; virtual void didAttachWithIME() override;
virtual void didDetachWithIME() override; virtual void didDetachWithIME() override;
virtual void insertText(const char* text, size_t len) override; virtual void insertText(const char* text, size_t len) override;
virtual void deleteBackward() override; virtual void deleteBackward(size_t numChars) override;
virtual std::string_view getContentText() override; virtual std::string_view getContentText() override;
virtual void controlKey(EventKeyboard::KeyCode keyCode) override; virtual void controlKey(EventKeyboard::KeyCode keyCode) override;

View File

@ -124,7 +124,7 @@ protected:
* @js NA * @js NA
* @lua NA * @lua NA
*/ */
virtual void deleteBackward() {} virtual void deleteBackward(size_t numChars) {}
/** /**
@brief Called by IMEDispatcher after the user press control key. @brief Called by IMEDispatcher after the user press control key.

View File

@ -222,7 +222,7 @@ void IMEDispatcher::dispatchInsertText(const char* text, size_t len)
} while (0); } while (0);
} }
void IMEDispatcher::dispatchDeleteBackward() void IMEDispatcher::dispatchDeleteBackward(int numChars)
{ {
do do
{ {
@ -231,7 +231,7 @@ void IMEDispatcher::dispatchDeleteBackward()
// there is no delegate attached to IME // there is no delegate attached to IME
AX_BREAK_IF(!_impl->_delegateWithIme); AX_BREAK_IF(!_impl->_delegateWithIme);
_impl->_delegateWithIme->deleteBackward(); _impl->_delegateWithIme->deleteBackward(numChars);
} while (0); } while (0);
} }

View File

@ -65,7 +65,7 @@ public:
* @brief Dispatches the delete-backward operation. * @brief Dispatches the delete-backward operation.
* @lua NA * @lua NA
*/ */
void dispatchDeleteBackward(); void dispatchDeleteBackward(int numChars);
/** /**
* @brief Dispatches the press control key operation. * @brief Dispatches the press control key operation.

View File

@ -1220,7 +1220,7 @@ void GLViewImpl::onGLFWKeyCallback(GLFWwindow* /*window*/, int key, int /*scanco
switch (g_keyCodeMap[key]) switch (g_keyCodeMap[key])
{ {
case EventKeyboard::KeyCode::KEY_BACKSPACE: case EventKeyboard::KeyCode::KEY_BACKSPACE:
IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(1);
break; break;
case EventKeyboard::KeyCode::KEY_HOME: case EventKeyboard::KeyCode::KEY_HOME:
case EventKeyboard::KeyCode::KEY_KP_HOME: case EventKeyboard::KeyCode::KEY_KP_HOME:

View File

@ -458,11 +458,11 @@ public class AxmolGLSurfaceView extends GLSurfaceView {
}); });
} }
public void deleteBackward() { public void deleteBackward(int numChars) {
this.queueEvent(new Runnable() { this.queueEvent(new Runnable() {
@Override @Override
public void run() { public void run() {
AxmolGLSurfaceView.this.mRenderer.handleDeleteBackward(); AxmolGLSurfaceView.this.mRenderer.handleDeleteBackward(numChars);
} }
}); });
} }

View File

@ -176,15 +176,15 @@ public class AxmolRenderer implements GLSurfaceView.Renderer {
} }
private static native void nativeInsertText(final String text); private static native void nativeInsertText(final String text);
private static native void nativeDeleteBackward(); private static native void nativeDeleteBackward(final int numChars);
private static native String nativeGetContentText(); private static native String nativeGetContentText();
public void handleInsertText(final String text) { public void handleInsertText(final String text) {
AxmolRenderer.nativeInsertText(text); AxmolRenderer.nativeInsertText(text);
} }
public void handleDeleteBackward() { public void handleDeleteBackward(final int numChars) {
AxmolRenderer.nativeDeleteBackward(); AxmolRenderer.nativeDeleteBackward(numChars);
} }
public String getContentText() { public String getContentText() {

View File

@ -90,8 +90,8 @@ public class TextInputWrapper implements TextWatcher, OnEditorActionListener {
new_i += 1; new_i += 1;
} }
for (; old_i < this.mText.length(); ++old_i) { if (old_i < this.mText.length()) {
this.mGLSurfaceView.deleteBackward(); this.mGLSurfaceView.deleteBackward(this.mText.length() - old_i);
} }
int nModified = s.length() - new_i; int nModified = s.length() - new_i;
@ -118,13 +118,13 @@ public class TextInputWrapper implements TextWatcher, OnEditorActionListener {
if (this.mGLSurfaceView.getEditText() == pTextView && this.isFullScreenEdit()) { if (this.mGLSurfaceView.getEditText() == pTextView && this.isFullScreenEdit()) {
// user press the action button, delete all old text and insert new text // user press the action button, delete all old text and insert new text
if (null != mOriginText) { if (null != mOriginText) {
for (int i = this.mOriginText.length(); i > 0; i--) { if (!this.mOriginText.isEmpty()) {
this.mGLSurfaceView.deleteBackward(); this.mGLSurfaceView.deleteBackward(this.mOriginText.length());
} }
} }
String text = pTextView.getText().toString(); String text = pTextView.getText().toString();
if (text != null) { if (text != null) {
/* If user input nothing, translate "\n" to engine. */ /* If user input nothing, translate "\n" to engine. */
if ( text.compareTo("") == 0) { if ( text.compareTo("") == 0) {
@ -135,16 +135,16 @@ public class TextInputWrapper implements TextWatcher, OnEditorActionListener {
text += '\n'; text += '\n';
} }
} }
final String insertText = text; final String insertText = text;
this.mGLSurfaceView.insertText(insertText); this.mGLSurfaceView.insertText(insertText);
} }
if (pActionID == EditorInfo.IME_ACTION_DONE) { if (pActionID == EditorInfo.IME_ACTION_DONE) {
this.mGLSurfaceView.requestFocus(); this.mGLSurfaceView.requestFocus();
} }
return false; return false;
} }
} }

View File

@ -70,9 +70,9 @@ JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolRenderer_nativeInsertText(JNIEnv*
ax::IMEDispatcher::sharedDispatcher()->dispatchInsertText(pszText, strlen(pszText)); ax::IMEDispatcher::sharedDispatcher()->dispatchInsertText(pszText, strlen(pszText));
} }
JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolRenderer_nativeDeleteBackward(JNIEnv*, jclass) JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolRenderer_nativeDeleteBackward(JNIEnv*, jclass, jint numChars)
{ {
ax::IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); ax::IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(numChars);
} }
JNIEXPORT jstring JNICALL Java_org_axmol_lib_AxmolRenderer_nativeGetContentText(JNIEnv* env, jclass) JNIEXPORT jstring JNICALL Java_org_axmol_lib_AxmolRenderer_nativeGetContentText(JNIEnv* env, jclass)

View File

@ -98,7 +98,7 @@ THE SOFTWARE.
[self.myMarkedText release]; [self.myMarkedText release];
self.myMarkedText = nil; self.myMarkedText = nil;
} }
ax::IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); ax::IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(1);
} }
- (void)insertText:(nonnull NSString*)text - (void)insertText:(nonnull NSString*)text

View File

@ -112,7 +112,7 @@ void KeyboardEvent::execute()
//Director::getInstance()()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeBackClicked); //Director::getInstance()()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeBackClicked);
break; break;
case AxmolKeyEvent::Back: case AxmolKeyEvent::Back:
IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(1);
break; break;
case AxmolKeyEvent::Enter: case AxmolKeyEvent::Enter:
IMEDispatcher::sharedDispatcher()->dispatchInsertText("\n", 1); IMEDispatcher::sharedDispatcher()->dispatchInsertText("\n", 1);

View File

@ -304,7 +304,7 @@ void KeyBoardWinRT::OnWinRTKeyboardEvent(WinRTKeyboardEventType type, KeyEventAr
switch (keyCode) switch (keyCode)
{ {
case EventKeyboard::KeyCode::KEY_BACKSPACE: case EventKeyboard::KeyCode::KEY_BACKSPACE:
IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); IMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(1);
break; break;
case EventKeyboard::KeyCode::KEY_HOME: case EventKeyboard::KeyCode::KEY_HOME:
case EventKeyboard::KeyCode::KEY_KP_HOME: case EventKeyboard::KeyCode::KEY_KP_HOME:

View File

@ -617,7 +617,7 @@ void TextFieldEx::insertText(const char* text, size_t len)
this->closeIME(); this->closeIME();
} }
void TextFieldEx::deleteBackward() void TextFieldEx::deleteBackward(size_t numChars)
{ {
if (!_editable || !this->_enabled || 0 == _charCount) if (!_editable || !this->_enabled || 0 == _charCount)
{ {
@ -634,23 +634,33 @@ void TextFieldEx::deleteBackward()
return; return;
} }
// get the delete byte number // Length of characters to delete is based on input editor, but the actual
size_t deleteLen = 1; // default, erase 1 byte // length of the displayed text may be less
numChars = std::min(numChars, len);
while (0x80 == (0xC0 & _inputText.at(_insertPos - deleteLen))) size_t totalDeleteLen = 0;
for (auto i = 0; i < numChars; ++i)
{ {
++deleteLen; // get the delete byte number
size_t deleteLen = 1; // default, erase 1 byte
// Calculate the actual number of bytes to delete for a specific character
while (0x80 == (0xC0 & _inputText.at(_insertPos - totalDeleteLen - deleteLen)))
{
++deleteLen;
}
totalDeleteLen += deleteLen;
} }
// if (_delegate && _delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - deleteLen, // if (_delegate && _delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - deleteLen,
// static_cast<int>(deleteLen))) // static_cast<int>(deleteLen)))
//{ //{
// // delegate doesn't wan't to delete backwards // // delegate doesn't want to delete backwards
// return; // return;
// } // }
// if all text deleted, show placeholder string // if all text deleted, show placeholder string
if (len <= deleteLen) if (len <= totalDeleteLen)
{ {
__moveCursor(-1); __moveCursor(-1);
@ -670,7 +680,7 @@ void TextFieldEx::deleteBackward()
// set new input text // set new input text
std::string text = _inputText; // (inputText.c_str(), len - deleteLen); std::string text = _inputText; // (inputText.c_str(), len - deleteLen);
text.erase(_insertPos - deleteLen, deleteLen); text.erase(_insertPos - totalDeleteLen, totalDeleteLen);
__moveCursor(-1); __moveCursor(-1);

View File

@ -140,7 +140,7 @@ protected:
bool canAttachWithIME() override; bool canAttachWithIME() override;
bool canDetachWithIME() override; bool canDetachWithIME() override;
void deleteBackward() override; void deleteBackward(size_t numChars) override;
std::string_view getContentText() override; std::string_view getContentText() override;
void handleDeleteKeyEvent(); void handleDeleteKeyEvent();

View File

@ -40,6 +40,7 @@ THE SOFTWARE.
#include "ui/UIListView.h" #include "ui/UIListView.h"
#include "ui/UISlider.h" #include "ui/UISlider.h"
#include "ui/UITextField.h" #include "ui/UITextField.h"
#include "ui/UITextFieldEx.h"
#include "ui/UITextBMFont.h" #include "ui/UITextBMFont.h"
#include "ui/UIPageView.h" #include "ui/UIPageView.h"
#include "ui/UIHelper.h" #include "ui/UIHelper.h"