Commit c0acd143 authored by Eike Hein's avatar Eike Hein
Browse files

[libtaskmanager] Track stacking order and window activation (on X11)

Summary:
`TaskGroupingProxyModel::requestToggleMaximized` now uses this to
minimize and restore groups of windows while preserving the
stacking order, a frequently user-requested wish.
BUG:368867

Window activation is additionally tracked to implement a new front-
end feature to activate the most recently active window (or fall
through to stacking order otherwise) subsequently.
CCBUG:370258

A Wayland implementation requires the addition of a
`PlasmaWindowManagement::stackingOrder()`, which should be a QList
of PlasmaWindow* in stacking order, along with a change signal. We
discussed this at the Plasma+KWin sprint and I'll code up patches
to KWin and KWayland soon and then implement the new API in here.

Reviewers: #plasma

Subscribers: davidedmundson, pino, anthonyfieroni, ngraham, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D22053
parent 7be2bc67
......@@ -91,6 +91,11 @@ public:
itself. DO NOT use this for destructive actions such as closing
the application. The intended use case is to try and (smartly)
gather more information about the task when needed. */
StackingOrder, /**< A window task's index in the window stacking order. Care must be
taken not to assume this index to be unique when iterating over
model contents due to the asynchronous nature of the windowing
system. */
LastActivated, /**< The timestamp of the last time a task was the active task. */
};
Q_ENUM(AdditionalRoles)
......
......@@ -1011,13 +1011,26 @@ void TaskGroupingProxyModel::requestToggleMaximized(const QModelIndex &index)
} else {
const bool goalState = !index.data(AbstractTasksModel::IsMaximized).toBool();
for (int i = 0; i < rowCount(index); ++i) {
const QModelIndex &child = index.child(i, 0);
QModelIndexList inStackingOrder;
if (child.data(AbstractTasksModel::IsMaximized).toBool() != goalState) {
d->abstractTasksSourceModel->requestToggleMaximized(mapToSource(child));
}
}
for (int i = 0; i < rowCount(index); ++i) {
const QModelIndex &child = index.child(i, 0);
if (child.data(AbstractTasksModel::IsMaximized).toBool() != goalState) {
inStackingOrder << mapToSource(child);
}
}
std::sort(inStackingOrder.begin(), inStackingOrder.end(),
[](const QModelIndex &a, const QModelIndex &b) {
return (a.data(AbstractTasksModel::StackingOrder).toInt()
< b.data(AbstractTasksModel::StackingOrder).toInt());
}
);
for (const QModelIndex &sourceChild : inStackingOrder) {
d->abstractTasksSourceModel->requestToggleMaximized(sourceChild);
}
}
}
......
......@@ -65,6 +65,8 @@ public:
QHash<WId, AppData> appDataCache;
QHash<WId, QRect> delegateGeometries;
QSet<WId> usingFallbackIcon;
QHash<WId, QTime> lastActivated;
QList<WId> cachedStackingOrder;
WId activeWindow = -1;
KSharedConfig::Ptr rulesConfig;
KDirWatch *configWatcher = nullptr;
......@@ -120,6 +122,8 @@ void XWindowTasksModel::Private::init()
AbstractTasksModel::SkipTaskbar});
};
cachedStackingOrder = KWindowSystem::stackingOrder();
sycocaChangeTimer.setSingleShot(true);
sycocaChangeTimer.setInterval(100);
......@@ -177,6 +181,7 @@ void XWindowTasksModel::Private::init()
[this](WId window) {
const WId oldActiveWindow = activeWindow;
activeWindow = window;
lastActivated[activeWindow] = QTime::currentTime();
int row = windows.indexOf(oldActiveWindow);
......@@ -192,6 +197,14 @@ void XWindowTasksModel::Private::init()
}
);
QObject::connect(KWindowSystem::self(), &KWindowSystem::stackingOrderChanged, q,
[this]() {
cachedStackingOrder = KWindowSystem::stackingOrder();
q->dataChanged(q->index(0, 0), q->index(q->rowCount() - 1, 0),
QVector<int>{StackingOrder});
}
);
activeWindow = KWindowSystem::activeWindow();
// Add existing windows.
......@@ -255,8 +268,9 @@ void XWindowTasksModel::Private::removeWindow(WId window)
transientsDemandingAttention.remove(window);
delete windowInfoCache.take(window);
appDataCache.remove(window);
usingFallbackIcon.remove(window);
delegateGeometries.remove(window);
usingFallbackIcon.remove(window);
lastActivated.remove(window);
q->endRemoveRows();
} else { // Could be a transient.
// Removing a transient might change the demands attention state of the leader.
......@@ -653,6 +667,12 @@ QVariant XWindowTasksModel::data(const QModelIndex &index, int role) const
return d->windowInfo(window)->hasState(NET::SkipPager);
} else if (role == AppPid) {
return d->windowInfo(window)->pid();
} else if (role == StackingOrder) {
return d->cachedStackingOrder.indexOf(window);
} else if (role == LastActivated) {
if (d->lastActivated.contains(window)) {
return d->lastActivated.value(window);
}
}
return QVariant();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment