Android: Move QtEditText to QtWindow

Every QtWindow has its own QtEditText. QtInputDelegate
uses QtEditText from the QtWindow which has the focus.

QtEditText is a View class which handles the creation
of the input connection, and encapsulates other various
keyboard input related functionalities. Previously
we have only had one, which requests focus when the
software keyboard is opened. However, with the
introduction of child windows, it does not make
sense for all the windows to operate through this
one instance.
Furthermore, since it always needs to have focus
to be able to open the software keyboard, this
leads to a bit surprising behavior in the focus
chain if we want to make each window focusable,
not just the top level one. Having each window
have its own QtEditText makes sure when a window
gets focus, the focus doesn't leave outside
of the matching view's own scope, making it easier
to handle.

This should also make it easier to clean up keyboard
input related events tied to a window when that window
is removed.

Task-number: QTBUG-118139
Change-Id: Idd1a9407bc0c48660f2885d3bda28e46d42c08a0
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit f4050cc5ea7490ba3b8b2bb0a174559d7e72a27e)
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Tinja Paavoseppä 2024-01-31 08:37:09 +02:00
parent 02ab923adf
commit 1712c4eeca
4 changed files with 49 additions and 30 deletions

View File

@ -274,8 +274,13 @@ class QtActivityDelegate extends QtActivityDelegateBase
public void openContextMenu(final int x, final int y, final int w, final int h)
{
m_layout.postDelayed(() -> {
m_layout.setLayoutParams(m_inputDelegate.getQtEditText(), new QtLayout.LayoutParams(w, h, x, y), false);
PopupMenu popup = new PopupMenu(m_activity, m_inputDelegate.getQtEditText());
final QtEditText focusedEditText = m_inputDelegate.getCurrentQtEditText();
if (focusedEditText == null) {
Log.w(QtTAG, "No focused view when trying to open context menu");
return;
}
m_layout.setLayoutParams(focusedEditText, new QtLayout.LayoutParams(w, h, x, y), false);
PopupMenu popup = new PopupMenu(m_activity, focusedEditText);
QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
popup.setOnMenuItemClickListener(menuItem ->
m_activity.onContextItemSelected(menuItem));
@ -325,6 +330,7 @@ class QtActivityDelegate extends QtActivityDelegateBase
m_topLevelWindows.put(window.getId(), window);
if (!m_splashScreenSticky)
hideSplashScreen();
m_inputDelegate.setFocusedView(window.getQtEditText());
});
}

View File

