Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions code.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,28 @@
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [
"tst_affinityWithPersistentCentralGroup"
"--wayland-toplevel-drag",
"-platform",
"wayland"
],
"cwd": "${command:cmake.buildDirectory}",
"env": {}
"env": {
"XDG_RUNTIME_DIR": "/run/user/1000"
}
},
{
"name": "Current Target (GDB)",
"type": "cppdbg",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [
"tst_affinityWithPersistentCentralGroup"
"--wayland-toplevel-drag",
"-platform",
"wayland"
],
"env": {
"XDG_RUNTIME_DIR": "/run/user/1000"
},
"cwd": "${command:cmake.buildDirectory}",
"MIMode": "gdb",
"setupCommands": [
Expand Down
12 changes: 12 additions & 0 deletions examples/dockwidgets/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ int main(int argc, char **argv)
QCoreApplication::translate("main", "Ctrl key toggles drop indicators"));
parser.addOption(ctrlTogglesDropIndicators);

#ifdef Q_OS_LINUX
QCommandLineOption waylandToplevelDrag(
"wayland-toplevel-drag",
QCoreApplication::translate("main", "Use xdg-toplevel-drag on Wayland so the actual window drags instead of a pixmap"));
parser.addOption(waylandToplevelDrag);
#endif

#if defined(DOCKS_DEVELOPER_MODE)
parser.addOption(centralFrame);

Expand Down Expand Up @@ -369,6 +376,11 @@ int main(int argc, char **argv)
if (parser.isSet(doubleClickMaximize))
flags |= KDDockWidgets::Config::Flag_DoubleClickMaximizes;

#ifdef Q_OS_LINUX
if (parser.isSet(waylandToplevelDrag))
flags |= KDDockWidgets::Config::Flag_WaylandToplevelDrag;
#endif

if (parser.isSet(incompatibleMainWindows) && !parser.isSet(multipleMainWindows)) {
qWarning() << "Error: Argument -i requires -m";
return 1;
Expand Down
2 changes: 1 addition & 1 deletion src/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ void Config::Private::fixFlags()
}

#if defined(Q_OS_LINUX)
if (KDDockWidgets::isWayland()) {
if (KDDockWidgets::isWayland() && !(m_flags & Config::Flag_WaylandToplevelDrag)) {
// Native title bar is forced on Wayland. Needed for moving the window.
// The inner KDDW title bar is used for DnD.
m_flags |= Flag_NativeTitleBar;
Expand Down
1 change: 1 addition & 0 deletions src/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class DOCKS_EXPORT Config
///< right clicking on the tab area
Flag_AutoHideAsTabGroups = 0x100000, ///< If tabbed dockwidgets are sent to/from sidebar, they're all sent and restored together
Flag_DisableDoubleClick = 0x200000, ///< Do not maximize of float if a title or tab is double-clicked.
Flag_WaylandToplevelDrag = 0x400000, ///< EXPERIMENTAL. Uses xdg-toplevel-drag on Wayland so the actual window drags instead of a pixmap. Requires compositor support.
Flag_Default = Flag_AeroSnapWithClientDecos ///< The defaults
};
Q_DECLARE_FLAGS(Flags, Flag)
Expand Down
9 changes: 6 additions & 3 deletions src/core/DragController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,13 @@ namespace {
StateDragging *createDraggingState(DragController *parent)
{
#ifdef KDDW_FRONTEND_QT
return isWayland() ? new StateDraggingWayland(parent) : new StateDragging(parent);
#else
return new StateDragging(parent);
if (isWayland()) {
if (Config::self().flags() & Config::Flag_WaylandToplevelDrag)
return new StateDraggingWaylandToplevel(parent);
return new StateDraggingWayland(parent);
}
#endif
return new StateDragging(parent);
}

}
Expand Down
1 change: 1 addition & 0 deletions src/core/DragController_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class DOCKS_EXPORT_FOR_UNIT_TESTS DragController : public MinimalStateMachine, p
friend class StateInternalMDIDragging;
friend class StateDropped;
friend class StateDraggingWayland;
friend class StateDraggingWaylandToplevel;
friend class ::TestQtWidgets;

explicit DragController(Core::Object * = nullptr);
Expand Down
12 changes: 11 additions & 1 deletion src/core/Utils_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ inline bool isWayland()
return Core::Platform::instance()->displayType() == Core::Platform::DisplayType::Wayland;
}

inline bool isWaylandXDGTopLevelDrag()
{
return isWayland() && (Config::self().flags() & Config::Flag_WaylandToplevelDrag);
}

inline bool isWaylandNonXDGTopLevelDrag()
{
return isWayland() && !isWaylandXDGTopLevelDrag();
}

inline bool isOffscreen()
{
return Core::Platform::instance()->displayType() == Core::Platform::DisplayType::QtOffscreen;
Expand Down Expand Up @@ -58,7 +68,7 @@ inline bool usesNativeTitleBar()
inline bool usesClientTitleBar()
{
if (isWayland()) {
// Wayland has both client and native title bars, due to limitations.
// Wayland always has the client title-bar, since the native one doesn't emit events when moved
return true;
}

Expand Down
80 changes: 80 additions & 0 deletions src/qtcommon/DragControllerWayland_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include "core/ScopedValueRollback_p.h"
#include "kddockwidgets/core/Platform.h"
#include "kddockwidgets/core/DropArea.h"
#include "kddockwidgets/core/FloatingWindow.h"
#include "qtcommon/Window_p.h"
#include "Config.h"

#include <QWindow>
#include <QDataStream>

using namespace KDDockWidgets::Core;

Expand Down Expand Up @@ -127,3 +133,77 @@ bool StateDraggingWayland::handleDragMove(DragMoveEvent *ev, DropArea *dropArea,

return true;
}

StateDraggingWaylandToplevel::StateDraggingWaylandToplevel(DragController *parent)
: StateDraggingWayland(parent)
{
}

StateDraggingWaylandToplevel::~StateDraggingWaylandToplevel()
{
}

void StateDraggingWaylandToplevel::onEntry()
{
KDDW_DEBUG("StateDraggingWaylandToplevel entered");

if (DragController::instance()->m_inQDrag) {
KDDW_ERROR("Impossible!");
return;
}

ScopedValueRollback guard(DragController::instance()->m_inQDrag, true);

q->m_windowBeingDragged = q->m_draggable->makeWindow();
if (!q->m_windowBeingDragged) {
KDDW_ERROR("StateDraggingWaylandToplevel: Failed to create window");
q->dragCanceled.emit();
return;
}

auto fw = q->m_windowBeingDragged->floatingWindow();
if (!fw) {
KDDW_ERROR("StateDraggingWaylandToplevel: No FloatingWindow");
q->dragCanceled.emit();
return;
}

auto window = fw->view()->window();
if (!window) {
KDDW_ERROR("StateDraggingWaylandToplevel: No window");
q->dragCanceled.emit();
return;
}

auto qtWindow = static_cast<QtCommon::Window *>(window.get())->qtWindow();
if (!qtWindow) {
KDDW_ERROR("StateDraggingWaylandToplevel: No QWindow");
q->dragCanceled.emit();
return;
}

auto serialize = [](const auto &object) {
QByteArray data;
QDataStream dataStream(&data, QIODevice::WriteOnly);
dataStream << object;
return data;
};

auto mimeData = new WaylandMimeData();
mimeData->setData(QStringLiteral("application/x-qt-mainwindowdrag-window"),
serialize(reinterpret_cast<qintptr>(qtWindow))); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
mimeData->setData(QStringLiteral("application/x-qt-mainwindowdrag-position"),
serialize(QCursor::pos()));

Drag drag(this);
drag.setMimeData(mimeData);

Platform::instance()->installGlobalEventFilter(q);
KDDW_DEBUG("Started QDrag (toplevel)");
const Qt::DropAction result = drag.exec();
KDDW_DEBUG("QDrag (toplevel) finished with result={}", int(result));

Platform::instance()->removeGlobalEventFilter(q);
if (result == Qt::IgnoreAction)
q->dragCanceled.emit();
}
9 changes: 9 additions & 0 deletions src/qtcommon/DragControllerWayland_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,14 @@ class WaylandMimeData : public QMimeData
public:
};

class StateDraggingWaylandToplevel : public StateDraggingWayland
{
Q_OBJECT
public:
explicit StateDraggingWaylandToplevel(DragController *parent);
~StateDraggingWaylandToplevel() override;
void onEntry() override;
};

}
}
1 change: 0 additions & 1 deletion src/qtwidgets/views/ClassicIndicatorsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ QString Indicator::iconFileName(bool active) const
static QWidget *parentForIndicatorWindow(ClassicDropIndicatorOverlay *classicIndicators_)
{
// On Wayland it can't be a top-level, as we have no way of positioning it

return isWayland() ? QtCommon::View_qt::asQWidget(classicIndicators_->view()) : nullptr;
}

Expand Down
Loading