/*
* Copyright (C) 2016-2017 Canonical, Ltd.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#ifndef TOPLEVELWINDOWMODEL_H
#define TOPLEVELWINDOWMODEL_H
#include
#include
#include "WindowManagerGlobal.h"
Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
class Window;
namespace unity {
namespace shell {
namespace application {
class ApplicationInfoInterface;
class ApplicationManagerInterface;
class MirSurfaceInterface;
class SurfaceManagerInterface;
}
}
}
/**
* @brief A model of top-level surfaces
*
* It's an abstraction of top-level application windows.
*
* When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
* still starting up. A shell should then display a splash screen or saved screenshot of the application
* until its surface comes up.
*
* As applications can have multiple surfaces and you can also have entries without surfaces at all,
* the only way to unambiguously refer to an entry in this model is through its id.
*/
class WINDOWMANAGERQML_EXPORT TopLevelWindowModel : public QAbstractListModel
{
Q_OBJECT
/**
* @brief Number of top-level surfaces in this model
*
* This is the same as rowCount, added in order to keep compatibility with QML ListModels.
*/
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
/**
* @brief The input method surface, if any
*
* The surface of a onscreen keyboard (akak "virtual keyboard") would be kept here and not in the model itself.
*/
Q_PROPERTY(unity::shell::application::MirSurfaceInterface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged)
/**
* @brief The currently focused window, if any
*/
Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager
READ surfaceManager
WRITE setSurfaceManager
NOTIFY surfaceManagerChanged)
Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager
READ applicationManager
WRITE setApplicationManager
NOTIFY applicationManagerChanged)
/**
The id to be used on the next entry created
Useful for tests
*/
Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
public:
/**
* @brief The Roles supported by the model
*
* WindowRole - A Window.
* ApplicationRole - An ApplicationInfoInterface
*/
enum Roles {
WindowRole = Qt::UserRole,
ApplicationRole = Qt::UserRole + 1,
};
TopLevelWindowModel();
// From QAbstractItemModel
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role) const override;
QHash roleNames() const override {
QHash roleNames { {WindowRole, "window"},
{ApplicationRole, "application"} };
return roleNames;
}
// Own API
unity::shell::application::MirSurfaceInterface* inputMethodSurface() const;
Window* focusedWindow() const;
unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; }
void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; }
void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
int nextId() const { return m_nextId; }
public:
/**
* @brief Returns the surface at the given index
*
* It will be a nullptr if the application is still starting up and thus hasn't yet created
* and drawn into a surface.
*
* Same as windowAt(index).surface()
*/
Q_INVOKABLE unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
/**
* @brief Returns the window at the given index
*
* Will always be valid
*/
Q_INVOKABLE Window *windowAt(int index) const;
/**
* @brief Returns the application at the given index
*/
Q_INVOKABLE unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
/**
* @brief Returns the unique id of the element at the given index
*/
Q_INVOKABLE int idAt(int index) const;
/**
* @brief Returns the index where the row with the given id is located
*
* Returns -1 if there's no row with the given id.
*/
Q_INVOKABLE int indexForId(int id) const;
/**
* @brief Raises the row with the given id to the top of the window stack (index == count-1)
*/
Q_INVOKABLE void raiseId(int id);
Q_SIGNALS:
void countChanged();
void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
void focusedWindowChanged(Window *focusedWindow);
void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*);
void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*);
/**
* @brief Emitted when the list changes
*
* Emitted when model gains an element, loses an element or when elements exchange positions.
*/
void listChanged();
void nextIdChanged();
private Q_SLOTS:
void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface);
void onSurfacesRaised(const QVector &surfaces);
void onModificationsStarted();
void onModificationsEnded();
private:
void doRaiseId(int id);
int generateId();
int nextFreeId(int candidateId, const int latestId);
int nextId(int id) const;
QString toString();
int indexOf(unity::shell::application::MirSurfaceInterface *surface);
void setInputMethodWindow(Window *window);
void setFocusedWindow(Window *window);
void removeInputMethodWindow();
int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
void deleteAt(int index);
void removeAt(int index);
void addApplication(unity::shell::application::ApplicationInfoInterface *application);
void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
void prependSurface(unity::shell::application::MirSurfaceInterface *surface,
unity::shell::application::ApplicationInfoInterface *application);
void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
unity::shell::application::ApplicationInfoInterface *application);
void prependWindow(Window *window, unity::shell::application::ApplicationInfoInterface *application);
void connectWindow(Window *window);
void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
void move(int from, int to);
void activateEmptyWindow(Window *window);
void activateTopMostWindowWithoutId(int forbiddenId);
Window *createWindow(unity::shell::application::MirSurfaceInterface *surface);
struct ModelEntry {
ModelEntry() {}
ModelEntry(Window *window,
unity::shell::application::ApplicationInfoInterface *application)
: window(window), application(application) {}
Window *window{nullptr};
unity::shell::application::ApplicationInfoInterface *application{nullptr};
bool removeOnceSurfaceDestroyed{false};
};
QVector m_windowModel;
Window* m_inputMethodWindow{nullptr};
Window* m_focusedWindow{nullptr};
int m_nextId{1};
// Just something big enough that we don't risk running out of unused id numbers.
// Not sure if QML int type supports something close to std::numeric_limits::max() and
// there's no reason to try out its limits.
static const int m_maxId{1000000};
unity::shell::application::ApplicationManagerInterface* m_applicationManager{nullptr};
unity::shell::application::SurfaceManagerInterface *m_surfaceManager{nullptr};
enum ModelState {
IdleState,
InsertingState,
RemovingState,
MovingState,
ResettingState
};
ModelState m_modelState{IdleState};
// Valid between modificationsStarted and modificationsEnded
bool m_focusedWindowChanged{false};
Window *m_newlyFocusedWindow{nullptr};
};
#endif // TOPLEVELWINDOWMODEL_H