forked from quickshell/quickshell
core/popupanchor: rework popup anchoring and add PopupAnchor
This commit is contained in:
parent
14910b1b60
commit
ebfa8ec448
14 changed files with 770 additions and 108 deletions
|
@ -52,6 +52,8 @@ endfunction()
|
|||
|
||||
qt_add_library(quickshell-wayland STATIC
|
||||
platformmenu.cpp
|
||||
popupanchor.cpp
|
||||
xdgshell.cpp
|
||||
)
|
||||
|
||||
# required to make sure the constructor is linked
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
#include <qtenvironmentvariables.h>
|
||||
|
||||
#include "../core/plugin.hpp"
|
||||
#include "platformmenu.hpp"
|
||||
|
||||
#ifdef QS_WAYLAND_WLR_LAYERSHELL
|
||||
#include "wlr_layershell.hpp"
|
||||
#endif
|
||||
|
||||
void installPlatformMenuHook();
|
||||
void installPopupPositioner();
|
||||
|
||||
namespace {
|
||||
|
||||
class WaylandPlugin: public QuickshellPlugin {
|
||||
|
@ -27,7 +29,10 @@ class WaylandPlugin: public QuickshellPlugin {
|
|||
return isWayland;
|
||||
}
|
||||
|
||||
void init() override { installPlatformMenuHook(); }
|
||||
void init() override {
|
||||
installPlatformMenuHook();
|
||||
installPopupPositioner();
|
||||
}
|
||||
|
||||
void registerTypes() override {
|
||||
#ifdef QS_WAYLAND_WLR_LAYERSHELL
|
||||
|
|
99
src/wayland/popupanchor.cpp
Normal file
99
src/wayland/popupanchor.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "popupanchor.hpp"
|
||||
|
||||
#include <private/qwayland-xdg-shell.h>
|
||||
#include <private/qwaylandwindow_p.h>
|
||||
#include <private/wayland-xdg-shell-client-protocol.h>
|
||||
#include <qvariant.h>
|
||||
#include <qwindow.h>
|
||||
|
||||
#include "../core/popupanchor.hpp"
|
||||
#include "../core/types.hpp"
|
||||
#include "xdgshell.hpp"
|
||||
|
||||
using QtWaylandClient::QWaylandWindow;
|
||||
using XdgPositioner = QtWayland::xdg_positioner;
|
||||
using qs::wayland::xdg_shell::XdgWmBase;
|
||||
|
||||
void WaylandPopupPositioner::reposition(PopupAnchor* anchor, QWindow* window, bool onlyIfDirty) {
|
||||
if (onlyIfDirty && !anchor->isDirty()) return;
|
||||
|
||||
auto* waylandWindow = dynamic_cast<QWaylandWindow*>(window->handle());
|
||||
auto* popupRole = waylandWindow ? waylandWindow->surfaceRole<::xdg_popup>() : nullptr;
|
||||
|
||||
anchor->markClean();
|
||||
|
||||
if (popupRole) {
|
||||
auto* xdgWmBase = XdgWmBase::instance();
|
||||
|
||||
if (xdgWmBase->QtWayland::xdg_wm_base::version() < XDG_POPUP_REPOSITION_SINCE_VERSION) {
|
||||
window->setVisible(false);
|
||||
WaylandPopupPositioner::setFlags(anchor, window);
|
||||
window->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto positioner = XdgPositioner(xdgWmBase->create_positioner());
|
||||
|
||||
positioner.set_constraint_adjustment(anchor->adjustment().toInt());
|
||||
|
||||
auto anchorRect = anchor->rect();
|
||||
positioner.set_anchor_rect(anchorRect.x, anchorRect.y, anchorRect.w, anchorRect.h);
|
||||
|
||||
XdgPositioner::anchor anchorFlag = XdgPositioner::anchor_none;
|
||||
switch (anchor->edges()) {
|
||||
case Edges::Top: anchorFlag = XdgPositioner::anchor_top; break;
|
||||
case Edges::Top | Edges::Right: anchorFlag = XdgPositioner::anchor_top_right; break;
|
||||
case Edges::Right: anchorFlag = XdgPositioner::anchor_right; break;
|
||||
case Edges::Bottom | Edges::Right: anchorFlag = XdgPositioner::anchor_bottom_right; break;
|
||||
case Edges::Bottom: anchorFlag = XdgPositioner::anchor_bottom; break;
|
||||
case Edges::Bottom | Edges::Left: anchorFlag = XdgPositioner::anchor_bottom_left; break;
|
||||
case Edges::Left: anchorFlag = XdgPositioner::anchor_left; break;
|
||||
case Edges::Top | Edges::Left: anchorFlag = XdgPositioner::anchor_top_left; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
positioner.set_anchor(anchorFlag);
|
||||
|
||||
XdgPositioner::gravity gravity = XdgPositioner::gravity_none;
|
||||
switch (anchor->gravity()) {
|
||||
case Edges::Top: gravity = XdgPositioner::gravity_top; break;
|
||||
case Edges::Top | Edges::Right: gravity = XdgPositioner::gravity_top_right; break;
|
||||
case Edges::Right: gravity = XdgPositioner::gravity_right; break;
|
||||
case Edges::Bottom | Edges::Right: gravity = XdgPositioner::gravity_bottom_right; break;
|
||||
case Edges::Bottom: gravity = XdgPositioner::gravity_bottom; break;
|
||||
case Edges::Bottom | Edges::Left: gravity = XdgPositioner::gravity_bottom_left; break;
|
||||
case Edges::Left: gravity = XdgPositioner::gravity_left; break;
|
||||
case Edges::Top | Edges::Left: gravity = XdgPositioner::gravity_top_left; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
positioner.set_gravity(gravity);
|
||||
auto geometry = waylandWindow->geometry();
|
||||
positioner.set_size(geometry.width(), geometry.height());
|
||||
|
||||
// Note: this needs to be set for the initial position as well but no compositor
|
||||
// supports it enough to test
|
||||
positioner.set_reactive();
|
||||
|
||||
xdg_popup_reposition(popupRole, positioner.object(), 0);
|
||||
|
||||
positioner.destroy();
|
||||
} else {
|
||||
WaylandPopupPositioner::setFlags(anchor, window);
|
||||
}
|
||||
}
|
||||
|
||||
// Should be false but nobody supports set_reactive.
|
||||
// This just tries its best when something like a bar gets resized.
|
||||
bool WaylandPopupPositioner::shouldRepositionOnMove() const { return true; }
|
||||
|
||||
void WaylandPopupPositioner::setFlags(PopupAnchor* anchor, QWindow* window) {
|
||||
// clang-format off
|
||||
window->setProperty("_q_waylandPopupConstraintAdjustment", anchor->adjustment().toInt());
|
||||
window->setProperty("_q_waylandPopupAnchorRect", anchor->rect().qrect());
|
||||
window->setProperty("_q_waylandPopupAnchor", QVariant::fromValue(Edges::toQt(anchor->edges())));
|
||||
window->setProperty("_q_waylandPopupGravity", QVariant::fromValue(Edges::toQt(anchor->gravity())));
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void installPopupPositioner() { PopupPositioner::setInstance(new WaylandPopupPositioner()); }
|
16
src/wayland/popupanchor.hpp
Normal file
16
src/wayland/popupanchor.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <qwindow.h>
|
||||
|
||||
#include "../core/popupanchor.hpp"
|
||||
|
||||
class WaylandPopupPositioner: public PopupPositioner {
|
||||
public:
|
||||
void reposition(PopupAnchor* anchor, QWindow* window, bool onlyIfDirty = true) override;
|
||||
[[nodiscard]] bool shouldRepositionOnMove() const override;
|
||||
|
||||
private:
|
||||
static void setFlags(PopupAnchor* anchor, QWindow* window);
|
||||
};
|
||||
|
||||
void installPopupPositioner();
|
14
src/wayland/xdgshell.cpp
Normal file
14
src/wayland/xdgshell.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "xdgshell.hpp"
|
||||
|
||||
#include <qwaylandclientextension.h>
|
||||
|
||||
namespace qs::wayland::xdg_shell {
|
||||
|
||||
XdgWmBase::XdgWmBase(): QWaylandClientExtensionTemplate(6) { this->initialize(); }
|
||||
|
||||
XdgWmBase* XdgWmBase::instance() {
|
||||
static auto* instance = new XdgWmBase(); // NOLINT
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace qs::wayland::xdg_shell
|
20
src/wayland/xdgshell.hpp
Normal file
20
src/wayland/xdgshell.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <private/qwayland-xdg-shell.h>
|
||||
#include <qwaylandclientextension.h>
|
||||
|
||||
namespace qs::wayland::xdg_shell {
|
||||
|
||||
// Hack that binds xdg_wm_base twice as QtWaylandXdgShell headers are not exported anywhere.
|
||||
|
||||
class XdgWmBase
|
||||
: public QWaylandClientExtensionTemplate<XdgWmBase>
|
||||
, public QtWayland::xdg_wm_base {
|
||||
public:
|
||||
static XdgWmBase* instance();
|
||||
|
||||
private:
|
||||
explicit XdgWmBase();
|
||||
};
|
||||
|
||||
} // namespace qs::wayland::xdg_shell
|
Loading…
Add table
Add a link
Reference in a new issue