Allow copying java EditText to STK editbox
This commit is contained in:
parent
27f0f8c961
commit
e6d5346e5e
@ -166,7 +166,8 @@ LOCAL_CFLAGS := -I../lib/angelscript/include \
|
||||
-DNDEBUG \
|
||||
-DANDROID_PACKAGE_NAME=\"$(PACKAGE_NAME)\" \
|
||||
-DANDROID_APP_DIR_NAME=\"$(APP_DIR_NAME)\" \
|
||||
-DSUPERTUXKART_VERSION=\"$(PROJECT_VERSION)\"
|
||||
-DSUPERTUXKART_VERSION=\"$(PROJECT_VERSION)\" \
|
||||
-DANDROID_PACKAGE_CALLBACK_NAME=$(PACKAGE_CALLBACK_NAME)
|
||||
LOCAL_CPPFLAGS := -std=gnu++0x
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := irrlicht bullet enet freetype ifaddrs angelscript \
|
||||
|
@ -471,7 +471,22 @@ sed -i "s/targetSdkVersion=\".*\"/targetSdkVersion=\"$TARGET_SDK_VERSION\"/g" \
|
||||
sed -i "s/package=\".*\"/package=\"$PACKAGE_NAME\"/g" \
|
||||
"$DIRNAME/AndroidManifest.xml"
|
||||
|
||||
sed -i "s/package .*/package $PACKAGE_NAME;/g" \
|
||||
sed -i "s/package org.supertuxkart.*/package $PACKAGE_NAME;/g" \
|
||||
"$DIRNAME/src/main/java/STKEditText.java"
|
||||
|
||||
sed -i "s/import org.supertuxkart.*/import $PACKAGE_NAME.STKInputConnection;/g" \
|
||||
"$DIRNAME/src/main/java/STKEditText.java"
|
||||
|
||||
sed -i "s/package org.supertuxkart.*/package $PACKAGE_NAME;/g" \
|
||||
"$DIRNAME/src/main/java/STKInputConnection.java"
|
||||
|
||||
sed -i "s/import org.supertuxkart.*.STKEditText;/import $PACKAGE_NAME.STKEditText;/g" \
|
||||
"$DIRNAME/src/main/java/STKInputConnection.java"
|
||||
|
||||
sed -i "s/package org.supertuxkart.*/package $PACKAGE_NAME;/g" \
|
||||
"$DIRNAME/src/main/java/SuperTuxKartActivity.java"
|
||||
|
||||
sed -i "s/import org.supertuxkart.*/import $PACKAGE_NAME.STKEditText;/g" \
|
||||
"$DIRNAME/src/main/java/SuperTuxKartActivity.java"
|
||||
|
||||
sed -i "s/versionName=\".*\"/versionName=\"$PROJECT_VERSION\"/g" \
|
||||
|
78
android/src/main/java/STKEditText.java
Normal file
78
android/src/main/java/STKEditText.java
Normal file
@ -0,0 +1,78 @@
|
||||
package org.supertuxkart.stk_dbg;
|
||||
|
||||
import org.supertuxkart.stk_dbg.STKInputConnection;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputType;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.EditText;
|
||||
|
||||
// We need to extend EditText instead of view to allow copying to our STK
|
||||
// editbox
|
||||
public class STKEditText extends EditText
|
||||
{
|
||||
private int m_composing_start;
|
||||
|
||||
private int m_composing_end;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
private native static void editText2STKEditbox(String full_text, int start,
|
||||
int end, int composing_start,
|
||||
int composing_end);
|
||||
// ------------------------------------------------------------------------
|
||||
public STKEditText(Context context)
|
||||
{
|
||||
super(context);
|
||||
setFocusableInTouchMode(true);
|
||||
m_composing_start = 0;
|
||||
m_composing_end = 0;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo out_attrs)
|
||||
{
|
||||
STKInputConnection sic =
|
||||
new STKInputConnection(super.onCreateInputConnection(out_attrs), this);
|
||||
out_attrs.actionLabel = null;
|
||||
out_attrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
out_attrs.imeOptions = EditorInfo.IME_ACTION_NEXT |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN |
|
||||
EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
||||
return sic;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
public void resetWhenFocus()
|
||||
{
|
||||
clearComposingText();
|
||||
getText().clear();
|
||||
m_composing_start = m_composing_end = 0;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
public void setComposingRegion(int start, int end)
|
||||
{
|
||||
// From doc of InputConnectionWrapper, it says:
|
||||
// Editor authors, be ready to accept a start that is greater than end.
|
||||
if (start != end && start > end)
|
||||
{
|
||||
m_composing_end = start;
|
||||
m_composing_start = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_composing_start = start;
|
||||
m_composing_end = end;
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
public void updateSTKEditBox()
|
||||
{
|
||||
if (!isFocused())
|
||||
return;
|
||||
editText2STKEditbox(getText().toString(), getSelectionStart(),
|
||||
getSelectionEnd(), m_composing_start, m_composing_end);
|
||||
}
|
||||
}
|
68
android/src/main/java/STKInputConnection.java
Normal file
68
android/src/main/java/STKInputConnection.java
Normal file
@ -0,0 +1,68 @@
|
||||
package org.supertuxkart.stk_dbg;
|
||||
|
||||
import org.supertuxkart.stk_dbg.STKEditText;
|
||||
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputConnectionWrapper;
|
||||
|
||||
public class STKInputConnection extends InputConnectionWrapper
|
||||
{
|
||||
/* The global edittext which will be "copied" to the current focused STK
|
||||
* box. */
|
||||
private STKEditText m_stk_edittext;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
public STKInputConnection(InputConnection target, STKEditText stk_edittext)
|
||||
{
|
||||
super(target, true/*mutable*/);
|
||||
m_stk_edittext = stk_edittext;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int new_cursor_position)
|
||||
{
|
||||
boolean ret = super.setComposingText(text, new_cursor_position);
|
||||
String composing_text = text.toString();
|
||||
String new_text = m_stk_edittext.getText().toString();
|
||||
int composing_start = 0;
|
||||
int composing_end = 0;
|
||||
// Test last char
|
||||
if (!composing_text.isEmpty() && !new_text.isEmpty() &&
|
||||
composing_text.charAt(composing_text.length() - 1) ==
|
||||
new_text.charAt(new_text.length() - 1))
|
||||
{
|
||||
composing_start = new_text.length() - composing_text.length();
|
||||
composing_end = composing_start + composing_text.length();
|
||||
}
|
||||
m_stk_edittext.setComposingRegion(composing_start, composing_end);
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
return ret;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean finishComposingText()
|
||||
{
|
||||
m_stk_edittext.setComposingRegion(0, 0);
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
return super.finishComposingText();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean setComposingRegion(int start, int end)
|
||||
{
|
||||
m_stk_edittext.setComposingRegion(start, end);
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
return super.setComposingRegion(start, end);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int new_cursor_position)
|
||||
{
|
||||
// Usually only a single character, so dismiss composing region
|
||||
boolean ret = super.commitText(text, new_cursor_position);
|
||||
m_stk_edittext.setComposingRegion(0, 0);
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +1,27 @@
|
||||
package org.supertuxkart.stk_dbg;
|
||||
|
||||
import org.supertuxkart.stk_dbg.STKEditText;
|
||||
|
||||
import android.app.NativeActivity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class SuperTuxKartActivity extends NativeActivity
|
||||
{
|
||||
private native void saveFromJavaChars(String chars);
|
||||
private native void saveKeyboardHeight(int height);
|
||||
private STKEditText m_stk_edittext;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
private native void saveKeyboardHeight(int height);
|
||||
// ------------------------------------------------------------------------
|
||||
private void hideNavBar(View decor_view)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < 19)
|
||||
@ -27,12 +34,14 @@ public class SuperTuxKartActivity extends NativeActivity
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public void onCreate(Bundle instance)
|
||||
{
|
||||
super.onCreate(instance);
|
||||
System.loadLibrary("main");
|
||||
m_stk_edittext = null;
|
||||
|
||||
final View root = getWindow().getDecorView().findViewById(
|
||||
android.R.id.content);
|
||||
root.getViewTreeObserver().addOnGlobalLayoutListener(new
|
||||
@ -61,26 +70,7 @@ public class SuperTuxKartActivity extends NativeActivity
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event)
|
||||
{
|
||||
// ACTION_MULTIPLE deprecated in API level Q, it says if the key code
|
||||
// is KEYCODE_UNKNOWN, then this is a sequence of characters as
|
||||
// returned by getCharacters()
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN &&
|
||||
event.getAction() == KeyEvent.ACTION_MULTIPLE)
|
||||
{
|
||||
String chars = event.getCharacters();
|
||||
if (chars != null)
|
||||
{
|
||||
saveFromJavaChars(chars);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean has_focus)
|
||||
{
|
||||
@ -88,20 +78,88 @@ public class SuperTuxKartActivity extends NativeActivity
|
||||
if (has_focus)
|
||||
hideNavBar(getWindow().getDecorView());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event)
|
||||
{
|
||||
// Called when user change cursor / select all text in native android
|
||||
// keyboard
|
||||
boolean ret = super.dispatchKeyEvent(event);
|
||||
if (m_stk_edittext != null)
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
return ret;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
public void showKeyboard()
|
||||
{
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(getWindow().getDecorView(),
|
||||
InputMethodManager.SHOW_FORCED);
|
||||
}
|
||||
|
||||
public void hideKeyboard()
|
||||
final Context context = this;
|
||||
// Need to run in ui thread as it access the view m_stk_edittext
|
||||
runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(
|
||||
getWindow().getDecorView().getWindowToken(), 0);
|
||||
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||
if (m_stk_edittext == null)
|
||||
{
|
||||
m_stk_edittext = new STKEditText(context);
|
||||
// For some copy-and-paste text are not done by commitText
|
||||
// in STKInputConnection, so we need an extra watcher
|
||||
m_stk_edittext.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s,
|
||||
int start, int before,
|
||||
int count) {}
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s,
|
||||
int start, int count,
|
||||
int after) {}
|
||||
@Override
|
||||
public void afterTextChanged(Editable edit)
|
||||
{
|
||||
if (m_stk_edittext != null)
|
||||
m_stk_edittext.updateSTKEditBox();
|
||||
}
|
||||
});
|
||||
addContentView(m_stk_edittext, params);
|
||||
}
|
||||
else
|
||||
m_stk_edittext.setLayoutParams(params);
|
||||
|
||||
m_stk_edittext.resetWhenFocus();
|
||||
m_stk_edittext.setVisibility(View.VISIBLE);
|
||||
m_stk_edittext.requestFocus();
|
||||
|
||||
imm.showSoftInput(m_stk_edittext,
|
||||
InputMethodManager.SHOW_FORCED);
|
||||
}
|
||||
});
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
public void hideKeyboard()
|
||||
{
|
||||
runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (m_stk_edittext == null)
|
||||
return;
|
||||
|
||||
m_stk_edittext.clearFocus();
|
||||
m_stk_edittext.setVisibility(View.GONE);
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(m_stk_edittext.getWindowToken(),
|
||||
0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "../../../lib/irrlicht/include/IrrCompileConfig.h"
|
||||
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "../../../lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.h"
|
||||
#endif
|
||||
@ -69,6 +71,9 @@ CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
|
||||
// FIXME quick hack to enable mark movement with keyboard and mouse for rtl language,
|
||||
// don't know why it's disabled in the first place, because STK fail
|
||||
// to input unicode characters before?
|
||||
m_from_android_edittext = false;
|
||||
m_composing_start = 0;
|
||||
m_composing_end = 0;
|
||||
|
||||
#ifdef _DEBUG
|
||||
setDebugName("CGUIEditBox");
|
||||
@ -261,6 +266,9 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
|
||||
#ifndef SERVER_ONLY
|
||||
if (isEnabled())
|
||||
{
|
||||
// Ignore key input if we only fromAndroidEditText
|
||||
if (m_from_android_edittext && event.EventType == EET_KEY_INPUT_EVENT)
|
||||
return true;
|
||||
switch(event.EventType)
|
||||
{
|
||||
case EET_GUI_EVENT:
|
||||
@ -287,6 +295,9 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
|
||||
dl->setTextInputEnabled(false);
|
||||
}
|
||||
#endif
|
||||
m_from_android_edittext = false;
|
||||
m_composing_start = 0;
|
||||
m_composing_end = 0;
|
||||
}
|
||||
else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
|
||||
{
|
||||
@ -1073,6 +1084,19 @@ void CGUIEditBox::draw()
|
||||
// it will return the input pointer if (this->isRTLLanguage()) from Translations::isRTLText
|
||||
// is false
|
||||
|
||||
// draw composing text underline
|
||||
if (!PasswordBox && focus && m_composing_start != m_composing_end && i == hlineStart)
|
||||
{
|
||||
s = txtLine->subString(0, m_composing_start);
|
||||
s32 underline_begin = font->getDimension(s.c_str()).Width;
|
||||
core::rect<s32> underline = CurrentTextRect;
|
||||
underline.UpperLeftCorner.X += underline_begin;
|
||||
s32 height = underline.LowerRightCorner.Y - underline.UpperLeftCorner.Y;
|
||||
underline.UpperLeftCorner.Y += s32(std::abs(height) * 0.9f);
|
||||
underline.LowerRightCorner.Y -= s32(std::abs(height) * 0.08f);
|
||||
GL32_draw2DRectangle(video::SColor(255, 0, 0, 0), underline);
|
||||
}
|
||||
|
||||
// draw mark and marked text
|
||||
if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
|
||||
{
|
||||
@ -1780,3 +1804,40 @@ void CGUIEditBox::openScreenKeyboard()
|
||||
new GUIEngine::ScreenKeyboard(1.0f, 0.40f, this);
|
||||
}
|
||||
|
||||
// Real copying is happening in text_box_widget.cpp with static function
|
||||
void CGUIEditBox::fromAndroidEditText(const core::stringw& text, int start,
|
||||
int end, int composing_start,
|
||||
int composing_end)
|
||||
{
|
||||
// When focus of this element is lost, this will be set to false again
|
||||
m_from_android_edittext = true;
|
||||
Text = text;
|
||||
// Prevent invalid start or end
|
||||
if ((unsigned)end > Text.size())
|
||||
{
|
||||
end = (int)Text.size();
|
||||
start = end;
|
||||
}
|
||||
|
||||
CursorPos = end;
|
||||
m_composing_start = 0;
|
||||
m_composing_end = 0;
|
||||
|
||||
if (start != end)
|
||||
setTextMarkers(start, end);
|
||||
else
|
||||
{
|
||||
MarkBegin = 0;
|
||||
MarkEnd = 0;
|
||||
}
|
||||
|
||||
if (composing_start != composing_end)
|
||||
{
|
||||
if (composing_start < 0)
|
||||
composing_start = 0;
|
||||
if (composing_end > end)
|
||||
composing_end = end;
|
||||
m_composing_start = composing_start;
|
||||
m_composing_end = composing_end;
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +119,8 @@ using namespace gui;
|
||||
virtual irr::gui::IGUIFont* getActiveFont() const { return NULL; }
|
||||
virtual void setDrawBackground(bool) { }
|
||||
|
||||
void fromAndroidEditText(const core::stringw& text, int start, int end,
|
||||
int composing_start, int composing_end);
|
||||
void openScreenKeyboard();
|
||||
s32 getCursorPosInBox() const { return CursorPos; }
|
||||
s32 getTextCount() const { return (s32)Text.size(); }
|
||||
@ -174,6 +176,13 @@ using namespace gui;
|
||||
core::array< s32 > BrokenTextPositions;
|
||||
|
||||
core::rect<s32> CurrentTextRect, FrameRect; // temporary values
|
||||
|
||||
s32 m_composing_start;
|
||||
s32 m_composing_end;
|
||||
|
||||
/* If true, this editbox will copy text and selection only from
|
||||
* android edittext, and process only mouse event. */
|
||||
bool m_from_android_edittext;
|
||||
};
|
||||
|
||||
|
||||
|
@ -228,3 +228,34 @@ EventPropagation TextBoxWidget::leftPressed (const int playerID)
|
||||
|
||||
return EVENT_LET;
|
||||
} // leftPressed
|
||||
|
||||
// ============================================================================
|
||||
/* This callback will allow copying android edittext data directly to editbox,
|
||||
* which will allow composing text to be auto updated. */
|
||||
#ifdef ANDROID
|
||||
#include "jni.h"
|
||||
|
||||
#if !defined(ANDROID_PACKAGE_CALLBACK_NAME)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#define MAKE_EDITTEXT_CALLBACK(x) JNIEXPORT void JNICALL Java_ ## x##_STKEditText_editText2STKEditbox(JNIEnv* env, jobject this_obj, jstring text, jint start, jint end, jint composing_start, jint composing_end)
|
||||
#define ANDROID_EDITTEXT_CALLBACK(PKG_NAME) MAKE_EDITTEXT_CALLBACK(PKG_NAME)
|
||||
|
||||
extern "C"
|
||||
ANDROID_EDITTEXT_CALLBACK(ANDROID_PACKAGE_CALLBACK_NAME)
|
||||
{
|
||||
TextBoxWidget* tb = dynamic_cast<TextBoxWidget*>(getFocusForPlayer(0));
|
||||
if (!tb || text == NULL)
|
||||
return;
|
||||
|
||||
const char* utf8_text = env->GetStringUTFChars(text, NULL);
|
||||
if (utf8_text == NULL)
|
||||
return;
|
||||
|
||||
core::stringw to_editbox = StringUtils::utf8ToWide(utf8_text);
|
||||
tb->getIrrlichtElement<MyCGUIEditBox>()->fromAndroidEditText(
|
||||
to_editbox, start, end, composing_start, composing_end);
|
||||
env->ReleaseStringUTFChars(text, utf8_text);
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user