@ -133,7 +133,6 @@ abstract class QtActivityDelegateBase
setUpLayout();
}
public void hideSplashScreen()
{
hideSplashScreen(0);

View File

@ -39,7 +39,7 @@ class QtInputDelegate {
public static native void handleLocationChanged(int id, int x, int y);
// handle methods
private final QtEditText m_editText;
private QtEditText m_currentEditText = null;
private final InputMethodManager m_imm;
private boolean m_keyboardIsVisible = false;
@ -108,13 +108,13 @@ class QtInputDelegate {
}
private final KeyboardVisibilityListener m_keyboardVisibilityListener;
private final QtInputConnectionListener m_inputConnectionListener;
QtInputDelegate(Activity activity, KeyboardVisibilityListener listener)
{
this.m_keyboardVisibilityListener = listener;
m_editText = new QtEditText(activity);
m_imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
QtInputConnectionListener inputConnectionListener = new QtInputConnectionListener() {
m_inputConnectionListener = new QtInputConnectionListener() {
@Override
public void onSetClosing(boolean closing) {
if (!closing)
@ -131,7 +131,6 @@ class QtInputDelegate {
hideSoftwareKeyboard();
}
};
m_editText.setQtInputConnectionListener(inputConnectionListener);
}
public boolean isKeyboardVisible()
@ -151,9 +150,9 @@ class QtInputDelegate {
m_softInputMode = inputMode;
}
QtEditText getQtEditText()
QtEditText getCurrentQtEditText()
{
return m_editText;
return m_currentEditText;
}
void setEditPopupMenu(EditPopupMenu editPopupMenu)
@ -187,34 +186,38 @@ class QtInputDelegate {
@UsedFromNativeCode
public void resetSoftwareKeyboard()
{
if (m_imm == null)
if (m_imm == null || m_currentEditText == null)
return;
m_editText.postDelayed(() -> {
m_imm.restartInput(m_editText);
m_editText.m_optionsChanged = false;
m_currentEditText.postDelayed(() -> {
m_imm.restartInput(m_currentEditText);
m_currentEditText.m_optionsChanged = false;
}, 5);
}
void setFocusedView(QtEditText currentEditText)
{
m_currentEditText = currentEditText;
// TODO rather set the listener when creating the edit text
if (m_currentEditText != null)
m_currentEditText.setQtInputConnectionListener(m_inputConnectionListener);
}
public void showSoftwareKeyboard(Activity activity, QtLayout layout,
final int x, final int y, final int width, final int height,
final int inputHints, final int enterKeyType)
{
QtNative.runAction(() -> {
if (m_imm == null)
if (m_imm == null || m_currentEditText == null)
return;
if (updateSoftInputMode(activity, height))
return;
setEditTextOptions(enterKeyType, inputHints);
m_currentEditText.requestFocus();
// TODO: The editText is added to the QtLayout, but is it ever removed?
QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(width, height, x, y);
layout.setLayoutParams(m_editText, layoutParams, false);
m_editText.requestFocus();
m_editText.postDelayed(() -> {
m_imm.showSoftInput(m_editText, 0, new ResultReceiver(new Handler()) {
m_currentEditText.postDelayed(() -> {
m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
@ -235,9 +238,9 @@ class QtInputDelegate {
}
}
});
if (m_editText.m_optionsChanged) {
m_imm.restartInput(m_editText);
m_editText.m_optionsChanged = false;
if (m_currentEditText.m_optionsChanged) {
m_imm.restartInput(m_currentEditText);
m_currentEditText.m_optionsChanged = false;
}
}, 15);
});
@ -304,9 +307,9 @@ class QtInputDelegate {
if (enterKeyType == 0 && (inputHints & ImhMultiLine) != 0)
imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
m_editText.setInitialCapsMode(initialCapsMode);
m_editText.setImeOptions(imeOptions);
m_editText.setInputType(inputType);
m_currentEditText.setInitialCapsMode(initialCapsMode);
m_currentEditText.setImeOptions(imeOptions);
m_currentEditText.setInputType(inputType);
}
private boolean isDisablePredictiveTextWorkaround(int inputHints)
@ -418,10 +421,10 @@ class QtInputDelegate {
{
m_isKeyboardHidingAnimationOngoing = true;
QtNative.runAction(() -> {
if (m_imm == null)
if (m_imm == null || m_currentEditText == null)
return;
m_imm.hideSoftInputFromWindow(m_editText.getWindowToken(), 0,
m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0,
new ResultReceiver(new Handler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
@ -448,7 +451,7 @@ class QtInputDelegate {
if (m_imm == null)
return;
m_imm.updateSelection(m_editText, selStart, selEnd, candidatesStart, candidatesEnd);
m_imm.updateSelection(m_currentEditText, selStart, selEnd, candidatesStart, candidatesEnd);
});
}

View File

@ -21,6 +21,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
private HashMap<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>();
private QtWindow m_parentWindow;
private GestureDetector m_gestureDetector;
private final QtEditText m_editText;
private static native void setSurface(int windowId, Surface surface);
@ -28,6 +29,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
{
super(context);
setId(View.generateViewId());
m_editText = new QtEditText(context);
setParent(parentWindow);
QtNative.runAction(() -> {
@ -41,6 +43,13 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
});
}
// TODO this is a temporary workaround to be able to set the input delegate current edit text,
// the next two patches make this redundant
QtEditText getQtEditText()
{
return m_editText;
}
void setVisible(boolean visible) {
QtNative.runAction(() -> {
if (visible)
@ -106,6 +115,8 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
// The surface container of this window will be added as the first of the stack.
// All other views are stacked based on the order they are created.
addView(m_surfaceContainer, 0);
addView(m_editText, new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
});
}