diff --git a/atoms.cpp b/atoms.cpp
index 904f5efe4a..ec2c60fcb8 100644
--- a/atoms.cpp
+++ b/atoms.cpp
@@ -58,6 +58,7 @@ Atoms::Atoms()
, kde_first_in_window_list(QByteArrayLiteral("_KDE_FIRST_IN_WINDOWLIST"))
, kde_color_sheme(QByteArrayLiteral("_KDE_NET_WM_COLOR_SCHEME"))
, kde_skip_close_animation(QByteArrayLiteral("_KDE_NET_WM_SKIP_CLOSE_ANIMATION"))
+ , kde_screen_edge_show(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"))
, m_dtSmWindowInfo(QByteArrayLiteral("_DT_SM_WINDOW_INFO"))
, m_motifSupport(QByteArrayLiteral("_MOTIF_WM_INFO"))
, m_helpersRetrieved(false)
diff --git a/atoms.h b/atoms.h
index 1690067c5d..3566d0a389 100644
--- a/atoms.h
+++ b/atoms.h
@@ -67,6 +67,7 @@ public:
Xcb::Atom kde_first_in_window_list;
Xcb::Atom kde_color_sheme;
Xcb::Atom kde_skip_close_animation;
+ Xcb::Atom kde_screen_edge_show;
/**
* @internal
diff --git a/client.cpp b/client.cpp
index 36431bfc33..023419254b 100644
--- a/client.cpp
+++ b/client.cpp
@@ -42,6 +42,7 @@ along with this program. If not, see .
#include "tabbox.h"
#endif
#include "workspace.h"
+#include "screenedge.h"
// KDE
#include
#include
@@ -2503,6 +2504,59 @@ xcb_window_t Client::frameId() const
return m_frame;
}
+void Client::updateShowOnScreenEdge()
+{
+ auto cookie = xcb_get_property_unchecked(connection(), false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1);
+ ScopedCPointer reply(xcb_get_property_reply(connection(), cookie, nullptr));
+
+ auto restore = [this]() {
+ // TODO: add proper unreserve
+ ScreenEdges::self()->reserve(this, ElectricNone);
+ hideClient(false);
+ };
+
+ if (!reply.isNull()) {
+ if (reply->format == 32 && reply->type == XCB_ATOM_CARDINAL && reply->value_len == 1) {
+ const uint32_t value = *reinterpret_cast(xcb_get_property_value(reply.data()));
+ ElectricBorder border = ElectricNone;
+ switch (value) {
+ case 0:
+ border = ElectricTop;
+ break;
+ case 1:
+ border = ElectricRight;
+ break;
+ case 2:
+ border = ElectricBottom;
+ break;
+ case 3:
+ border = ElectricLeft;
+ break;
+ }
+ if (border != ElectricNone) {
+ hideClient(true);
+ ScreenEdges::self()->reserve(this, border);
+ } else {
+ // property value is incorrect, delete the property
+ // so that the client knows that it is not hidden
+ xcb_delete_property(connection(), window(), atoms->kde_screen_edge_show);
+ }
+
+ } else if (reply->type == XCB_ATOM_NONE) {
+ // the property got deleted, show the client again
+ restore();
+ }
+ } else {
+ restore();
+ }
+}
+
+void Client::showOnScreenEdge()
+{
+ hideClient(false);
+ xcb_delete_property(connection(), window(), atoms->kde_screen_edge_show);
+}
+
} // namespace
#include "client.moc"
diff --git a/client.h b/client.h
index 6a0dbe4f45..a8ab0027bd 100644
--- a/client.h
+++ b/client.h
@@ -648,6 +648,12 @@ public:
QPalette palette() const;
+ /**
+ * Restores the Client after it had been hidden due to show on screen edge functionality.
+ * In addition the property gets deleted so that the Client knows that it is visible again.
+ **/
+ void showOnScreenEdge();
+
public Q_SLOTS:
void closeWindow();
void updateCaption();
@@ -834,6 +840,12 @@ private:
bool tabTo(Client *other, bool behind, bool activate);
+ /**
+ * Reads the property and creates/destroys the screen edge if required
+ * and shows/hides the client.
+ **/
+ void updateShowOnScreenEdge();
+
Xcb::Window m_client;
Xcb::Window m_wrapper;
Xcb::Window m_frame;
diff --git a/events.cpp b/events.cpp
index 1fa6e425d4..a2ba41f245 100644
--- a/events.cpp
+++ b/events.cpp
@@ -803,6 +803,8 @@ void Client::propertyNotifyEvent(xcb_property_notify_event_t *e)
updateFirstInTabBox();
else if (e->atom == atoms->kde_color_sheme)
updateColorScheme();
+ else if (e->atom == atoms->kde_screen_edge_show)
+ updateShowOnScreenEdge();
break;
}
}
diff --git a/manage.cpp b/manage.cpp
index 3e385cd6ae..563cedb34d 100644
--- a/manage.cpp
+++ b/manage.cpp
@@ -619,6 +619,7 @@ bool Client::manage(xcb_window_t w, bool isMapped)
updateCompositeBlocking(true);
updateColorScheme();
+ updateShowOnScreenEdge();
// TODO: there's a small problem here - isManaged() depends on the mapping state,
// but this client is not yet in Workspace's client list at this point, will
diff --git a/screenedge.cpp b/screenedge.cpp
index 04cf0d6d52..3c3d1d3ee7 100644
--- a/screenedge.cpp
+++ b/screenedge.cpp
@@ -59,6 +59,7 @@ Edge::Edge(ScreenEdges *parent)
, m_approaching(false)
, m_lastApproachingFactor(0)
, m_blocked(false)
+ , m_client(nullptr)
{
}
@@ -87,6 +88,7 @@ void Edge::unreserve()
m_reserved--;
if (m_reserved == 0) {
// got deactivated
+ stopApproaching();
deactivate();
}
}
@@ -122,20 +124,28 @@ bool Edge::triggersFor(const QPoint &cursorPos) const
return true;
}
-void Edge::check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack)
+bool Edge::check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack)
{
if (!triggersFor(cursorPos)) {
- return;
+ return false;
}
// no pushback so we have to activate at once
bool directActivate = forceNoPushBack || edges()->cursorPushBackDistance().isNull();
if (directActivate || canActivate(cursorPos, triggerTime)) {
- m_lastTrigger = triggerTime;
- m_lastReset = QDateTime(); // invalidate
+ markAsTriggered(cursorPos, triggerTime);
handle(cursorPos);
+ return true;
} else {
pushCursorBack(cursorPos);
+ m_triggeredPoint = cursorPos;
}
+ return false;
+}
+
+void Edge::markAsTriggered(const QPoint &cursorPos, const QDateTime &triggerTime)
+{
+ m_lastTrigger = triggerTime;
+ m_lastReset = QDateTime(); // invalidate
m_triggeredPoint = cursorPos;
}
@@ -164,6 +174,12 @@ bool Edge::canActivate(const QPoint &cursorPos, const QDateTime &triggerTime)
void Edge::handle(const QPoint &cursorPos)
{
+ if (m_client) {
+ pushCursorBack(cursorPos);
+ m_client->showOnScreenEdge();
+ unreserve();
+ return;
+ }
if ((edges()->isDesktopSwitchingMovingClients() && Workspace::self()->getMovingClient()) ||
(edges()->isDesktopSwitching() && isScreenEdge())) {
// always switch desktops in case:
@@ -555,6 +571,12 @@ ScreenEdges::ScreenEdges(QObject *parent)
{
QWidget w;
m_cornerOffset = (w.physicalDpiX() + w.physicalDpiY() + 5) / 6;
+
+ connect(workspace(), &Workspace::clientRemoved, [this](KWin::Client *client) {
+ deleteEdgeForClient(client);
+ QObject::disconnect(client, &Client::geometryChanged,
+ ScreenEdges::self(), &ScreenEdges::handleClientGeometryChanged);
+ });
}
ScreenEdges::~ScreenEdges()
@@ -808,6 +830,11 @@ void ScreenEdges::recreateEdges()
oldIt != oldEdges.constEnd();
++oldIt) {
WindowBasedEdge *oldEdge = *oldIt;
+ if (oldEdge->client()) {
+ // show the client again and don't recreate the edge
+ oldEdge->client()->showOnScreenEdge();
+ continue;
+ }
if (oldEdge->border() != edge->border()) {
continue;
}
@@ -869,15 +896,17 @@ void ScreenEdges::createHorizontalEdge(ElectricBorder border, const QRect &scree
m_edges << createEdge(border, x, y, width, 1);
}
-WindowBasedEdge *ScreenEdges::createEdge(ElectricBorder border, int x, int y, int width, int height)
+WindowBasedEdge *ScreenEdges::createEdge(ElectricBorder border, int x, int y, int width, int height, bool createAction)
{
WindowBasedEdge *edge = new WindowBasedEdge(this);
edge->setBorder(border);
edge->setGeometry(QRect(x, y, width, height));
- const ElectricBorderAction action = actionForEdge(edge);
- if (action != KWin::ElectricActionNone) {
- edge->reserve();
- edge->setAction(action);
+ if (createAction) {
+ const ElectricBorderAction action = actionForEdge(edge);
+ if (action != KWin::ElectricActionNone) {
+ edge->reserve();
+ edge->setAction(action);
+ }
}
if (isDesktopSwitching()) {
if (edge->isCorner()) {
@@ -961,8 +990,127 @@ void ScreenEdges::unreserve(ElectricBorder border, QObject *object)
}
}
+void ScreenEdges::reserve(Client *client, ElectricBorder border)
+{
+ auto it = m_edges.begin();
+ while (it != m_edges.end()) {
+ if ((*it)->client() == client) {
+ if ((*it)->border() == border) {
+ if (client->isHiddenInternal() && !(*it)->isReserved()) {
+ (*it)->reserve();
+ }
+ return;
+ } else {
+ delete *it;
+ it = m_edges.erase(it);
+ }
+ } else {
+ it++;
+ }
+ }
+ createEdgeForClient(client, border);
+
+ connect(client, &Client::geometryChanged, this, &ScreenEdges::handleClientGeometryChanged);
+}
+
+void ScreenEdges::createEdgeForClient(Client *client, ElectricBorder border)
+{
+ int y = 0;
+ int x = 0;
+ int width = 0;
+ int height = 0;
+ const QRect geo = client->geometry();
+ const QRect fullArea = workspace()->clientArea(FullArea, 0, 1);
+ for (int i = 0; i < screens()->count(); ++i) {
+ const QRect screen = screens()->geometry(i);
+ if (!screen.contains(geo)) {
+ // ignoring Clients having a geometry overlapping with multiple screens
+ // this would make the code more complex. If it's needed in future it can be added
+ continue;
+ }
+ const bool bordersTop = (screen.y() == geo.y());
+ const bool bordersLeft = (screen.x() == geo.x());
+ const bool bordersBottom = (screen.y() + screen.height() == geo.y() + geo.height());
+ const bool bordersRight = (screen.x() + screen.width() == geo.x() + geo.width());
+
+ if (bordersTop && border == ElectricTop) {
+ if (!isTopScreen(screen, fullArea)) {
+ continue;
+ }
+ y = geo.y();
+ x = geo.x();
+ height = 1;
+ width = geo.width();
+ break;
+ }
+ if (bordersBottom && border == ElectricBottom) {
+ if (!isBottomScreen(screen, fullArea)) {
+ continue;
+ }
+ y = geo.y() + geo.height() - 1;
+ x = geo.x();
+ height = 1;
+ width = geo.width();
+ break;
+ }
+ if (bordersLeft && border == ElectricLeft) {
+ if (!isLeftScreen(screen, fullArea)) {
+ continue;
+ }
+ x = geo.x();
+ y = geo.y();
+ width = 1;
+ height = geo.height();
+ break;
+ }
+ if (bordersRight && border == ElectricRight) {
+ if (!isRightScreen(screen, fullArea)) {
+ continue;
+ }
+ x = geo.x() + geo.width() - 1;
+ y = geo.y();
+ width = 1;
+ height = geo.height();
+ break;
+ }
+ }
+
+ if (width > 0 && height > 0) {
+ WindowBasedEdge *edge = createEdge(border, x, y, width, height, false);
+ edge->setClient(client);
+ m_edges.append(edge);
+ if (client->isHiddenInternal()) {
+ edge->reserve();
+ }
+ } else {
+ // we could not create an edge window, so don't allow the window to hide
+ client->showOnScreenEdge();
+ }
+}
+
+void ScreenEdges::handleClientGeometryChanged()
+{
+ Client *c = static_cast(sender());
+ deleteEdgeForClient(c);
+ c->showOnScreenEdge();
+}
+
+void ScreenEdges::deleteEdgeForClient(Client* c)
+{
+ auto it = m_edges.begin();
+ while (it != m_edges.end()) {
+ if ((*it)->client() == c) {
+ delete *it;
+ it = m_edges.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
void ScreenEdges::check(const QPoint &pos, const QDateTime &now, bool forceNoPushBack)
{
+ bool activatedForClient = false;
for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) {
if (!(*it)->isReserved()) {
continue;
@@ -970,7 +1118,15 @@ void ScreenEdges::check(const QPoint &pos, const QDateTime &now, bool forceNoPus
if ((*it)->approachGeometry().contains(pos)) {
(*it)->startApproaching();
}
- (*it)->check(pos, now, forceNoPushBack);
+ if ((*it)->client() != nullptr && activatedForClient) {
+ (*it)->markAsTriggered(pos, now);
+ continue;
+ }
+ if ((*it)->check(pos, now, forceNoPushBack)) {
+ if ((*it)->client()) {
+ activatedForClient = true;
+ }
+ }
}
}
@@ -992,14 +1148,21 @@ bool ScreenEdges::isEntered(xcb_client_message_event_t *event)
bool ScreenEdges::handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp)
{
+ bool activated = false;
+ bool activatedForClient = false;
for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) {
WindowBasedEdge *edge = *it;
if (!edge->isReserved()) {
continue;
}
if (edge->window() == window) {
- edge->check(point, timestamp);
- return true;
+ if (edge->check(point, timestamp)) {
+ if ((*it)->client()) {
+ activatedForClient = true;
+ }
+ }
+ activated = true;
+ break;
}
if (edge->approachWindow() == window) {
edge->startApproaching();
@@ -1007,7 +1170,14 @@ bool ScreenEdges::handleEnterNotifiy(xcb_window_t window, const QPoint &point, c
return true;
}
}
- return false;
+ if (activatedForClient) {
+ for (auto it = m_edges.constBegin(); it != m_edges.constEnd(); ++it) {
+ if ((*it)->client()) {
+ (*it)->markAsTriggered(point, timestamp);
+ }
+ }
+ }
+ return activated;
}
bool ScreenEdges::handleDndNotify(xcb_window_t window, const QPoint &point)
diff --git a/screenedge.h b/screenedge.h
index 60f5fd669c..f1c579cf19 100644
--- a/screenedge.h
+++ b/screenedge.h
@@ -56,7 +56,8 @@ public:
bool isCorner() const;
bool isScreenEdge() const;
bool triggersFor(const QPoint &cursorPos) const;
- void check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack = false);
+ bool check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack = false);
+ void markAsTriggered(const QPoint &cursorPos, const QDateTime &triggerTime);
bool isReserved() const;
const QRect &approachGeometry() const;
@@ -65,6 +66,8 @@ public:
const QHash &callBacks() const;
void startApproaching();
void stopApproaching();
+ void setClient(Client *client);
+ Client *client() const;
public Q_SLOTS:
void reserve();
@@ -108,6 +111,7 @@ private:
bool m_approaching;
int m_lastApproachingFactor;
bool m_blocked;
+ Client *m_client;
};
class WindowBasedEdge : public Edge
@@ -242,6 +246,29 @@ public:
* @todo: add pointer to script/effect
*/
void unreserve(ElectricBorder border, QObject *object);
+ /**
+ * Reserves an edge for the @p client. The idea behind this is to show the @p client if the
+ * screen edge which the @p client borders gets triggered.
+ *
+ * When first called it is tried to create an Edge for the client. This is only done if the
+ * client borders with a screen edge specified by @p border. If the client doesn't border the
+ * screen edge, no Edge gets created and the client is shown again. Otherwise there would not
+ * be a possibility to show the client again.
+ *
+ * On subsequent calls for the client no new Edge is created, but the existing one gets reused
+ * and if the client is already hidden, the Edge gets reserved.
+ *
+ * Once the Edge for the client triggers, the client gets shown again and the Edge unreserved.
+ * The idea is that the Edge can only get activated if the client is currently hidden.
+ *
+ * To make sure that the client can always be shown again the implementation also starts to
+ * track geometry changes and shows the Client again. The same for screen geometry changes.
+ *
+ * The Edge gets automatically destroyed if the client gets released.
+ * @param client The Client for which an Edge should be reserved
+ * @param border The border which the client wants to use, only proper borders are supported (no corners)
+ **/
+ void reserve(KWin::Client *client, ElectricBorder border);
/**
* Reserve desktop switching for screen edges, if @p isToReserve is @c true. Unreserve otherwise.
* @param reserve indicated weather desktop switching should be reserved or unreseved
@@ -317,11 +344,14 @@ private:
void setReActivationThreshold(int threshold);
void createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea);
void createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea);
- WindowBasedEdge *createEdge(ElectricBorder border, int x, int y, int width, int height);
+ WindowBasedEdge *createEdge(ElectricBorder border, int x, int y, int width, int height, bool createAction = true);
void setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue);
ElectricBorderAction actionForEdge(Edge *edge) const;
bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp);
bool handleDndNotify(xcb_window_t window, const QPoint &point);
+ void createEdgeForClient(Client *client, ElectricBorder border);
+ void handleClientGeometryChanged();
+ void deleteEdgeForClient(Client *client);
bool m_desktopSwitching;
bool m_desktopSwitchingMovingClients;
QSize m_cursorPushBackDistance;
@@ -433,6 +463,16 @@ inline bool Edge::isBlocked() const
return m_blocked;
}
+inline void Edge::setClient(Client *client)
+{
+ m_client = client;
+}
+
+inline Client *Edge::client() const
+{
+ return m_client;
+}
+
/**********************************************************
* Inlines WindowBasedEdge
*********************************************************/
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5d8cb132a4..26524ffa0e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -3,3 +3,8 @@ if (XCB_ICCCM_FOUND)
add_executable(normalhintsbasesizetest ${normalhintsbasesizetest_SRCS})
target_link_libraries(normalhintsbasesizetest XCB::XCB XCB::ICCCM)
endif()
+
+# next target
+set(screenedgeshowtest_SRCS screenedgeshowtest.cpp)
+add_executable(screenedgeshowtest ${screenedgeshowtest_SRCS})
+target_link_libraries(screenedgeshowtest Qt5::Widgets Qt5::X11Extras ${XCB_XCB_LIBRARY})
diff --git a/tests/screenedgeshowtest.cpp b/tests/screenedgeshowtest.cpp
new file mode 100644
index 0000000000..e8bb1ceb7f
--- /dev/null
+++ b/tests/screenedgeshowtest.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 Martin Gräßlin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "../xcbutils.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ QApplication::setApplicationDisplayName(QStringLiteral("Screen Edge Show Test App"));
+
+ QScopedPointer widget(new QWidget(nullptr, Qt::FramelessWindowHint));
+
+ KWin::Xcb::Atom atom(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"));
+
+ uint32_t value = 2;
+ QPushButton *hideWindowButton = new QPushButton(QStringLiteral("Hide"), widget.data());
+ QObject::connect(hideWindowButton, &QPushButton::clicked, [&widget, &atom, &value]() {
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, widget->winId(), atom, XCB_ATOM_CARDINAL, 32, 1, &value);
+ });
+ QPushButton *hideAndRestoreButton = new QPushButton(QStringLiteral("Hide and Restore after 10 sec"), widget.data());
+ QTimer *restoreTimer = new QTimer(hideAndRestoreButton);
+ restoreTimer->setSingleShot(true);
+ QObject::connect(hideAndRestoreButton, &QPushButton::clicked, [&widget, &atom, &value, restoreTimer]() {
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, widget->winId(), atom, XCB_ATOM_CARDINAL, 32, 1, &value);
+ restoreTimer->start(10000);
+ });
+ QObject::connect(restoreTimer, &QTimer::timeout, [&widget, &atom]() {
+ xcb_delete_property(QX11Info::connection(), widget->winId(), atom);
+ });
+
+ QToolButton *edgeButton = new QToolButton(widget.data());
+ edgeButton->setText(QStringLiteral("Edge"));
+ edgeButton->setPopupMode(QToolButton::MenuButtonPopup);
+ QMenu *edgeButtonMenu = new QMenu(edgeButton);
+ QObject::connect(edgeButtonMenu->addAction("Top"), &QAction::triggered, [&widget, &value]() {
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(geo.x(), geo.y(), geo.width(), 100);
+ value = 0;
+ });
+ QObject::connect(edgeButtonMenu->addAction("Right"), &QAction::triggered, [&widget, &value]() {
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(geo.x() + geo.width() - 100, geo.y(), 100, geo.height());
+ value = 1;
+ });
+ QObject::connect(edgeButtonMenu->addAction("Bottom"), &QAction::triggered, [&widget, &value]() {
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(geo.x(), geo.y() + geo.height() - 100, geo.width(), 100);
+ value = 2;
+ });
+ QObject::connect(edgeButtonMenu->addAction("Left"), &QAction::triggered, [&widget, &value]() {
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(geo.x(), geo.y(), 100, geo.height());
+ value = 3;
+ });
+ edgeButtonMenu->addSeparator();
+ QObject::connect(edgeButtonMenu->addAction("Floating"), &QAction::triggered, [&widget, &value]() {
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(QRect(geo.center(), QSize(100, 100)));
+ value = 4;
+ });
+ edgeButton->setMenu(edgeButtonMenu);
+
+ QHBoxLayout *layout = new QHBoxLayout(widget.data());
+ layout->addWidget(hideWindowButton);
+ layout->addWidget(hideAndRestoreButton);
+ layout->addWidget(edgeButton);
+ widget->setLayout(layout);
+
+ const QRect geo = QGuiApplication::primaryScreen()->geometry();
+ widget->setGeometry(geo.x(), geo.y() + geo.height() - 100, geo.width(), 100);
+ widget->show();
+
+ return app.exec();
+}