diff --git a/libtaskmanager/abstracttasksmodel.h b/libtaskmanager/abstracttasksmodel.h --- a/libtaskmanager/abstracttasksmodel.h +++ b/libtaskmanager/abstracttasksmodel.h @@ -59,6 +59,7 @@ IsStartup, /**< This is a startup task. */ IsLauncher, /**< This is a launcher task. */ HasLauncher, /**< A launcher exists for this task. Only implemented by TasksModel, not by either the single-type or munging tasks models. */ + IsPinned, /**< Pinned as in: only show the icon, even when running. */ IsGroupParent, /**< This is a parent item for a group of child tasks. */ ChildCount, /**< The number of tasks in this group. */ IsGroupable, /**< Whether this task is being ignored by grouping or not. */ diff --git a/libtaskmanager/taskgroupingproxymodel.cpp b/libtaskmanager/taskgroupingproxymodel.cpp --- a/libtaskmanager/taskgroupingproxymodel.cpp +++ b/libtaskmanager/taskgroupingproxymodel.cpp @@ -693,6 +693,8 @@ return d->any(proxyIndex, AbstractTasksModel::IsActive); } else if (role == AbstractTasksModel::IsClosable) { return d->all(proxyIndex, AbstractTasksModel::IsClosable); + } else if (role == AbstractTasksModel::IsPinned) { + return d->all(proxyIndex, AbstractTasksModel::IsPinned); } else if (role == AbstractTasksModel::IsMovable) { // Moving groups makes no sense. return false; diff --git a/libtaskmanager/tasksmodel.h b/libtaskmanager/tasksmodel.h --- a/libtaskmanager/tasksmodel.h +++ b/libtaskmanager/tasksmodel.h @@ -88,6 +88,10 @@ Q_PROPERTY(QStringList groupingLauncherUrlBlacklist READ groupingLauncherUrlBlacklist WRITE setGroupingLauncherUrlBlacklist NOTIFY groupingLauncherUrlBlacklistChanged) Q_PROPERTY(QModelIndex activeTask READ activeTask NOTIFY activeTaskChanged) + Q_PROPERTY(QStringList pinAppIdList READ pinAppIdList + WRITE setPinAppIdList NOTIFY pinAppIdListChanged) + Q_PROPERTY(QStringList pinLauncherUrlList READ pinLauncherUrlList + WRITE setPinLauncherUrlList NOTIFY pinLauncherUrlListChanged) public: enum SortMode { @@ -514,6 +518,42 @@ **/ void setGroupingLauncherUrlBlacklist(const QStringList &list); + /** + * A list of app ids (AbstractTasksModel::AppId) that should be pinned + * (show icon only) + * + * The default list is empty. + * + * @returns the list of app ids consulted for pinning. + **/ + QStringList pinAppIdList() const; + + /** + * Sets the list of app ids (AbstractTasksModel::AppId) that is considered to be pinned + * (show icon only) + * + * @param list a list of app ids to be consulted for pinning. + **/ + void setPinAppIdList(const QStringList &list); + + /** + * A list of launcher URLs (AbstractTasksModel::LauncherUrl) that should be pinned + * (show icon only) + * + * The default list is empty. + * + * @returns the list of launcher URLs consulted for pinning. + **/ + QStringList pinLauncherUrlList() const; + + /** + * Sets the list of launcher URLs (AbstractTasksModel::LauncherUrl) that is considered + * to be pinned (show icon only) + * + * @param list a list of launcher URLs to be consulted for pinning. + **/ + void setPinLauncherUrlList(const QStringList &list); + /** * Finds the first active (AbstractTasksModel::IsActive) task in the model * and returns its QModelIndex, or a null QModelIndex if no active task is @@ -523,6 +563,11 @@ */ QModelIndex activeTask() const; + /** + * Checks whether a window is pinned + */ + bool isPinned(const QModelIndex &sourceIndex) const; + /** * Request adding a launcher with the given URL. * @@ -770,6 +815,20 @@ **/ Q_INVOKABLE void requestToggleGrouping(const QModelIndex &index); + /** + * Request toggling whether the task at the given index, along with any + * tasks matching its kind, should be pinned or not. Pinned in this sense + * means that only the application icon is shown, even in non-icon-only + * task manager. This is the same as tab pinning in web browsers. + * + * As grouping is toggled for a task, updates are made to the + * pin*List properties of the model instance. + * + * + * @param index An index in this tasks model. + **/ + Q_INVOKABLE void requestTogglePin(const QModelIndex &index); + /** * Moves a task to a new position in the list. The insert position is * is bounded to the list start and end. @@ -846,6 +905,8 @@ void groupingAppIdBlacklistChanged() const; void groupingLauncherUrlBlacklistChanged() const; void activeTaskChanged() const; + void pinAppIdListChanged() const; + void pinLauncherUrlListChanged() const; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; diff --git a/libtaskmanager/tasksmodel.cpp b/libtaskmanager/tasksmodel.cpp --- a/libtaskmanager/tasksmodel.cpp +++ b/libtaskmanager/tasksmodel.cpp @@ -58,6 +58,9 @@ FlattenTaskGroupsProxyModel* flattenGroupsProxyModel = nullptr; AbstractTasksModelIface *abstractTasksSourceModel = nullptr; + QSet pinAppIdList; + QSet pinLauncherUrlList; + bool anyTaskDemandsAttention = false; int launcherCount = 0; @@ -970,6 +973,10 @@ return winIds; } + else if (role == AbstractTasksModel::IsPinned + && proxyIndex.isValid() && proxyIndex.row() < rowCount()) { + return isPinned(proxyIndex); + } return QSortFilterProxyModel::data(proxyIndex, role); } @@ -1237,6 +1244,36 @@ } } +QStringList TasksModel::pinAppIdList() const +{ + return d->pinAppIdList.toList(); +} + +void TasksModel::setPinAppIdList(const QStringList &list) +{ + const QSet &set = QSet::fromList(list); + + if (d->pinAppIdList != set) { + d->pinAppIdList = set; + emit pinAppIdListChanged(); + } +} + +QStringList TasksModel::pinLauncherUrlList() const +{ + return d->pinLauncherUrlList.toList(); +} + +void TasksModel::setPinLauncherUrlList(const QStringList &list) +{ + const QSet &set = QSet::fromList(list); + + if (d->pinLauncherUrlList != set) { + d->pinLauncherUrlList = set; + emit pinLauncherUrlListChanged(); + } +} + QStringList TasksModel::launcherList() const { if (d->launcherTasksModel) { @@ -1457,6 +1494,51 @@ } } +void TasksModel::requestTogglePin(const QModelIndex &index) +{ + if (index.isValid() && index.model() == this) { + const QModelIndex &target = mapToSource(index); + const QString &appId = target.data(AbstractTasksModel::AppId).toString(); + const QUrl &launcherUrl = target.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); + const QString &launcherUrlString = launcherUrl.toString(QUrl::RemoveQuery | QUrl::RemoveQuery); + + if (d->pinAppIdList.contains(appId) || d->pinLauncherUrlList.contains(launcherUrlString)) { + d->pinAppIdList.remove(appId); + d->pinLauncherUrlList.remove(launcherUrlString); + } else { + d->pinAppIdList.insert(appId); + d->pinLauncherUrlList.insert(launcherUrlString); + } + + //TODO: Update IsPinned data role for all items + dataChanged(index, index, QVector{AbstractTasksModel::IsPinned}); + + emit pinAppIdListChanged(); + emit pinLauncherUrlListChanged(); + } +} + +bool TasksModel::isPinned(const QModelIndex &sourceIndex) const +{ + // Check app id against pin list. + if (d->pinAppIdList.count() + && d->pinAppIdList.contains(sourceIndex.data(AbstractTasksModel::AppId).toString())) { + return true; + } + + // Check launcher URL (sans query items) against pin list. + if (d->pinLauncherUrlList.count()) { + const QUrl &launcherUrl = sourceIndex.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl(); + const QString &launcherUrlString = launcherUrl.toString(QUrl::PrettyDecoded | QUrl::RemoveQuery); + + if (d->pinLauncherUrlList.contains(launcherUrlString)) { + return true; + } + } + + return false; +} + bool TasksModel::move(int row, int newPos, const QModelIndex &parent) { if (d->sortMode != SortManual || row == newPos || newPos < 0 || newPos >= rowCount(parent)) {