Android: Add preliminary support for child windows
Update the manual test case for embedded windows to have native window on Android. There are still some sharp corners, for example: * The windows are implemented with SurfaceViews, which makes z-ordering with multiple of them a bit tricky. The Surfaces they instantiate are basically z-ordered to either be below everything, with a hole punched in the window, or on top of everything, with the Surfaces created later on top of the ones created earlier. Also, with the foreign views it looks like the native view is on top of the Surface, because it is created later. And since the child windows create their Surfaces before the parent, they would be behind the parent window, currently circumventing this with letting the parent be z-ordered behind everything, and the children on top of everything. A follow up commit addresses this by changing the native view class to TextureView when multiple windows are present. * Parent window always gets the touch events - fixed in a follow up commit * If a child window has a text edit, it does not receive focus when clicking on it Task-number: QTBUG-116187 Change-Id: I32188ec5e3d3fce9fd8e3a931e317d1e081f691c Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
0a92d881bb
commit
6ff88f97a6
@ -29,6 +29,7 @@ set(java_sources
|
|||||||
src/org/qtproject/qt/android/QtClipboardManager.java
|
src/org/qtproject/qt/android/QtClipboardManager.java
|
||||||
src/org/qtproject/qt/android/QtDisplayManager.java
|
src/org/qtproject/qt/android/QtDisplayManager.java
|
||||||
src/org/qtproject/qt/android/UsedFromNativeCode.java
|
src/org/qtproject/qt/android/UsedFromNativeCode.java
|
||||||
|
src/org/qtproject/qt/android/QtRootLayout.java
|
||||||
src/org/qtproject/qt/android/QtWindow.java
|
src/org/qtproject/qt/android/QtWindow.java
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,7 +59,8 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
|
|||||||
return dispatchHoverEvent(event);
|
return dispatchHoverEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO do we want to have one QtAccessibilityDelegate for the whole app (QtRootLayout) or
|
||||||
|
// e.g. one per window?
|
||||||
public QtAccessibilityDelegate(QtLayout layout)
|
public QtAccessibilityDelegate(QtLayout layout)
|
||||||
{
|
{
|
||||||
m_layout = layout;
|
m_layout = layout;
|
||||||
|
@ -37,13 +37,12 @@ class QtActivityDelegate
|
|||||||
{
|
{
|
||||||
private Activity m_activity;
|
private Activity m_activity;
|
||||||
|
|
||||||
private QtLayout m_layout = null;
|
private QtRootLayout m_layout = null;
|
||||||
private HashMap<Integer, QtWindow> m_topLevelWindows;
|
private HashMap<Integer, QtWindow> m_topLevelWindows;
|
||||||
private ImageView m_splashScreen = null;
|
private ImageView m_splashScreen = null;
|
||||||
private boolean m_splashScreenSticky = false;
|
private boolean m_splashScreenSticky = false;
|
||||||
|
|
||||||
private View m_dummyView = null;
|
private View m_dummyView = null;
|
||||||
|
|
||||||
private QtAccessibilityDelegate m_accessibilityDelegate = null;
|
private QtAccessibilityDelegate m_accessibilityDelegate = null;
|
||||||
private QtDisplayManager m_displayManager = null;
|
private QtDisplayManager m_displayManager = null;
|
||||||
|
|
||||||
@ -135,7 +134,7 @@ class QtActivityDelegate
|
|||||||
|
|
||||||
private void initMembers()
|
private void initMembers()
|
||||||
{
|
{
|
||||||
m_layout = new QtLayout(m_activity);
|
m_layout = new QtRootLayout(m_activity);
|
||||||
m_membersInitialized = true;
|
m_membersInitialized = true;
|
||||||
m_topLevelWindows = new HashMap<Integer, QtWindow>();
|
m_topLevelWindows = new HashMap<Integer, QtWindow>();
|
||||||
|
|
||||||
|
@ -30,37 +30,6 @@ class QtLayout extends ViewGroup
|
|||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
|
||||||
{
|
|
||||||
Activity activity = (Activity)getContext();
|
|
||||||
if (activity == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
|
||||||
Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
|
||||||
? activity.getWindowManager().getDefaultDisplay()
|
|
||||||
: activity.getDisplay();
|
|
||||||
|
|
||||||
if (display == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
display.getRealMetrics(realMetrics);
|
|
||||||
if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) {
|
|
||||||
// This is an intermediate state during display rotation.
|
|
||||||
// The new size is still reported for old orientation, while
|
|
||||||
// realMetrics contain sizes for new orientation. Setting
|
|
||||||
// such parameters will produce inconsistent results, so
|
|
||||||
// we just skip them.
|
|
||||||
// We will have another onSizeChanged() with normal values
|
|
||||||
// a bit later.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
|
|
||||||
QtDisplayManager.handleOrientationChanges(activity, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
{
|
{
|
||||||
@ -79,11 +48,15 @@ class QtLayout extends ViewGroup
|
|||||||
int childRight;
|
int childRight;
|
||||||
int childBottom;
|
int childBottom;
|
||||||
|
|
||||||
QtLayout.LayoutParams lp
|
if (child.getLayoutParams() instanceof QtLayout.LayoutParams) {
|
||||||
= (QtLayout.LayoutParams) child.getLayoutParams();
|
QtLayout.LayoutParams lp
|
||||||
|
= (QtLayout.LayoutParams) child.getLayoutParams();
|
||||||
childRight = lp.x + child.getMeasuredWidth();
|
childRight = lp.x + child.getMeasuredWidth();
|
||||||
childBottom = lp.y + child.getMeasuredHeight();
|
childBottom = lp.y + child.getMeasuredHeight();
|
||||||
|
} else {
|
||||||
|
childRight = child.getMeasuredWidth();
|
||||||
|
childBottom = child.getMeasuredHeight();
|
||||||
|
}
|
||||||
|
|
||||||
maxWidth = Math.max(maxWidth, childRight);
|
maxWidth = Math.max(maxWidth, childRight);
|
||||||
maxHeight = Math.max(maxHeight, childBottom);
|
maxHeight = Math.max(maxHeight, childBottom);
|
||||||
@ -181,6 +154,11 @@ class QtLayout extends ViewGroup
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LayoutParams(int width, int height)
|
||||||
|
{
|
||||||
|
super(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
package org.qtproject.qt.android;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.Display;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A layout which corresponds to one Activity, i.e. is the root layout where the top level window
|
||||||
|
and handles orientation changes.
|
||||||
|
*/
|
||||||
|
public class QtRootLayout extends QtLayout
|
||||||
|
{
|
||||||
|
|
||||||
|
private int m_activityDisplayRotation = -1;
|
||||||
|
private int m_ownDisplayRotation = -1;
|
||||||
|
private int m_nativeOrientation = -1;
|
||||||
|
|
||||||
|
public QtRootLayout(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivityDisplayRotation(int rotation)
|
||||||
|
{
|
||||||
|
m_activityDisplayRotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNativeOrientation(int orientation)
|
||||||
|
{
|
||||||
|
m_nativeOrientation = orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int displayRotation()
|
||||||
|
{
|
||||||
|
return m_ownDisplayRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged (int w, int h, int oldw, int oldh)
|
||||||
|
{
|
||||||
|
Activity activity = (Activity)getContext();
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||||
|
Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||||
|
? activity.getWindowManager().getDefaultDisplay()
|
||||||
|
: activity.getDisplay();
|
||||||
|
|
||||||
|
if (display == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
display.getRealMetrics(realMetrics);
|
||||||
|
if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) {
|
||||||
|
// This is an intermediate state during display rotation.
|
||||||
|
// The new size is still reported for old orientation, while
|
||||||
|
// realMetrics contain sizes for new orientation. Setting
|
||||||
|
// such parameters will produce inconsistent results, so
|
||||||
|
// we just skip them.
|
||||||
|
// We will have another onSizeChanged() with normal values
|
||||||
|
// a bit later.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
|
||||||
|
QtDisplayManager.handleOrientationChanges(activity, true);
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,6 @@
|
|||||||
package org.qtproject.qt.android;
|
package org.qtproject.qt.android;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -18,14 +16,26 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
|
|
||||||
private QtSurface m_surface;
|
private QtSurface m_surface;
|
||||||
private View m_nativeView;
|
private View m_nativeView;
|
||||||
private Handler m_androidHandler;
|
private HashMap<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>();
|
||||||
|
private QtWindow m_parentWindow;
|
||||||
|
|
||||||
private static native void setSurface(int windowId, Surface surface);
|
private static native void setSurface(int windowId, Surface surface);
|
||||||
|
|
||||||
public QtWindow(Context context)
|
public QtWindow(Context context, QtWindow parentWindow)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
setId(View.generateViewId());
|
setId(View.generateViewId());
|
||||||
|
|
||||||
|
setParent(parentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisible(boolean visible) {
|
||||||
|
QtNative.runAction(() -> {
|
||||||
|
if (visible)
|
||||||
|
setVisibility(View.VISIBLE);
|
||||||
|
else
|
||||||
|
setVisibility(View.INVISIBLE);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -34,6 +44,12 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
setSurface(getId(), surface);
|
setSurface(getId(), surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeWindow()
|
||||||
|
{
|
||||||
|
if (m_parentWindow != null)
|
||||||
|
m_parentWindow.removeChildWindow(getId());
|
||||||
|
}
|
||||||
|
|
||||||
public void createSurface(final boolean onTop,
|
public void createSurface(final boolean onTop,
|
||||||
final int x, final int y, final int w, final int h,
|
final int x, final int y, final int w, final int h,
|
||||||
final int imageDepth)
|
final int imageDepth)
|
||||||
@ -44,11 +60,20 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
if (m_surface != null)
|
if (m_surface != null)
|
||||||
removeView(m_surface);
|
removeView(m_surface);
|
||||||
|
|
||||||
QtSurface surface = new QtSurface(getContext(),
|
setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
|
||||||
QtWindow.this, QtWindow.this.getId(),
|
// TODO currently setting child windows to onTop, since their surfaces
|
||||||
onTop, imageDepth);
|
// now get created earlier than the parents -> they are behind the parent window
|
||||||
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
|
// without this, and SurfaceView z-ordering is limited
|
||||||
|
boolean tempOnTop = onTop || (m_parentWindow != null);
|
||||||
|
|
||||||
|
QtSurface surface = new QtSurface(getContext(), QtWindow.this,
|
||||||
|
QtWindow.this.getId(), tempOnTop, imageDepth);
|
||||||
|
surface.setLayoutParams(new QtLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
// The QtSurface 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(surface, 0);
|
addView(surface, 0);
|
||||||
m_surface = surface;
|
m_surface = surface;
|
||||||
}
|
}
|
||||||
@ -68,16 +93,34 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSurfaceGeometry(final int x, final int y, final int w, final int h)
|
public void setGeometry(final int x, final int y, final int w, final int h)
|
||||||
{
|
{
|
||||||
QtNative.runAction(new Runnable() {
|
QtNative.runAction(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
QtLayout.LayoutParams lp = new QtLayout.LayoutParams(w, h, x, y);
|
QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
|
||||||
if (m_surface != null)
|
}
|
||||||
m_surface.setLayoutParams(lp);
|
});
|
||||||
else if (m_nativeView != null)
|
}
|
||||||
m_nativeView.setLayoutParams(lp);
|
|
||||||
|
public void addChildWindow(QtWindow window)
|
||||||
|
{
|
||||||
|
QtNative.runAction(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
m_childWindows.put(window.getId(), window);
|
||||||
|
addView(window, getChildCount());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChildWindow(int id)
|
||||||
|
{
|
||||||
|
QtNative.runAction(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (m_childWindows.containsKey(id))
|
||||||
|
removeView(m_childWindows.remove(id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -92,13 +135,35 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
removeView(m_nativeView);
|
removeView(m_nativeView);
|
||||||
|
|
||||||
m_nativeView = view;
|
m_nativeView = view;
|
||||||
m_nativeView.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
|
QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
|
||||||
|
m_nativeView.setLayoutParams(new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
addView(m_nativeView);
|
addView(m_nativeView);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void bringChildToFront(int id)
|
||||||
|
{
|
||||||
|
View view = m_childWindows.get(id);
|
||||||
|
if (view != null) {
|
||||||
|
if (getChildCount() > 0)
|
||||||
|
moveChild(view, getChildCount() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bringChildToBack(int id) {
|
||||||
|
QtNative.runAction(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
View view = m_childWindows.get(id);
|
||||||
|
if (view != null) {
|
||||||
|
moveChild(view, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void removeNativeView()
|
public void removeNativeView()
|
||||||
{
|
{
|
||||||
QtNative.runAction(new Runnable() {
|
QtNative.runAction(new Runnable() {
|
||||||
@ -111,4 +176,22 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setParent(QtWindow parentWindow)
|
||||||
|
{
|
||||||
|
if (m_parentWindow == parentWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_parentWindow != null)
|
||||||
|
m_parentWindow.removeChildWindow(getId());
|
||||||
|
|
||||||
|
m_parentWindow = parentWindow;
|
||||||
|
if (m_parentWindow != null)
|
||||||
|
m_parentWindow.addChildWindow(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtWindow parent()
|
||||||
|
{
|
||||||
|
return m_parentWindow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ void QAndroidPlatformForeignWindow::setGeometry(const QRect &rect)
|
|||||||
QAndroidPlatformWindow::setGeometry(rect);
|
QAndroidPlatformWindow::setGeometry(rect);
|
||||||
|
|
||||||
if (m_nativeViewInserted)
|
if (m_nativeViewInserted)
|
||||||
setSurfaceGeometry(rect);
|
setNativeGeometry(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformForeignWindow::setVisible(bool visible)
|
void QAndroidPlatformForeignWindow::setVisible(bool visible)
|
||||||
@ -63,11 +63,6 @@ void QAndroidPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState
|
|||||||
QAndroidPlatformWindow::applicationStateChanged(state);
|
QAndroidPlatformWindow::applicationStateChanged(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformForeignWindow::setParent(const QPlatformWindow *window)
|
|
||||||
{
|
|
||||||
Q_UNUSED(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAndroidPlatformForeignWindow::addViewToWindow()
|
void QAndroidPlatformForeignWindow::addViewToWindow()
|
||||||
{
|
{
|
||||||
jint x = 0, y = 0, w = -1, h = -1;
|
jint x = 0, y = 0, w = -1, h = -1;
|
||||||
|
@ -20,7 +20,6 @@ public:
|
|||||||
void setGeometry(const QRect &rect) override;
|
void setGeometry(const QRect &rect) override;
|
||||||
void setVisible(bool visible) override;
|
void setVisible(bool visible) override;
|
||||||
void applicationStateChanged(Qt::ApplicationState state) override;
|
void applicationStateChanged(Qt::ApplicationState state) override;
|
||||||
void setParent(const QPlatformWindow *window) override;
|
|
||||||
bool isForeignWindow() const override { return true; }
|
bool isForeignWindow() const override { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -45,7 +45,9 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
|
|||||||
m_oldGeometry = geometry();
|
m_oldGeometry = geometry();
|
||||||
|
|
||||||
QAndroidPlatformWindow::setGeometry(rect);
|
QAndroidPlatformWindow::setGeometry(rect);
|
||||||
setSurfaceGeometry(rect);
|
|
||||||
|
|
||||||
|
setNativeGeometry(rect);
|
||||||
|
|
||||||
QRect availableGeometry = screen()->availableGeometry();
|
QRect availableGeometry = screen()->availableGeometry();
|
||||||
if (rect.width() > 0
|
if (rect.width() > 0
|
||||||
@ -59,7 +61,7 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
|
|||||||
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
|
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
|
||||||
{
|
{
|
||||||
if (QAndroidEventDispatcherStopper::stopped() ||
|
if (QAndroidEventDispatcherStopper::stopped() ||
|
||||||
QGuiApplication::applicationState() == Qt::ApplicationSuspended || !window()->isTopLevel()) {
|
QGuiApplication::applicationState() == Qt::ApplicationSuspended) {
|
||||||
return m_eglSurface;
|
return m_eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ public:
|
|||||||
void removeWindow(QAndroidPlatformWindow *window);
|
void removeWindow(QAndroidPlatformWindow *window);
|
||||||
void raise(QAndroidPlatformWindow *window);
|
void raise(QAndroidPlatformWindow *window);
|
||||||
void lower(QAndroidPlatformWindow *window);
|
void lower(QAndroidPlatformWindow *window);
|
||||||
|
|
||||||
void topVisibleWindowChanged();
|
void topVisibleWindowChanged();
|
||||||
int displayId() const override;
|
int displayId() const override;
|
||||||
|
|
||||||
|
@ -39,7 +39,8 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
|
|||||||
m_oldGeometry = geometry();
|
m_oldGeometry = geometry();
|
||||||
|
|
||||||
QAndroidPlatformWindow::setGeometry(rect);
|
QAndroidPlatformWindow::setGeometry(rect);
|
||||||
setSurfaceGeometry(rect);
|
if (m_surfaceCreated)
|
||||||
|
setNativeGeometry(rect);
|
||||||
|
|
||||||
QRect availableGeometry = screen()->availableGeometry();
|
QRect availableGeometry = screen()->availableGeometry();
|
||||||
if (rect.width() > 0
|
if (rect.width() > 0
|
||||||
|
@ -19,10 +19,9 @@ Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
|
|||||||
Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0);
|
Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||||
|
|
||||||
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
|
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
|
||||||
: QPlatformWindow(window), m_nativeQtWindow(QNativeInterface::QAndroidApplication::context()),
|
: QPlatformWindow(window), m_nativeQtWindow(nullptr), m_nativeParentQtWindow(nullptr),
|
||||||
m_androidSurfaceObject(nullptr)
|
m_androidSurfaceObject(nullptr)
|
||||||
{
|
{
|
||||||
m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
|
|
||||||
m_windowFlags = Qt::Widget;
|
m_windowFlags = Qt::Widget;
|
||||||
m_windowState = Qt::WindowNoState;
|
m_windowState = Qt::WindowNoState;
|
||||||
// the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
|
// the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
|
||||||
@ -48,22 +47,44 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
|
|||||||
if (requestedNativeGeometry != finalNativeGeometry)
|
if (requestedNativeGeometry != finalNativeGeometry)
|
||||||
setGeometry(finalNativeGeometry);
|
setGeometry(finalNativeGeometry);
|
||||||
}
|
}
|
||||||
platformScreen()->addWindow(this);
|
|
||||||
|
if (parent())
|
||||||
|
m_nativeParentQtWindow = static_cast<QAndroidPlatformWindow*>(parent())->nativeWindow();
|
||||||
|
|
||||||
|
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this]() {
|
||||||
|
m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
|
||||||
|
QNativeInterface::QAndroidApplication::context(),
|
||||||
|
m_nativeParentQtWindow);
|
||||||
|
m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
|
||||||
|
}).waitForFinished();
|
||||||
|
|
||||||
|
if (window->isTopLevel())
|
||||||
|
platformScreen()->addWindow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
QAndroidPlatformWindow::~QAndroidPlatformWindow()
|
QAndroidPlatformWindow::~QAndroidPlatformWindow()
|
||||||
{
|
{
|
||||||
platformScreen()->removeWindow(this);
|
if (window()->isTopLevel())
|
||||||
|
platformScreen()->removeWindow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void QAndroidPlatformWindow::lower()
|
void QAndroidPlatformWindow::lower()
|
||||||
{
|
{
|
||||||
|
if (m_nativeParentQtWindow.isValid()) {
|
||||||
|
m_nativeParentQtWindow.callMethod<void>("bringChildToBack", nativeViewId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
platformScreen()->lower(this);
|
platformScreen()->lower(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformWindow::raise()
|
void QAndroidPlatformWindow::raise()
|
||||||
{
|
{
|
||||||
|
if (m_nativeParentQtWindow.isValid()) {
|
||||||
|
m_nativeParentQtWindow.callMethod<void>("bringChildToFront", nativeViewId());
|
||||||
|
QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
updateSystemUiVisibility();
|
updateSystemUiVisibility();
|
||||||
platformScreen()->raise(this);
|
platformScreen()->raise(this);
|
||||||
}
|
}
|
||||||
@ -87,16 +108,20 @@ void QAndroidPlatformWindow::setGeometry(const QRect &rect)
|
|||||||
|
|
||||||
void QAndroidPlatformWindow::setVisible(bool visible)
|
void QAndroidPlatformWindow::setVisible(bool visible)
|
||||||
{
|
{
|
||||||
|
m_nativeQtWindow.callMethod<void>("setVisible", visible);
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
updateSystemUiVisibility();
|
if (window()->isTopLevel()) {
|
||||||
if ((m_windowState & Qt::WindowFullScreen)
|
updateSystemUiVisibility();
|
||||||
|| ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) {
|
if ((m_windowState & Qt::WindowFullScreen)
|
||||||
setGeometry(platformScreen()->geometry());
|
|| ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) {
|
||||||
} else if (m_windowState & Qt::WindowMaximized) {
|
setGeometry(platformScreen()->geometry());
|
||||||
setGeometry(platformScreen()->availableGeometry());
|
} else if (m_windowState & Qt::WindowMaximized) {
|
||||||
|
setGeometry(platformScreen()->availableGeometry());
|
||||||
|
}
|
||||||
|
requestActivateWindow();
|
||||||
}
|
}
|
||||||
requestActivateWindow();
|
} else if (window()->isTopLevel() && window() == qGuiApp->focusWindow()) {
|
||||||
} else if (window() == qGuiApp->focusWindow()) {
|
|
||||||
platformScreen()->topVisibleWindowChanged();
|
platformScreen()->topVisibleWindowChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,13 +157,22 @@ Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const
|
|||||||
|
|
||||||
void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
|
void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
|
||||||
{
|
{
|
||||||
// even though we do not yet support child windows properly, any windows getting a parent
|
using namespace QtJniTypes;
|
||||||
// should be removed from screen's window stack which is only for top level windows,
|
|
||||||
// and respectively any window becoming top level should go in there
|
|
||||||
if (window) {
|
if (window) {
|
||||||
platformScreen()->removeWindow(this);
|
// If we were a top level window, remove from screen
|
||||||
|
if (!m_nativeParentQtWindow.isValid())
|
||||||
|
platformScreen()->removeWindow(this);
|
||||||
|
|
||||||
|
const QAndroidPlatformWindow *androidWindow =
|
||||||
|
static_cast<const QAndroidPlatformWindow*>(window);
|
||||||
|
const QtWindow parentWindow = androidWindow->nativeWindow();
|
||||||
|
// If this was a child window of another window, the java method takes care of that
|
||||||
|
m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
|
||||||
|
m_nativeParentQtWindow = parentWindow;
|
||||||
} else {
|
} else {
|
||||||
|
m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
|
||||||
platformScreen()->addWindow(this);
|
platformScreen()->addWindow(this);
|
||||||
|
m_nativeParentQtWindow = QJniObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +188,7 @@ void QAndroidPlatformWindow::propagateSizeHints()
|
|||||||
|
|
||||||
void QAndroidPlatformWindow::requestActivateWindow()
|
void QAndroidPlatformWindow::requestActivateWindow()
|
||||||
{
|
{
|
||||||
|
// raise() will handle differences between top level and child windows, and requesting focus
|
||||||
if (!blockedByModal())
|
if (!blockedByModal())
|
||||||
raise();
|
raise();
|
||||||
}
|
}
|
||||||
@ -214,7 +249,7 @@ void QAndroidPlatformWindow::destroySurface()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &geometry)
|
void QAndroidPlatformWindow::setNativeGeometry(const QRect &geometry)
|
||||||
{
|
{
|
||||||
if (!m_surfaceCreated)
|
if (!m_surfaceCreated)
|
||||||
return;
|
return;
|
||||||
@ -229,7 +264,7 @@ void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &geometry)
|
|||||||
w = geometry.width();
|
w = geometry.width();
|
||||||
h = geometry.height();
|
h = geometry.height();
|
||||||
}
|
}
|
||||||
m_nativeQtWindow.callMethod<void>("setSurfaceGeometry", x, y, w, h);
|
m_nativeQtWindow.callMethod<void>("setGeometry", x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
|
void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
|
||||||
|
@ -65,7 +65,7 @@ protected:
|
|||||||
void unlockSurface() { m_surfaceMutex.unlock(); }
|
void unlockSurface() { m_surfaceMutex.unlock(); }
|
||||||
void createSurface();
|
void createSurface();
|
||||||
void destroySurface();
|
void destroySurface();
|
||||||
void setSurfaceGeometry(const QRect &geometry);
|
void setNativeGeometry(const QRect &geometry);
|
||||||
void sendExpose() const;
|
void sendExpose() const;
|
||||||
bool blockedByModal() const;
|
bool blockedByModal() const;
|
||||||
|
|
||||||
@ -76,6 +76,7 @@ protected:
|
|||||||
WId m_windowId;
|
WId m_windowId;
|
||||||
|
|
||||||
QtJniTypes::QtWindow m_nativeQtWindow;
|
QtJniTypes::QtWindow m_nativeQtWindow;
|
||||||
|
QtJniTypes::QtWindow m_nativeParentQtWindow;
|
||||||
// The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex
|
// The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex
|
||||||
QtJniTypes::Surface m_androidSurfaceObject;
|
QtJniTypes::Surface m_androidSurfaceObject;
|
||||||
QWaitCondition m_surfaceWaitCondition;
|
QWaitCondition m_surfaceWaitCondition;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_WIN) || QT_CONFIG(xcb)
|
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_WIN) || QT_CONFIG(xcb) || defined(ANDROID)
|
||||||
#include "../../shared/nativewindow.h"
|
#include "../../shared/nativewindow.h"
|
||||||
#define HAVE_NATIVE_WINDOW
|
#define HAVE_NATIVE_WINDOW
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,6 +14,12 @@
|
|||||||
# include <winuser.h>
|
# include <winuser.h>
|
||||||
#elif QT_CONFIG(xcb)
|
#elif QT_CONFIG(xcb)
|
||||||
# include <xcb/xcb.h>
|
# include <xcb/xcb.h>
|
||||||
|
#elif defined(ANDROID)
|
||||||
|
# include <QtCore/qjniobject.h>
|
||||||
|
# include <QtCore/qjnitypes.h>
|
||||||
|
# include <QtCore/qnativeinterface.h>
|
||||||
|
Q_DECLARE_JNI_CLASS(View, "android/view/View")
|
||||||
|
Q_DECLARE_JNI_CLASS(ViewParent, "android/view/ViewParent")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class NativeWindow
|
class NativeWindow
|
||||||
@ -28,6 +34,8 @@ public:
|
|||||||
using Handle = HWND;
|
using Handle = HWND;
|
||||||
#elif QT_CONFIG(xcb)
|
#elif QT_CONFIG(xcb)
|
||||||
using Handle = xcb_window_t;
|
using Handle = xcb_window_t;
|
||||||
|
#elif defined(ANDROID)
|
||||||
|
using Handle = QtJniTypes::View;;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NativeWindow();
|
NativeWindow();
|
||||||
@ -249,6 +257,48 @@ void NativeWindow::setParent(WId parent)
|
|||||||
parent ? Handle(parent) : screen->root, 0, 0);
|
parent ? Handle(parent) : screen->root, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined (ANDROID)
|
||||||
|
NativeWindow::NativeWindow()
|
||||||
|
{
|
||||||
|
m_handle = QJniObject::construct<QtJniTypes::View, QtJniTypes::Context>(
|
||||||
|
QNativeInterface::QAndroidApplication::context());
|
||||||
|
m_handle.callMethod<void>("setBackgroundColor", 0xffffaaff);
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeWindow::~NativeWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeWindow::operator WId() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<WId>(m_handle.object());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindow::setGeometry(const QRect &rect)
|
||||||
|
{
|
||||||
|
// No-op, the view geometry is handled by the QWindow constructed from it
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect NativeWindow::geometry() const
|
||||||
|
{
|
||||||
|
int x = m_handle.callMethod<jint>("getX");
|
||||||
|
int y = m_handle.callMethod<jint>("getY");
|
||||||
|
int w = m_handle.callMethod<jint>("getWidth");
|
||||||
|
int h = m_handle.callMethod<jint>("getHeight");
|
||||||
|
return QRect(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
WId NativeWindow::parentWinId() const
|
||||||
|
{
|
||||||
|
// TODO note, the returned object is a ViewParent, not necessarily
|
||||||
|
// a View - what is this used for?
|
||||||
|
using namespace QtJniTypes;
|
||||||
|
ViewParent parentView = m_handle.callMethod<ViewParent>("getParent");
|
||||||
|
if (parentView.isValid())
|
||||||
|
return reinterpret_cast<WId>(parentView.object());
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NATIVEWINDOW_H
|
#endif // NATIVEWINDOW_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user