Add <number> to Wayland captions if the caption is the same
Summary: Bringing another caption feature from X11 to Wayland. If we have multiple windows with the same caption, starting from the second window a suffix <number> is added. E.g. if we have three windows with caption "foo", the naming is: * foo * foo <2> * foo <3> The change tries to use as much shared code between the X11 and Wayland implementation. Unfortunately it's not possible to share completely as the X11 implementation does X11 specific things like editing the visible name. By sharing the code the numbering also works cross windowing system. That is if a window is called "foo" on X11, a new window on Wayland with caption "foo" will get adjusted to "foo <2>" and vice versa. The change also eliminates a duplicated signal for captionChanged in ShellClient (found by test case). By using the shared implementation on X11 side a bug gets fixed which got introduced with the support of "unresponsive", this is no longer considered and the numbering still works even if there is a window which is unresponsive. Test Plan: New test case and manual testing Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D7425
This commit is contained in:
parent
833f933c5c
commit
6685288d48
7 changed files with 111 additions and 9 deletions
|
@ -1758,4 +1758,12 @@ QString AbstractClient::shortcutCaptionSuffix() const
|
||||||
return QLatin1String(" {") + shortcut().toString() + QLatin1Char('}');
|
return QLatin1String(" {") + shortcut().toString() + QLatin1Char('}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbstractClient *AbstractClient::findClientWithSameCaption() const
|
||||||
|
{
|
||||||
|
auto fetchNameInternalPredicate = [this](const AbstractClient *cl) {
|
||||||
|
return (!cl->isSpecialWindow() || cl->isToolbar()) && cl != this && cl->captionNormal() == captionNormal() && cl->captionSuffix() == captionSuffix();
|
||||||
|
};
|
||||||
|
return workspace()->findAbstractClient(fetchNameInternalPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -343,7 +343,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void updateMouseGrab();
|
virtual void updateMouseGrab();
|
||||||
|
/**
|
||||||
|
* @returns The caption consisting of @link{captionNormal} and @link{captionSuffix}
|
||||||
|
* @see captionNormal
|
||||||
|
* @see captionSuffix
|
||||||
|
**/
|
||||||
virtual QString caption(bool full = true) const = 0;
|
virtual QString caption(bool full = true) const = 0;
|
||||||
|
/**
|
||||||
|
* @returns The caption as set by the AbstractClient without any suffix.
|
||||||
|
* @see caption
|
||||||
|
* @see captionSuffix
|
||||||
|
**/
|
||||||
|
virtual QString captionNormal() const = 0;
|
||||||
|
/**
|
||||||
|
* @returns The suffix added to the caption (e.g. shortcut, machine name, etc.)
|
||||||
|
* @see caption
|
||||||
|
* @see captionNormal
|
||||||
|
**/
|
||||||
|
virtual QString captionSuffix() const = 0;
|
||||||
virtual bool isCloseable() const = 0;
|
virtual bool isCloseable() const = 0;
|
||||||
// TODO: remove boolean trap
|
// TODO: remove boolean trap
|
||||||
virtual bool isShown(bool shaded_is_shown) const = 0;
|
virtual bool isShown(bool shaded_is_shown) const = 0;
|
||||||
|
@ -986,6 +1003,12 @@ protected:
|
||||||
QString shortcutCaptionSuffix() const;
|
QString shortcutCaptionSuffix() const;
|
||||||
virtual void updateCaption() = 0;
|
virtual void updateCaption() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for another AbstractClient with same @link{captionNormal} and @link{captionSuffix}.
|
||||||
|
* If no such AbstractClient exists @c nullptr is returned.
|
||||||
|
**/
|
||||||
|
AbstractClient *findClientWithSameCaption() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handlePaletteChange();
|
void handlePaletteChange();
|
||||||
QSharedPointer<TabBox::TabBoxClientImpl> m_tabBoxClient;
|
QSharedPointer<TabBox::TabBoxClientImpl> m_tabBoxClient;
|
||||||
|
|
|
@ -74,6 +74,7 @@ private Q_SLOTS:
|
||||||
void testHidden();
|
void testHidden();
|
||||||
void testDesktopFileName();
|
void testDesktopFileName();
|
||||||
void testCaptionSimplified();
|
void testCaptionSimplified();
|
||||||
|
void testCaptionMultipleWindows();
|
||||||
void testKillWindow_data();
|
void testKillWindow_data();
|
||||||
void testKillWindow();
|
void testKillWindow();
|
||||||
void testX11WindowId_data();
|
void testX11WindowId_data();
|
||||||
|
@ -693,6 +694,53 @@ void TestShellClient::testCaptionSimplified()
|
||||||
QCOMPARE(c->caption(), origTitle.simplified());
|
QCOMPARE(c->caption(), origTitle.simplified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestShellClient::testCaptionMultipleWindows()
|
||||||
|
{
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(qobject_cast<XdgShellSurface*>(Test::createShellSurface(Test::ShellSurfaceType::XdgShellV5, surface.data())));
|
||||||
|
shellSurface->setTitle(QStringLiteral("foo"));
|
||||||
|
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(c);
|
||||||
|
QCOMPARE(c->caption(), QStringLiteral("foo"));
|
||||||
|
QCOMPARE(c->captionNormal(), QStringLiteral("foo"));
|
||||||
|
QCOMPARE(c->captionSuffix(), QString());
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface2(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface2(qobject_cast<XdgShellSurface*>(Test::createShellSurface(Test::ShellSurfaceType::XdgShellV5, surface2.data())));
|
||||||
|
shellSurface2->setTitle(QStringLiteral("foo"));
|
||||||
|
auto c2 = Test::renderAndWaitForShown(surface2.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(c2);
|
||||||
|
QCOMPARE(c2->caption(), QStringLiteral("foo <2>"));
|
||||||
|
QCOMPARE(c2->captionNormal(), QStringLiteral("foo"));
|
||||||
|
QCOMPARE(c2->captionSuffix(), QStringLiteral(" <2>"));
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface3(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface3(qobject_cast<XdgShellSurface*>(Test::createShellSurface(Test::ShellSurfaceType::XdgShellV5, surface3.data())));
|
||||||
|
shellSurface3->setTitle(QStringLiteral("foo"));
|
||||||
|
auto c3 = Test::renderAndWaitForShown(surface3.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(c3);
|
||||||
|
QCOMPARE(c3->caption(), QStringLiteral("foo <3>"));
|
||||||
|
QCOMPARE(c3->captionNormal(), QStringLiteral("foo"));
|
||||||
|
QCOMPARE(c3->captionSuffix(), QStringLiteral(" <3>"));
|
||||||
|
|
||||||
|
QScopedPointer<Surface> surface4(Test::createSurface());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface4(qobject_cast<XdgShellSurface*>(Test::createShellSurface(Test::ShellSurfaceType::XdgShellV5, surface4.data())));
|
||||||
|
shellSurface4->setTitle(QStringLiteral("bar"));
|
||||||
|
auto c4 = Test::renderAndWaitForShown(surface4.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(c4);
|
||||||
|
QCOMPARE(c4->caption(), QStringLiteral("bar"));
|
||||||
|
QCOMPARE(c4->captionNormal(), QStringLiteral("bar"));
|
||||||
|
QCOMPARE(c4->captionSuffix(), QString());
|
||||||
|
QSignalSpy captionChangedSpy(c4, &ShellClient::captionChanged);
|
||||||
|
QVERIFY(captionChangedSpy.isValid());
|
||||||
|
shellSurface4->setTitle(QStringLiteral("foo"));
|
||||||
|
QVERIFY(captionChangedSpy.wait());
|
||||||
|
QCOMPARE(captionChangedSpy.count(), 1);
|
||||||
|
QCOMPARE(c4->caption(), QStringLiteral("foo <4>"));
|
||||||
|
QCOMPARE(c4->captionNormal(), QStringLiteral("foo"));
|
||||||
|
QCOMPARE(c4->captionSuffix(), QStringLiteral(" <4>"));
|
||||||
|
}
|
||||||
|
|
||||||
void TestShellClient::testKillWindow_data()
|
void TestShellClient::testKillWindow_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<bool>("socketMode");
|
QTest::addColumn<bool>("socketMode");
|
||||||
|
|
|
@ -1463,16 +1463,12 @@ void Client::setCaption(const QString& _s, bool force)
|
||||||
}
|
}
|
||||||
QString shortcut_suffix = shortcutCaptionSuffix();
|
QString shortcut_suffix = shortcutCaptionSuffix();
|
||||||
cap_suffix = machine_suffix + shortcut_suffix;
|
cap_suffix = machine_suffix + shortcut_suffix;
|
||||||
auto fetchNameInternalPredicate = [this](const Client *cl) {
|
if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) {
|
||||||
return (!cl->isSpecialWindow() || cl->isToolbar()) &&
|
|
||||||
cl != this && cl->caption() == caption();
|
|
||||||
};
|
|
||||||
if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(fetchNameInternalPredicate)) {
|
|
||||||
int i = 2;
|
int i = 2;
|
||||||
do {
|
do {
|
||||||
cap_suffix = machine_suffix + QLatin1String(" <") + QString::number(i) + QLatin1Char('>') + LRM;
|
cap_suffix = machine_suffix + QLatin1String(" <") + QString::number(i) + QLatin1Char('>') + LRM;
|
||||||
i++;
|
i++;
|
||||||
} while (workspace()->findClient(fetchNameInternalPredicate));
|
} while (findClientWithSameCaption());
|
||||||
info->setVisibleName(caption().toUtf8().constData());
|
info->setVisibleName(caption().toUtf8().constData());
|
||||||
reset_name = false;
|
reset_name = false;
|
||||||
}
|
}
|
||||||
|
|
6
client.h
6
client.h
|
@ -220,6 +220,12 @@ public:
|
||||||
inline bool isBlockingCompositing() { return blocks_compositing; }
|
inline bool isBlockingCompositing() { return blocks_compositing; }
|
||||||
|
|
||||||
QString caption(bool full = true) const override;
|
QString caption(bool full = true) const override;
|
||||||
|
QString captionNormal() const override {
|
||||||
|
return cap_normal;
|
||||||
|
}
|
||||||
|
QString captionSuffix() const override {
|
||||||
|
return cap_suffix;
|
||||||
|
}
|
||||||
|
|
||||||
using AbstractClient::keyPressEvent;
|
using AbstractClient::keyPressEvent;
|
||||||
void keyPressEvent(uint key_code, xcb_timestamp_t time); // FRAME ??
|
void keyPressEvent(uint key_code, xcb_timestamp_t time); // FRAME ??
|
||||||
|
|
|
@ -104,12 +104,19 @@ template <class T>
|
||||||
void ShellClient::initSurface(T *shellSurface)
|
void ShellClient::initSurface(T *shellSurface)
|
||||||
{
|
{
|
||||||
m_caption = shellSurface->title().simplified();
|
m_caption = shellSurface->title().simplified();
|
||||||
connect(shellSurface, &T::titleChanged, this, &ShellClient::captionChanged);
|
// delay till end of init
|
||||||
|
QTimer::singleShot(0, this, &ShellClient::updateCaption);
|
||||||
connect(shellSurface, &T::destroyed, this, &ShellClient::destroyClient);
|
connect(shellSurface, &T::destroyed, this, &ShellClient::destroyClient);
|
||||||
connect(shellSurface, &T::titleChanged, this,
|
connect(shellSurface, &T::titleChanged, this,
|
||||||
[this] (const QString &s) {
|
[this] (const QString &s) {
|
||||||
|
const auto oldSuffix = m_captionSuffix;
|
||||||
m_caption = s.simplified();
|
m_caption = s.simplified();
|
||||||
emit captionChanged();
|
updateCaption();
|
||||||
|
if (m_captionSuffix == oldSuffix) {
|
||||||
|
// don't emit caption change twice
|
||||||
|
// it already got emitted by the changing suffix
|
||||||
|
emit captionChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
connect(shellSurface, &T::moveRequested, this,
|
connect(shellSurface, &T::moveRequested, this,
|
||||||
|
@ -571,7 +578,15 @@ QString ShellClient::caption(bool full) const
|
||||||
void ShellClient::updateCaption()
|
void ShellClient::updateCaption()
|
||||||
{
|
{
|
||||||
const QString oldSuffix = m_captionSuffix;
|
const QString oldSuffix = m_captionSuffix;
|
||||||
m_captionSuffix = shortcutCaptionSuffix();
|
const auto shortcut = shortcutCaptionSuffix();
|
||||||
|
m_captionSuffix = shortcut;
|
||||||
|
if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) {
|
||||||
|
int i = 2;
|
||||||
|
do {
|
||||||
|
m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>');
|
||||||
|
i++;
|
||||||
|
} while (findClientWithSameCaption());
|
||||||
|
}
|
||||||
if (m_captionSuffix != oldSuffix) {
|
if (m_captionSuffix != oldSuffix) {
|
||||||
emit captionChanged();
|
emit captionChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,12 @@ public:
|
||||||
|
|
||||||
void blockActivityUpdates(bool b = true) override;
|
void blockActivityUpdates(bool b = true) override;
|
||||||
QString caption(bool full = true) const override;
|
QString caption(bool full = true) const override;
|
||||||
|
QString captionNormal() const override {
|
||||||
|
return m_caption;
|
||||||
|
}
|
||||||
|
QString captionSuffix() const override {
|
||||||
|
return m_captionSuffix;
|
||||||
|
}
|
||||||
void closeWindow() override;
|
void closeWindow() override;
|
||||||
AbstractClient *findModal(bool allow_itself = false) override;
|
AbstractClient *findModal(bool allow_itself = false) override;
|
||||||
bool isCloseable() const override;
|
bool isCloseable() const override;
|
||||||
|
|
Loading…
Reference in a new issue