diff --git a/src/wayland/xdgshell_interface.cpp b/src/wayland/xdgshell_interface.cpp index 071a8de98e..8b189681ce 100644 --- a/src/wayland/xdgshell_interface.cpp +++ b/src/wayland/xdgshell_interface.cpp @@ -11,6 +11,7 @@ #include "output_interface.h" #include "seat_interface.h" #include "utils.h" +#include "utils/common.h" #include @@ -1075,6 +1076,121 @@ quint32 XdgPositioner::parentConfigure() const return d->parentConfigure; } +QRectF XdgPositioner::placement(const QRectF &bounds) const +{ + // returns if a target is within the supplied bounds, optional edges argument states which side to check + auto inBounds = [bounds](const QRectF &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool { + if (edges & Qt::LeftEdge && target.left() < bounds.left()) { + return false; + } + if (edges & Qt::TopEdge && target.top() < bounds.top()) { + return false; + } + if (edges & Qt::RightEdge && target.right() > bounds.right()) { + // normal QRect::right issue cancels out + return false; + } + if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) { + return false; + } + return true; + }; + + QRectF popupRect(KWin::popupOffset(anchorRect(), anchorEdges(), gravityEdges(), size()) + offset(), size()); + + // if that fits, we don't need to do anything + if (inBounds(popupRect)) { + return popupRect; + } + // otherwise apply constraint adjustment per axis in order XDG Shell Popup states + + if (flipConstraintAdjustments() & Qt::Horizontal) { + if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) { + // flip both edges (if either bit is set, XOR both) + auto flippedAnchorEdge = anchorEdges(); + if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) { + flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge); + } + auto flippedGravity = gravityEdges(); + if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) { + flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge); + } + auto flippedPopupRect = QRectF(KWin::popupOffset(anchorRect(), flippedAnchorEdge, flippedGravity, size()) + offset(), size()); + + // if it still doesn't fit we should continue with the unflipped version + if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) { + popupRect.moveLeft(flippedPopupRect.left()); + } + } + } + if (slideConstraintAdjustments() & Qt::Horizontal) { + if (!inBounds(popupRect, Qt::LeftEdge)) { + popupRect.moveLeft(bounds.left()); + } + if (!inBounds(popupRect, Qt::RightEdge)) { + popupRect.moveRight(bounds.right()); + } + } + if (resizeConstraintAdjustments() & Qt::Horizontal) { + QRectF unconstrainedRect = popupRect; + + if (!inBounds(unconstrainedRect, Qt::LeftEdge)) { + unconstrainedRect.setLeft(bounds.left()); + } + if (!inBounds(unconstrainedRect, Qt::RightEdge)) { + unconstrainedRect.setRight(bounds.right()); + } + + if (unconstrainedRect.isValid()) { + popupRect = unconstrainedRect; + } + } + + if (flipConstraintAdjustments() & Qt::Vertical) { + if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) { + // flip both edges (if either bit is set, XOR both) + auto flippedAnchorEdge = anchorEdges(); + if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) { + flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge); + } + auto flippedGravity = gravityEdges(); + if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) { + flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge); + } + auto flippedPopupRect = QRectF(KWin::popupOffset(anchorRect(), flippedAnchorEdge, flippedGravity, size()) + offset(), size()); + + // if it still doesn't fit we should continue with the unflipped version + if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) { + popupRect.moveTop(flippedPopupRect.top()); + } + } + } + if (slideConstraintAdjustments() & Qt::Vertical) { + if (!inBounds(popupRect, Qt::TopEdge)) { + popupRect.moveTop(bounds.top()); + } + if (!inBounds(popupRect, Qt::BottomEdge)) { + popupRect.moveBottom(bounds.bottom()); + } + } + if (resizeConstraintAdjustments() & Qt::Vertical) { + QRectF unconstrainedRect = popupRect; + + if (!inBounds(unconstrainedRect, Qt::TopEdge)) { + unconstrainedRect.setTop(bounds.top()); + } + if (!inBounds(unconstrainedRect, Qt::BottomEdge)) { + unconstrainedRect.setBottom(bounds.bottom()); + } + + if (unconstrainedRect.isValid()) { + popupRect = unconstrainedRect; + } + } + + return popupRect; +} + XdgPositioner XdgPositioner::get(::wl_resource *resource) { XdgPositionerPrivate *xdgPositionerPrivate = XdgPositionerPrivate::get(resource); diff --git a/src/wayland/xdgshell_interface.h b/src/wayland/xdgshell_interface.h index 5e18c0e468..1e0007c6c7 100644 --- a/src/wayland/xdgshell_interface.h +++ b/src/wayland/xdgshell_interface.h @@ -505,6 +505,12 @@ public: */ quint32 parentConfigure() const; + /** + * Returns the unconstrained geometry of the popup. The \a bounds is in the parent local + * coordinate space. + */ + QRectF placement(const QRectF &bounds) const; + /** * Returns the current state of the xdg positioner object identified by \a resource. */ diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp index ce672a6c6e..c243f271ef 100644 --- a/src/xdgshellwindow.cpp +++ b/src/xdgshellwindow.cpp @@ -1611,7 +1611,14 @@ void XdgPopupWindow::handleRepositionRequested(quint32 token) void XdgPopupWindow::updateRelativePlacement() { const QPointF parentPosition = transientFor()->framePosToClientPos(transientFor()->pos()); - m_relativePlacement = placement().translated(-parentPosition); + const QRectF bounds = workspace()->clientArea(transientFor()->isFullScreen() ? FullScreenArea : PlacementArea, transientFor()).translated(-parentPosition); + const XdgPositioner positioner = m_shellSurface->positioner(); + + if (m_plasmaShellSurface && m_plasmaShellSurface->isPositionSet()) { + m_relativePlacement = QRectF(m_plasmaShellSurface->position(), positioner.size()).translated(-parentPosition); + } else { + m_relativePlacement = positioner.placement(bounds); + } } void XdgPopupWindow::relayout() @@ -1673,132 +1680,6 @@ QRectF XdgPopupWindow::transientPlacement() const return m_relativePlacement.translated(parentPosition); } -QRectF XdgPopupWindow::placement() const -{ - const QRectF bounds = Workspace::self()->clientArea(transientFor()->isFullScreen() ? FullScreenArea : PlacementArea, transientFor()); - - const XdgPositioner positioner = m_shellSurface->positioner(); - const QSize desiredSize = positioner.size(); - - if (m_plasmaShellSurface && m_plasmaShellSurface->isPositionSet()) { - return QRectF(m_plasmaShellSurface->position(), desiredSize); - } - - const QPointF parentPosition = transientFor()->framePosToClientPos(transientFor()->pos()); - - // returns if a target is within the supplied bounds, optional edges argument states which side to check - auto inBounds = [bounds](const QRectF &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool { - if (edges & Qt::LeftEdge && target.left() < bounds.left()) { - return false; - } - if (edges & Qt::TopEdge && target.top() < bounds.top()) { - return false; - } - if (edges & Qt::RightEdge && target.right() > bounds.right()) { - // normal QRect::right issue cancels out - return false; - } - if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) { - return false; - } - return true; - }; - - QRectF popupRect(popupOffset(positioner.anchorRect(), positioner.anchorEdges(), positioner.gravityEdges(), desiredSize) + positioner.offset() + parentPosition, desiredSize); - - // if that fits, we don't need to do anything - if (inBounds(popupRect)) { - return popupRect; - } - // otherwise apply constraint adjustment per axis in order XDG Shell Popup states - - if (positioner.flipConstraintAdjustments() & Qt::Horizontal) { - if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) { - // flip both edges (if either bit is set, XOR both) - auto flippedAnchorEdge = positioner.anchorEdges(); - if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) { - flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge); - } - auto flippedGravity = positioner.gravityEdges(); - if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) { - flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge); - } - auto flippedPopupRect = QRectF(popupOffset(positioner.anchorRect(), flippedAnchorEdge, flippedGravity, desiredSize) + positioner.offset() + parentPosition, desiredSize); - - // if it still doesn't fit we should continue with the unflipped version - if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) { - popupRect.moveLeft(flippedPopupRect.left()); - } - } - } - if (positioner.slideConstraintAdjustments() & Qt::Horizontal) { - if (!inBounds(popupRect, Qt::LeftEdge)) { - popupRect.moveLeft(bounds.left()); - } - if (!inBounds(popupRect, Qt::RightEdge)) { - popupRect.moveRight(bounds.right()); - } - } - if (positioner.resizeConstraintAdjustments() & Qt::Horizontal) { - QRectF unconstrainedRect = popupRect; - - if (!inBounds(unconstrainedRect, Qt::LeftEdge)) { - unconstrainedRect.setLeft(bounds.left()); - } - if (!inBounds(unconstrainedRect, Qt::RightEdge)) { - unconstrainedRect.setRight(bounds.right()); - } - - if (unconstrainedRect.isValid()) { - popupRect = unconstrainedRect; - } - } - - if (positioner.flipConstraintAdjustments() & Qt::Vertical) { - if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) { - // flip both edges (if either bit is set, XOR both) - auto flippedAnchorEdge = positioner.anchorEdges(); - if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) { - flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge); - } - auto flippedGravity = positioner.gravityEdges(); - if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) { - flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge); - } - auto flippedPopupRect = QRectF(popupOffset(positioner.anchorRect(), flippedAnchorEdge, flippedGravity, desiredSize) + positioner.offset() + parentPosition, desiredSize); - - // if it still doesn't fit we should continue with the unflipped version - if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) { - popupRect.moveTop(flippedPopupRect.top()); - } - } - } - if (positioner.slideConstraintAdjustments() & Qt::Vertical) { - if (!inBounds(popupRect, Qt::TopEdge)) { - popupRect.moveTop(bounds.top()); - } - if (!inBounds(popupRect, Qt::BottomEdge)) { - popupRect.moveBottom(bounds.bottom()); - } - } - if (positioner.resizeConstraintAdjustments() & Qt::Vertical) { - QRectF unconstrainedRect = popupRect; - - if (!inBounds(unconstrainedRect, Qt::TopEdge)) { - unconstrainedRect.setTop(bounds.top()); - } - if (!inBounds(unconstrainedRect, Qt::BottomEdge)) { - unconstrainedRect.setBottom(bounds.bottom()); - } - - if (unconstrainedRect.isValid()) { - popupRect = unconstrainedRect; - } - } - - return popupRect; -} - bool XdgPopupWindow::isCloseable() const { return false; diff --git a/src/xdgshellwindow.h b/src/xdgshellwindow.h index 85dba45617..07d99a5943 100644 --- a/src/xdgshellwindow.h +++ b/src/xdgshellwindow.h @@ -261,8 +261,6 @@ protected: void handleRoleDestroyed() override; private: - QRectF placement() const; - void handleGrabRequested(KWaylandServer::SeatInterface *seat, quint32 serial); void handleRepositionRequested(quint32 token); void initialize();