2
* Copyright (C) 2016 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "windowmodel.h"
19
#include "mirsurface.h"
22
#include "nativeinterface.h"
25
#include <QGuiApplication>
28
using namespace qtmir;
30
WindowModel::WindowModel()
32
auto nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
34
if (!nativeInterface) {
35
qFatal("ERROR: Unity.Application QML plugin requires use of the 'mirserver' QPA plugin");
38
m_windowController = static_cast<WindowControllerInterface*>(nativeInterface->nativeResourceForIntegration("WindowController"));
40
auto windowModel = static_cast<WindowModelNotifier*>(nativeInterface->nativeResourceForIntegration("WindowModelNotifier"));
41
connectToWindowModelNotifier(windowModel);
44
WindowModel::WindowModel(WindowModelNotifier *notifier,
45
WindowControllerInterface *controller)
46
: m_windowController(controller)
48
connectToWindowModelNotifier(notifier);
51
void WindowModel::connectToWindowModelNotifier(WindowModelNotifier *notifier)
53
connect(notifier, &WindowModelNotifier::windowAdded, this, &WindowModel::onWindowAdded, Qt::QueuedConnection);
54
connect(notifier, &WindowModelNotifier::windowRemoved, this, &WindowModel::onWindowRemoved, Qt::QueuedConnection);
55
connect(notifier, &WindowModelNotifier::windowReady, this, &WindowModel::onWindowReady, Qt::QueuedConnection);
56
connect(notifier, &WindowModelNotifier::windowMoved, this, &WindowModel::onWindowMoved, Qt::QueuedConnection);
57
connect(notifier, &WindowModelNotifier::windowStateChanged, this, &WindowModel::onWindowStateChanged, Qt::QueuedConnection);
58
connect(notifier, &WindowModelNotifier::windowFocusChanged, this, &WindowModel::onWindowFocusChanged, Qt::QueuedConnection);
59
connect(notifier, &WindowModelNotifier::windowsRaised, this, &WindowModel::onWindowsRaised, Qt::QueuedConnection);
62
QHash<int, QByteArray> WindowModel::roleNames() const
64
QHash<int, QByteArray> roleNames;
65
roleNames.insert(SurfaceRole, "surface");
69
void WindowModel::onWindowAdded(const NewWindow &window)
71
if (window.windowInfo.type() == mir_surface_type_inputmethod) {
72
addInputMethodWindow(window);
76
const int index = m_windowModel.count();
77
beginInsertRows(QModelIndex(), index, index);
78
m_windowModel.append(new MirSurface(window, m_windowController));
80
Q_EMIT countChanged();
83
void WindowModel::onWindowRemoved(const miral::WindowInfo &windowInfo)
85
if (windowInfo.type() == mir_surface_type_inputmethod) {
86
removeInputMethodWindow();
90
const int index = findIndexOf(windowInfo.window());
92
beginRemoveRows(QModelIndex(), index, index);
93
m_windowModel.takeAt(index);
95
Q_EMIT countChanged();
98
void WindowModel::onWindowReady(const miral::WindowInfo &windowInfo)
100
if (auto mirSurface = find(windowInfo)) {
101
mirSurface->setReady();
105
void WindowModel::onWindowMoved(const miral::WindowInfo &windowInfo, const QPoint topLeft)
107
if (auto mirSurface = find(windowInfo)) {
108
mirSurface->setPosition(topLeft);
112
void WindowModel::onWindowFocusChanged(const miral::WindowInfo &windowInfo, bool focused)
114
if (auto mirSurface = find(windowInfo)) {
115
mirSurface->setFocused(focused);
119
void WindowModel::onWindowStateChanged(const miral::WindowInfo &windowInfo, Mir::State state)
121
if (auto mirSurface = find(windowInfo)) {
122
mirSurface->updateState(state);
126
void WindowModel::addInputMethodWindow(const NewWindow &windowInfo)
128
if (m_inputMethodSurface) {
129
qDebug("Multiple Input Method Surfaces created, removing the old one!");
130
delete m_inputMethodSurface;
132
m_inputMethodSurface = new MirSurface(windowInfo, m_windowController);
133
Q_EMIT inputMethodSurfaceChanged(m_inputMethodSurface);
136
void WindowModel::removeInputMethodWindow()
138
if (m_inputMethodSurface) {
139
delete m_inputMethodSurface;
140
m_inputMethodSurface = nullptr;
141
Q_EMIT inputMethodSurfaceChanged(m_inputMethodSurface);
145
void WindowModel::onWindowsRaised(const std::vector<miral::Window> &windows)
147
// Reminder: last item in the "windows" list should end up at the top of the model
148
const int modelCount = m_windowModel.count();
149
const int raiseCount = windows.size();
151
// Assumption: no NO-OPs are in this list - Qt will crash on endMoveRows() if you try NO-OPs!!!
153
// 1. "indices" is an empty list
154
// 2. "indices" of the form (..., modelCount - 2, modelCount - 1) which results in an unchanged list
156
// Precompute the list of indices of Windows/Surfaces to raise, including the offsets due to
157
// indices which have already been moved.
158
QVector<QPair<int /*from*/, int /*to*/>> moveList;
160
for (int i=raiseCount-1; i>=0; i--) {
161
int from = findIndexOf(windows[i]);
162
const int to = modelCount - raiseCount + i;
165
// how many list items under "index" have been moved so far, correct "from" to suit
166
for (int j=raiseCount-1; j>i; j--) {
167
if (findIndexOf(windows[j]) < from) {
174
// is NO-OP, would result in moving element to itself
176
moveList.prepend({from, to});
180
// Perform the moving, trusting the moveList is correct for each iteration.
182
for (int i=moveList.count()-1; i>=0; i--) {
183
const int from = moveList[i].first;
184
const int to = moveList[i].second;
186
beginMoveRows(parent, from, from, parent, to+1);
187
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
188
const auto &window = m_windowModel.takeAt(from);
189
m_windowModel.insert(to, window);
191
m_windowModel.move(from, to);
198
int WindowModel::rowCount(const QModelIndex &/*parent*/) const
200
return m_windowModel.count();
203
QVariant WindowModel::data(const QModelIndex &index, int role) const
205
if (index.row() < 0 || index.row() >= m_windowModel.count())
208
if (role == SurfaceRole) {
209
auto &surface = m_windowModel.at(index.row());
210
return QVariant::fromValue(surface);
216
MirSurface *WindowModel::find(const miral::WindowInfo &needle) const
218
auto window = needle.window();
219
Q_FOREACH(const auto mirSurface, m_windowModel) {
220
if (mirSurface->window() == window) {
227
int WindowModel::findIndexOf(const miral::Window &needle) const
229
for (int i=0; i<m_windowModel.count(); i++) {
230
if (m_windowModel[i]->window() == needle) {