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