Honor struts when placing Wayland transients
Summary: So far transients were placed anywhere on the screen. This behavior was inspired from X11 where context menus were able to overlap any other window and use the complete screen area. On X11 context menus and similar windows are override redirect and thus above all windows managed by KWin. On Wayland, though, context menus and similar and windows just like any other window and thus follow stacking constraints like the parent window. A context menu is stacked just above it's parent and is (normally) below any panels. This resulted in problems that context menu are stacked behind the panel with unreachable options. This change changes the placement for transients to use the PlacementArea instead of a screen geometry. Thus the transient does not render behind the panel. Only in case of a fullscreen the struts are ignored. BUG: 389222 FIXED-IN: 5.15 Test Plan: New test case Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D17826
This commit is contained in:
parent
0b28abeb01
commit
792d840455
2 changed files with 87 additions and 2 deletions
|
@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <KWayland/Client/event_queue.h>
|
||||
#include <KWayland/Client/keyboard.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <KWayland/Client/plasmashell.h>
|
||||
#include <KWayland/Client/pointer.h>
|
||||
#include <KWayland/Client/shell.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
|
@ -63,6 +64,7 @@ private Q_SLOTS:
|
|||
void testDecorationPosition();
|
||||
void testXdgPopup_data();
|
||||
void testXdgPopup();
|
||||
void testXdgPopupWithPanel();
|
||||
|
||||
private:
|
||||
AbstractClient *showWlShellWindow(const QSize &size, bool decorated = false, KWayland::Client::Surface *parent = nullptr, const QPoint &offset = QPoint());
|
||||
|
@ -91,7 +93,7 @@ void TransientPlacementTest::initTestCase()
|
|||
|
||||
void TransientPlacementTest::init()
|
||||
{
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration));
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | Test::AdditionalWaylandInterface::PlasmaShell));
|
||||
|
||||
screens()->setCurrent(0);
|
||||
Cursor::setPos(QPoint(640, 512));
|
||||
|
@ -378,6 +380,88 @@ void TransientPlacementTest::testXdgPopup()
|
|||
QTEST(transient->geometry(), "expectedGeometry");
|
||||
}
|
||||
|
||||
void TransientPlacementTest::testXdgPopupWithPanel()
|
||||
{
|
||||
using namespace KWayland::Client;
|
||||
|
||||
QScopedPointer<Surface> surface{Test::createSurface()};
|
||||
QVERIFY(!surface.isNull());
|
||||
QScopedPointer<XdgShellSurface> dockShellSurface{Test::createXdgShellStableSurface(surface.data(), surface.data())};
|
||||
QVERIFY(!dockShellSurface.isNull());
|
||||
QScopedPointer<PlasmaShellSurface> plasmaSurface(Test::waylandPlasmaShell()->createSurface(surface.data()));
|
||||
QVERIFY(!plasmaSurface.isNull());
|
||||
plasmaSurface->setRole(PlasmaShellSurface::Role::Panel);
|
||||
plasmaSurface->setPosition(QPoint(0, screens()->geometry(0).height() - 50));
|
||||
plasmaSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible);
|
||||
|
||||
// now render and map the window
|
||||
QVERIFY(workspace()->clientArea(PlacementArea, 0, 1) == workspace()->clientArea(FullScreenArea, 0, 1));
|
||||
auto dock = Test::renderAndWaitForShown(surface.data(), QSize(1280, 50), Qt::blue);
|
||||
QVERIFY(dock);
|
||||
QCOMPARE(dock->windowType(), NET::Dock);
|
||||
QVERIFY(dock->isDock());
|
||||
QCOMPARE(dock->geometry(), QRect(0, screens()->geometry(0).height() - 50, 1280, 50));
|
||||
QCOMPARE(dock->hasStrut(), true);
|
||||
QVERIFY(workspace()->clientArea(PlacementArea, 0, 1) != workspace()->clientArea(FullScreenArea, 0, 1));
|
||||
|
||||
//create parent
|
||||
Surface *parentSurface = Test::createSurface(Test::waylandCompositor());
|
||||
QVERIFY(parentSurface);
|
||||
auto parentShellSurface = Test::createXdgShellStableSurface(parentSurface, Test::waylandCompositor());
|
||||
QVERIFY(parentShellSurface);
|
||||
auto parent = Test::renderAndWaitForShown(parentSurface, {800, 600}, Qt::blue);
|
||||
QVERIFY(parent);
|
||||
|
||||
QVERIFY(!parent->isDecorated());
|
||||
parent->move({0, screens()->geometry(0).height() - 600});
|
||||
parent->keepInArea(workspace()->clientArea(PlacementArea, parent));
|
||||
QCOMPARE(parent->geometry(), QRect(0, screens()->geometry(0).height() - 600 - 50, 800, 600));
|
||||
|
||||
Surface *transientSurface = Test::createSurface(Test::waylandCompositor());
|
||||
QVERIFY(transientSurface);
|
||||
XdgPositioner positioner(QSize(200,200), QRect(50,500, 200,200));
|
||||
|
||||
auto transientShellSurface = Test::createXdgShellStablePopup(transientSurface, parentShellSurface, positioner, Test::waylandCompositor());
|
||||
auto transient = Test::renderAndWaitForShown(transientSurface, positioner.initialSize(), Qt::red);
|
||||
QVERIFY(transient);
|
||||
|
||||
QVERIFY(!transient->isDecorated());
|
||||
QVERIFY(transient->hasTransientPlacementHint());
|
||||
|
||||
QCOMPARE(transient->geometry(), QRect(50, screens()->geometry(0).height() - 200 - 50, 200, 200));
|
||||
|
||||
transientShellSurface->deleteLater();
|
||||
transientSurface->deleteLater();
|
||||
QVERIFY(Test::waitForWindowDestroyed(transient));
|
||||
|
||||
// now parent to fullscreen - on fullscreen the panel is ignored
|
||||
QSignalSpy fullscreenSpy{parentShellSurface, &XdgShellSurface::configureRequested};
|
||||
QVERIFY(fullscreenSpy.isValid());
|
||||
parent->setFullScreen(true);
|
||||
QVERIFY(fullscreenSpy.wait());
|
||||
parentShellSurface->ackConfigure(fullscreenSpy.first().at(2).value<quint32>());
|
||||
QSignalSpy geometryShapeChangedSpy{parent, &ShellClient::geometryShapeChanged};
|
||||
QVERIFY(geometryShapeChangedSpy.isValid());
|
||||
Test::render(parentSurface, fullscreenSpy.first().at(0).toSize(), Qt::red);
|
||||
QVERIFY(geometryShapeChangedSpy.wait());
|
||||
QCOMPARE(parent->geometry(), screens()->geometry(0));
|
||||
QVERIFY(parent->isFullScreen());
|
||||
|
||||
// another transient, with same hints as before from bottom of window
|
||||
transientSurface = Test::createSurface(Test::waylandCompositor());
|
||||
QVERIFY(transientSurface);
|
||||
|
||||
XdgPositioner positioner2(QSize(200,200), QRect(50,screens()->geometry(0).height()-100, 200,200));
|
||||
transientShellSurface = Test::createXdgShellStablePopup(transientSurface, parentShellSurface, positioner2, Test::waylandCompositor());
|
||||
transient = Test::renderAndWaitForShown(transientSurface, positioner2.initialSize(), Qt::red);
|
||||
QVERIFY(transient);
|
||||
|
||||
QVERIFY(!transient->isDecorated());
|
||||
QVERIFY(transient->hasTransientPlacementHint());
|
||||
|
||||
QCOMPARE(transient->geometry(), QRect(50, screens()->geometry(0).height() - 200, 200, 200));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(KWin::TransientPlacementTest)
|
||||
|
|
|
@ -498,7 +498,8 @@ void Placement::placeOnScreenDisplay(AbstractClient* c, QRect& area)
|
|||
|
||||
void Placement::placeTransient(AbstractClient *c)
|
||||
{
|
||||
const QRect screen = screens()->geometry(c->transientFor()->screen());
|
||||
const auto parent = c->transientFor();
|
||||
const QRect screen = Workspace::self()->clientArea(parent->isFullScreen() ? FullScreenArea : PlacementArea, parent);
|
||||
const QPoint popupPos = c->transientPlacement(screen).topLeft();
|
||||
c->move(popupPos);
|
||||
|
||||
|
|
Loading…
Reference in a new issue