1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtVirtualKeyboard/private/qvirtualkeyboard_global_p.h> |
5 | #include <QtVirtualKeyboard/private/desktopinputpanel_p.h> |
6 | #include <QtVirtualKeyboard/private/appinputpanel_p_p.h> |
7 | #include <QtVirtualKeyboard/private/inputview_p.h> |
8 | #include <QtVirtualKeyboard/private/platforminputcontext_p.h> |
9 | #include <QtVirtualKeyboard/private/qvirtualkeyboardinputcontext_p.h> |
10 | #include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h> |
11 | #include <QGuiApplication> |
12 | #include <QQmlEngine> |
13 | #include <QScreen> |
14 | #include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h> |
15 | #include <qpa/qplatformnativeinterface.h> |
16 | #include <QtCore/private/qobject_p.h> |
17 | #include <QtCore/QLibraryInfo> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | namespace QtVirtualKeyboard { |
21 | |
22 | class DesktopInputPanelPrivate : public AppInputPanelPrivate |
23 | { |
24 | public: |
25 | enum WindowingSystem { |
26 | Windows, |
27 | Xcb, |
28 | Other, |
29 | }; |
30 | |
31 | DesktopInputPanelPrivate() : |
32 | AppInputPanelPrivate(), |
33 | view(), |
34 | keyboardRect(), |
35 | previewRect(), |
36 | previewVisible(false), |
37 | previewBindingActive(false), |
38 | windowingSystem(Other) |
39 | { |
40 | const QString platformName = QGuiApplication::platformName(); |
41 | if (platformName == QLatin1String("windows" )) |
42 | windowingSystem = Windows; |
43 | else if (platformName == QLatin1String("xcb" )) |
44 | windowingSystem = Xcb; |
45 | } |
46 | |
47 | QScopedPointer<InputView> view; |
48 | QRectF keyboardRect; |
49 | QRectF previewRect; |
50 | bool previewVisible; |
51 | bool previewBindingActive; |
52 | WindowingSystem windowingSystem; |
53 | }; |
54 | |
55 | /*! |
56 | \class QtVirtualKeyboard::DesktopInputPanel |
57 | \internal |
58 | */ |
59 | |
60 | DesktopInputPanel::DesktopInputPanel(QObject *parent) : |
61 | AppInputPanel(*new DesktopInputPanelPrivate(), parent) |
62 | { |
63 | /* Activate the alpha buffer for this application. |
64 | */ |
65 | QQuickWindow::setDefaultAlphaBuffer(true); |
66 | QScreen *screen = QGuiApplication::primaryScreen(); |
67 | connect(asender: screen, SIGNAL(virtualGeometryChanged(QRect)), SLOT(repositionView(QRect))); |
68 | } |
69 | |
70 | DesktopInputPanel::~DesktopInputPanel() |
71 | { |
72 | } |
73 | |
74 | void DesktopInputPanel::show() |
75 | { |
76 | AppInputPanel::show(); |
77 | Q_D(DesktopInputPanel); |
78 | if (d->view) { |
79 | repositionView(rect: QGuiApplication::primaryScreen()->availableGeometry()); |
80 | d->view->show(); |
81 | } |
82 | } |
83 | |
84 | void DesktopInputPanel::hide() |
85 | { |
86 | AppInputPanel::hide(); |
87 | Q_D(DesktopInputPanel); |
88 | if (d->view) |
89 | d->view->hide(); |
90 | } |
91 | |
92 | bool DesktopInputPanel::isVisible() const |
93 | { |
94 | return AppInputPanel::isVisible(); |
95 | } |
96 | |
97 | void DesktopInputPanel::setInputRect(const QRect &inputRect) |
98 | { |
99 | Q_D(DesktopInputPanel); |
100 | d->keyboardRect = inputRect; |
101 | updateInputRegion(); |
102 | } |
103 | |
104 | void DesktopInputPanel::createView() |
105 | { |
106 | Q_D(DesktopInputPanel); |
107 | if (!d->view) { |
108 | if (qGuiApp) { |
109 | connect(qGuiApp, SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*))); |
110 | focusWindowChanged(qGuiApp->focusWindow()); |
111 | } |
112 | d->view.reset(other: new InputView()); |
113 | d->view->setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus); |
114 | /* Set appropriate WindowType for target environment. |
115 | There seems to be no common type which would |
116 | work in all environments. The purpose of this |
117 | flag is to avoid the window from capturing focus, |
118 | as well as hiding it from the task bar. */ |
119 | switch (d->windowingSystem) { |
120 | case DesktopInputPanelPrivate::Xcb: |
121 | d->view->setFlags(d->view->flags() | Qt::Window | Qt::BypassWindowManagerHint); |
122 | break; |
123 | default: |
124 | d->view->setFlags(d->view->flags() | Qt::Tool); |
125 | break; |
126 | } |
127 | d->view->setColor(QColor(Qt::transparent)); |
128 | d->view->setSource(QUrl(QLatin1String("qrc:///qt-project.org/imports/QtQuick/VirtualKeyboard/InputPanel.qml" ))); |
129 | if (QGuiApplication *app = qGuiApp) |
130 | connect(asender: app, SIGNAL(aboutToQuit()), SLOT(destroyView())); |
131 | } |
132 | } |
133 | |
134 | void DesktopInputPanel::destroyView() |
135 | { |
136 | Q_D(DesktopInputPanel); |
137 | d->view.reset(); |
138 | d->previewBindingActive = false; |
139 | } |
140 | |
141 | void DesktopInputPanel::repositionView(const QRect &rect) |
142 | { |
143 | Q_D(DesktopInputPanel); |
144 | VIRTUALKEYBOARD_DEBUG() << "DesktopInputPanel::repositionView():" << rect; |
145 | if (d->view && d->view->geometry() != rect) { |
146 | QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext(); |
147 | if (inputContext) { |
148 | inputContext->setAnimating(true); |
149 | if (!d->previewBindingActive) { |
150 | QVirtualKeyboardInputContextPrivate *inputContextPrivate = inputContext->priv(); |
151 | QObject::connect(sender: inputContextPrivate, signal: &QVirtualKeyboardInputContextPrivate::previewRectangleChanged, context: this, slot: &DesktopInputPanel::previewRectangleChanged); |
152 | QObject::connect(sender: inputContextPrivate, signal: &QVirtualKeyboardInputContextPrivate::previewVisibleChanged, context: this, slot: &DesktopInputPanel::previewVisibleChanged); |
153 | d->previewBindingActive = true; |
154 | } |
155 | } |
156 | d->view->setResizeMode(QQuickView::SizeViewToRootObject); |
157 | setInputRect(QRect()); |
158 | d->view->setGeometry(rect); |
159 | d->view->setResizeMode(QQuickView::SizeRootObjectToView); |
160 | if (inputContext) |
161 | inputContext->setAnimating(false); |
162 | } |
163 | } |
164 | |
165 | void DesktopInputPanel::focusWindowChanged(QWindow *focusWindow) |
166 | { |
167 | disconnect(receiver: this, SLOT(focusWindowVisibleChanged(bool))); |
168 | if (focusWindow) |
169 | connect(sender: focusWindow, signal: &QWindow::visibleChanged, context: this, slot: &DesktopInputPanel::focusWindowVisibleChanged); |
170 | } |
171 | |
172 | void DesktopInputPanel::focusWindowVisibleChanged(bool visible) |
173 | { |
174 | if (!visible) { |
175 | QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext(); |
176 | if (inputContext) |
177 | inputContext->priv()->hideInputPanel(); |
178 | } |
179 | } |
180 | |
181 | void DesktopInputPanel::previewRectangleChanged() |
182 | { |
183 | Q_D(DesktopInputPanel); |
184 | QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext(); |
185 | d->previewRect = inputContext->priv()->previewRectangle(); |
186 | if (d->previewVisible) |
187 | updateInputRegion(); |
188 | } |
189 | |
190 | void DesktopInputPanel::previewVisibleChanged() |
191 | { |
192 | Q_D(DesktopInputPanel); |
193 | QVirtualKeyboardInputContext *inputContext = qobject_cast<PlatformInputContext *>(object: parent())->inputContext(); |
194 | d->previewVisible = inputContext->priv()->previewVisible(); |
195 | if (d->view->isVisible()) |
196 | updateInputRegion(); |
197 | } |
198 | |
199 | void DesktopInputPanel::updateInputRegion() |
200 | { |
201 | Q_D(DesktopInputPanel); |
202 | |
203 | if (d->view.isNull() || d->keyboardRect.isEmpty()) |
204 | return; |
205 | |
206 | // Make sure the native window is created |
207 | if (!d->view->handle()) |
208 | d->view->create(); |
209 | |
210 | QRegion inputRegion(d->keyboardRect.toRect()); |
211 | if (d->previewVisible && !d->previewRect.isEmpty()) |
212 | inputRegion += d->previewRect.toRect(); |
213 | |
214 | d->view->setMask(inputRegion); |
215 | } |
216 | |
217 | } // namespace QtVirtualKeyboard |
218 | QT_END_NAMESPACE |
219 | |