Commit 0f7dc0cf authored by Yari Polla's avatar Yari Polla Committed by Devin Lin
Browse files

quicksettings: implement swipe view

parent 1ebd1ece
......@@ -8,6 +8,7 @@ set(mobileshellplugin_SRCS
mobileshellplugin.cpp
mobileshellsettings.cpp
quicksetting.cpp
paginatemodel.cpp
quicksettingsmodel.cpp
savedquicksettings.cpp
savedquicksettingsmodel.cpp
......
......@@ -20,6 +20,7 @@
#include "taskswitcher/displaysmodel.h"
#include "mobileshellsettings.h"
#include "paginatemodel.h"
#include "quicksetting.h"
#include "quicksettingsmodel.h"
#include "shellutil.h"
......@@ -44,6 +45,7 @@ void MobileShellPlugin::registerTypes(const char *uri)
qmlRegisterType<QuickSetting>(uri, 1, 0, "QuickSetting");
qmlRegisterType<QuickSettingsModel>(uri, 1, 0, "QuickSettingsModel");
qmlRegisterType<PaginateModel>(uri, 1, 0, "PaginateModel");
qmlRegisterType<SavedQuickSettings>(uri, 1, 0, "SavedQuickSettings");
qmlRegisterType<SavedQuickSettingsModel>(uri, 1, 0, "SavedQuickSettingsModel");
qmlRegisterSingletonType<WindowUtil>(uri, 1, 0, "WindowUtil", [](QQmlEngine *, QJSEngine *) -> QObject * {
......
/*
* SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "paginatemodel.h"
#include <QtMath>
class PaginateModel::PaginateModelPrivate
{
public:
int m_firstItem = 0;
int m_pageSize = 0;
QAbstractItemModel *m_sourceModel = nullptr;
bool m_hasStaticRowCount = false;
};
PaginateModel::PaginateModel(QObject *object)
: QAbstractListModel(object)
, d(new PaginateModelPrivate)
{
}
PaginateModel::~PaginateModel() = default;
int PaginateModel::firstItem() const
{
return d->m_firstItem;
}
void PaginateModel::setFirstItem(int row)
{
Q_ASSERT(row >= 0 && row < d->m_sourceModel->rowCount());
if (row != d->m_firstItem) {
beginResetModel();
d->m_firstItem = row;
endResetModel();
Q_EMIT firstItemChanged();
}
}
int PaginateModel::pageSize() const
{
return d->m_pageSize;
}
void PaginateModel::setPageSize(int count)
{
if (count != d->m_pageSize) {
const int oldSize = rowsByPageSize(d->m_pageSize);
const int newSize = rowsByPageSize(count);
const int difference = newSize - oldSize;
if (difference == 0) {
d->m_pageSize = count;
} else if (difference > 0) {
beginInsertRows(QModelIndex(), d->m_pageSize, d->m_pageSize + difference - 1);
d->m_pageSize = count;
endInsertRows();
} else {
beginRemoveRows(QModelIndex(), d->m_pageSize + difference, d->m_pageSize - 1);
d->m_pageSize = count;
endRemoveRows();
}
Q_EMIT pageSizeChanged();
}
}
QAbstractItemModel *PaginateModel::sourceModel() const
{
return d->m_sourceModel;
}
void PaginateModel::setSourceModel(QAbstractItemModel *model)
{
if (d->m_sourceModel) {
disconnect(d->m_sourceModel, nullptr, this, nullptr);
}
if (model != d->m_sourceModel) {
beginResetModel();
d->m_sourceModel = model;
if (model) {
connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &PaginateModel::_k_sourceRowsAboutToBeInserted);
connect(d->m_sourceModel, &QAbstractItemModel::rowsInserted, this, &PaginateModel::_k_sourceRowsInserted);
connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &PaginateModel::_k_sourceRowsAboutToBeRemoved);
connect(d->m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &PaginateModel::_k_sourceRowsRemoved);
connect(d->m_sourceModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &PaginateModel::_k_sourceRowsAboutToBeMoved);
connect(d->m_sourceModel, &QAbstractItemModel::rowsMoved, this, &PaginateModel::_k_sourceRowsMoved);
connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeInserted, this, &PaginateModel::_k_sourceColumnsAboutToBeInserted);
connect(d->m_sourceModel, &QAbstractItemModel::columnsInserted, this, &PaginateModel::_k_sourceColumnsInserted);
connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &PaginateModel::_k_sourceColumnsAboutToBeRemoved);
connect(d->m_sourceModel, &QAbstractItemModel::columnsRemoved, this, &PaginateModel::_k_sourceColumnsRemoved);
connect(d->m_sourceModel, &QAbstractItemModel::columnsAboutToBeMoved, this, &PaginateModel::_k_sourceColumnsAboutToBeMoved);
connect(d->m_sourceModel, &QAbstractItemModel::columnsMoved, this, &PaginateModel::_k_sourceColumnsMoved);
connect(d->m_sourceModel, &QAbstractItemModel::dataChanged, this, &PaginateModel::_k_sourceDataChanged);
connect(d->m_sourceModel, &QAbstractItemModel::headerDataChanged, this, &PaginateModel::_k_sourceHeaderDataChanged);
connect(d->m_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &PaginateModel::_k_sourceModelAboutToBeReset);
connect(d->m_sourceModel, &QAbstractItemModel::modelReset, this, &PaginateModel::_k_sourceModelReset);
connect(d->m_sourceModel, &QAbstractItemModel::rowsInserted, this, &PaginateModel::pageCountChanged);
connect(d->m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &PaginateModel::pageCountChanged);
connect(d->m_sourceModel, &QAbstractItemModel::modelReset, this, &PaginateModel::pageCountChanged);
}
endResetModel();
Q_EMIT sourceModelChanged();
}
}
QHash<int, QByteArray> PaginateModel::roleNames() const
{
return d->m_sourceModel ? d->m_sourceModel->roleNames() : QAbstractItemModel::roleNames();
}
int PaginateModel::rowsByPageSize(int size) const
{
return d->m_hasStaticRowCount ? size : !d->m_sourceModel ? 0 : qMin(d->m_sourceModel->rowCount() - d->m_firstItem, size);
}
int PaginateModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : rowsByPageSize(d->m_pageSize);
}
QModelIndex PaginateModel::mapToSource(const QModelIndex &idx) const
{
if (!d->m_sourceModel)
return QModelIndex();
return d->m_sourceModel->index(idx.row() + d->m_firstItem, idx.column());
}
QModelIndex PaginateModel::mapFromSource(const QModelIndex &idx) const
{
Q_ASSERT(idx.model() == d->m_sourceModel);
if (!d->m_sourceModel)
return QModelIndex();
return index(idx.row() - d->m_firstItem, idx.column());
}
QVariant PaginateModel::data(const QModelIndex &index, int role) const
{
if (!d->m_sourceModel)
return QVariant();
QModelIndex idx = mapToSource(index);
return idx.data(role);
}
void PaginateModel::firstPage()
{
setFirstItem(0);
}
void PaginateModel::lastPage()
{
setFirstItem((pageCount() - 1) * d->m_pageSize);
}
void PaginateModel::nextPage()
{
setFirstItem(d->m_firstItem + d->m_pageSize);
}
void PaginateModel::previousPage()
{
setFirstItem(d->m_firstItem - d->m_pageSize);
}
int PaginateModel::currentPage() const
{
return d->m_firstItem / d->m_pageSize;
}
int PaginateModel::pageCount() const
{
if (!d->m_sourceModel)
return 0;
const int rc = d->m_sourceModel->rowCount();
const int r = (rc % d->m_pageSize == 0) ? 1 : 0;
return qMax(qCeil(float(rc) / d->m_pageSize) - r, 1);
}
bool PaginateModel::hasStaticRowCount() const
{
return d->m_hasStaticRowCount;
}
void PaginateModel::setStaticRowCount(bool src)
{
if (src == d->m_hasStaticRowCount) {
return;
}
beginResetModel();
d->m_hasStaticRowCount = src;
endResetModel();
Q_EMIT staticRowCountChanged();
}
//////////////////////////////
void PaginateModel::_k_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
Q_UNUSED(end)
if (parent.isValid() || start != 0) {
return;
}
beginResetModel();
}
void PaginateModel::_k_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
{
Q_UNUSED(sourceParent)
Q_UNUSED(sourceStart)
Q_UNUSED(sourceEnd)
Q_UNUSED(destParent)
Q_UNUSED(dest)
beginResetModel();
}
void PaginateModel::_k_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
Q_UNUSED(end)
if (parent.isValid() || start != 0) {
return;
}
beginResetModel();
}
void PaginateModel::_k_sourceColumnsInserted(const QModelIndex &parent, int start, int end)
{
Q_UNUSED(end)
if (parent.isValid() || start != 0) {
return;
}
endResetModel();
}
void PaginateModel::_k_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
{
Q_UNUSED(sourceParent)
Q_UNUSED(sourceStart)
Q_UNUSED(sourceEnd)
Q_UNUSED(destParent)
Q_UNUSED(dest)
endResetModel();
}
void PaginateModel::_k_sourceColumnsRemoved(const QModelIndex &parent, int start, int end)
{
Q_UNUSED(end)
if (parent.isValid() || start != 0) {
return;
}
endResetModel();
}
void PaginateModel::_k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
if (topLeft.parent().isValid() || bottomRight.row() < d->m_firstItem || topLeft.row() > lastItem()) {
return;
}
QModelIndex idxTop = mapFromSource(topLeft);
QModelIndex idxBottom = mapFromSource(bottomRight);
if (!idxTop.isValid())
idxTop = index(0);
if (!idxBottom.isValid())
idxBottom = index(rowCount() - 1);
Q_EMIT dataChanged(idxTop, idxBottom, roles);
}
void PaginateModel::_k_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last)
{
Q_UNUSED(last)
if (first == 0)
Q_EMIT headerDataChanged(orientation, 0, 0);
}
void PaginateModel::_k_sourceModelAboutToBeReset()
{
beginResetModel();
}
void PaginateModel::_k_sourceModelReset()
{
endResetModel();
}
bool PaginateModel::isIntervalValid(const QModelIndex &parent, int start, int /*end*/) const
{
return !parent.isValid() && start <= lastItem();
}
bool PaginateModel::canSizeChange() const
{
return !d->m_hasStaticRowCount && currentPage() == pageCount() - 1;
}
void PaginateModel::_k_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
if (!isIntervalValid(parent, start, end)) {
return;
}
if (canSizeChange()) {
const int newStart = qMax(start - d->m_firstItem, 0);
const int insertedCount = qMin(end - start, pageSize() - newStart - 1);
beginInsertRows(QModelIndex(), newStart, newStart + insertedCount);
} else {
beginResetModel();
}
}
void PaginateModel::_k_sourceRowsInserted(const QModelIndex &parent, int start, int end)
{
if (!isIntervalValid(parent, start, end)) {
return;
}
if (canSizeChange()) {
endInsertRows();
} else {
endResetModel();
}
}
void PaginateModel::_k_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
{
Q_UNUSED(sourceParent)
Q_UNUSED(sourceStart)
Q_UNUSED(sourceEnd)
Q_UNUSED(destParent)
Q_UNUSED(dest)
// NOTE could optimize, unsure if it makes sense
beginResetModel();
}
void PaginateModel::_k_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest)
{
Q_UNUSED(sourceParent)
Q_UNUSED(sourceStart)
Q_UNUSED(sourceEnd)
Q_UNUSED(destParent)
Q_UNUSED(dest)
endResetModel();
}
void PaginateModel::_k_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
if (!isIntervalValid(parent, start, end)) {
return;
}
if (canSizeChange()) {
const int removedCount = end - start;
const int newStart = qMax(start - d->m_firstItem, 0);
beginRemoveRows(QModelIndex(), newStart, newStart + removedCount);
} else {
beginResetModel();
}
}
void PaginateModel::_k_sourceRowsRemoved(const QModelIndex &parent, int start, int end)
{
if (!isIntervalValid(parent, start, end)) {
return;
}
if (canSizeChange()) {
endRemoveRows();
} else {
beginResetModel();
}
}
int PaginateModel::lastItem() const
{
return d->m_firstItem + rowCount();
}
/*
* SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef PAGINATEMODEL_H
#define PAGINATEMODEL_H
#include <QAbstractListModel>
/**
* @class PaginateModel
*
* This class can be used to create representations of only a chunk of a model.
*
* With this component it will be possible to create views that only show a page
* of a model, instead of drawing all the elements in the model.
*/
class PaginateModel : public QAbstractListModel
{
Q_OBJECT
/** Holds the number of elements that will fit in a page */
Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize NOTIFY pageSizeChanged)
/** Tells what is the first row shown in the model */
Q_PROPERTY(int firstItem READ firstItem WRITE setFirstItem NOTIFY firstItemChanged)
/** The model we will be proxying */
Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
/** Among the totality of elements, indicates the one we're currently offering */
Q_PROPERTY(int currentPage READ currentPage NOTIFY firstItemChanged)
/** Provides the number of pages available, given the sourceModel size */
Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged)
/** If enabled, ensures that pageCount and pageSize are the same. */
Q_PROPERTY(bool staticRowCount READ hasStaticRowCount WRITE setStaticRowCount NOTIFY staticRowCountChanged)
public:
explicit PaginateModel(QObject *object = nullptr);
~PaginateModel() override;
int pageSize() const;
void setPageSize(int count);
int firstItem() const;
void setFirstItem(int row);
/**
* @returns Last visible item.
*
* Convenience function
*/
int lastItem() const;
QAbstractItemModel *sourceModel() const;
void setSourceModel(QAbstractItemModel *model);
QModelIndex mapToSource(const QModelIndex &idx) const;
QModelIndex mapFromSource(const QModelIndex &idx) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int currentPage() const;
int pageCount() const;
QHash<int, QByteArray> roleNames() const override;
void setStaticRowCount(bool src);
bool hasStaticRowCount() const;
/** Display the first rows of the model */
Q_SCRIPTABLE void firstPage();
/** Display the rows right after the ones that are currently being served */
Q_SCRIPTABLE void nextPage();
/** Display the rows right before the ones that are currently being served */
Q_SCRIPTABLE void previousPage();
/** Display the last set of rows of the source model */
Q_SCRIPTABLE void lastPage();
private Q_SLOTS:
void _k_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
void _k_sourceRowsInserted(const QModelIndex &parent, int start, int end);
void _k_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
void _k_sourceRowsRemoved(const QModelIndex &parent, int start, int end);
void _k_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
void _k_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
void _k_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
void _k_sourceColumnsInserted(const QModelIndex &parent, int start, int end);
void _k_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
void _k_sourceColumnsRemoved(const QModelIndex &parent, int start, int end);
void _k_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
void _k_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest);
void _k_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
void _k_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last);
void _k_sourceModelAboutToBeReset();
void _k_sourceModelReset();
Q_SIGNALS:
void pageSizeChanged();
void firstItemChanged();
void sourceModelChanged();
void pageCountChanged();
void staticRowCountChanged();
private:
bool canSizeChange() const;
bool isIntervalValid(const QModelIndex &parent, int start, int end) const;
int rowsByPageSize(int size) const;
class PaginateModelPrivate;
QScopedPointer<PaginateModelPrivate> d;
};
#endif
......@@ -27,7 +27,7 @@ Item {
readonly property real columns: Math.round(Util.applyMinMaxRange(3, 6, width / intendedColumnWidth))
readonly property real columnWidth: Math.floor(width / columns)
readonly property real minimizedColumns: Math.round(Util.applyMinMaxRange(5, 8, width / intendedMinimizedColumnWidth))
readonly property int minimizedColumns: Math.round(Util.applyMinMaxRange(5, 8, width / intendedMinimizedColumnWidth))
readonly property real minimizedColumnWidth: Math.floor(width / minimizedColumns)
readonly property real rowHeight: columnWidth * 0.7
......@@ -40,11 +40,31 @@ Item {
property real minimizedViewProgress: 0
property real fullViewProgress: 1
readonly property var quickSettingsModel: MobileShell.QuickSettingsModel {}
readonly property int settingsPerPage: Math.max(6, Math.floor(width/columnWidth) * 2)
readonly property real quickSettingsHeight: 2 * rowHeight
readonly property MobileShell.QuickSettingsModel quickSettingsModel: MobileShell.QuickSettingsModel {}
function resetSwipeView() {
swipeView.currentIndex = 0;
}
// return to the first page when the action drawer is closed
Connections {
target: actionDrawer
onOpenedChanged: {
if(!actionDrawer.opened) {
resetSwipeView();
}
}
}
// view when fully open
ColumnLayout {
id: fullView
height: 1
opacity: root.fullViewProgress
visible: opacity !== 0
transform: Translate { y: (1 - fullView.opacity) * root.rowHeight }
......@@ -53,39 +73,77 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
// TODO add pages
Flow {
id: flow
spacing: 0
SwipeView {
id: swipeView
Layout.fillWidth: true
Layout.preferredHeight: quickSettingsHeight
Repeater {
model: root.quickSettingsModel
delegate: Components.BaseItem {
required property var modelData
model: Math.ceil(quickSettingsModel.count / settingsPerPage)
delegate: Flow {
id: flow