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
icc-effect-5.14.5
Martin Flöser 2017-08-20 08:56:13 +02:00
parent 833f933c5c
commit 6685288d48
7 changed files with 111 additions and 9 deletions

View File

@ -1758,4 +1758,12 @@ QString AbstractClient::shortcutCaptionSuffix() const
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);
}
}

View File

@ -343,7 +343,24 @@ public:
}
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;
/**
* @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;
// TODO: remove boolean trap
virtual bool isShown(bool shaded_is_shown) const = 0;
@ -986,6 +1003,12 @@ protected:
QString shortcutCaptionSuffix() const;
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:
void handlePaletteChange();
QSharedPointer<TabBox::TabBoxClientImpl> m_tabBoxClient;

View File

@ -74,6 +74,7 @@ private Q_SLOTS:
void testHidden();
void testDesktopFileName();
void testCaptionSimplified();
void testCaptionMultipleWindows();
void testKillWindow_data();
void testKillWindow();
void testX11WindowId_data();
@ -693,6 +694,53 @@ void TestShellClient::testCaptionSimplified()
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()
{
QTest::addColumn<bool>("socketMode");

View File

@ -1463,16 +1463,12 @@ void Client::setCaption(const QString& _s, bool force)
}
QString shortcut_suffix = shortcutCaptionSuffix();
cap_suffix = machine_suffix + shortcut_suffix;
auto fetchNameInternalPredicate = [this](const Client *cl) {
return (!cl->isSpecialWindow() || cl->isToolbar()) &&
cl != this && cl->caption() == caption();
};
if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(fetchNameInternalPredicate)) {
if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) {
int i = 2;
do {
cap_suffix = machine_suffix + QLatin1String(" <") + QString::number(i) + QLatin1Char('>') + LRM;
i++;
} while (workspace()->findClient(fetchNameInternalPredicate));
} while (findClientWithSameCaption());
info->setVisibleName(caption().toUtf8().constData());
reset_name = false;
}

View File

@ -220,6 +220,12 @@ public:
inline bool isBlockingCompositing() { return blocks_compositing; }
QString caption(bool full = true) const override;
QString captionNormal() const override {
return cap_normal;
}
QString captionSuffix() const override {
return cap_suffix;
}
using AbstractClient::keyPressEvent;
void keyPressEvent(uint key_code, xcb_timestamp_t time); // FRAME ??

View File

@ -104,12 +104,19 @@ template <class T>
void ShellClient::initSurface(T *shellSurface)
{
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::titleChanged, this,
[this] (const QString &s) {
const auto oldSuffix = m_captionSuffix;
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,
@ -571,7 +578,15 @@ QString ShellClient::caption(bool full) const
void ShellClient::updateCaption()
{
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) {
emit captionChanged();
}

View File

@ -64,6 +64,12 @@ public:
void blockActivityUpdates(bool b = true) 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;
AbstractClient *findModal(bool allow_itself = false) override;
bool isCloseable() const override;