diff --git a/src/qtquick/TestHelpers_qtquick.cpp b/src/qtquick/TestHelpers_qtquick.cpp index 353beba9..ca8ec9c1 100644 --- a/src/qtquick/TestHelpers_qtquick.cpp +++ b/src/qtquick/TestHelpers_qtquick.cpp @@ -65,7 +65,6 @@ View *Platform_qtquick::tests_createView(View *parent) newItem->QQuickItem::setParentItem(view->contentItem()); newItem->QQuickItem::setParent(view->contentItem()); - view->show(); QTest::qWait(100); // the root object gets sized delayed } diff --git a/src/qtquick/views/View_qtquick.cpp b/src/qtquick/views/View_qtquick.cpp index 7cf599dd..8bc634d5 100644 --- a/src/qtquick/views/View_qtquick.cpp +++ b/src/qtquick/views/View_qtquick.cpp @@ -10,21 +10,120 @@ */ #include "View_qtquick.h" +#include "private/Utils_p.h" #include using namespace KDDockWidgets::Views; +namespace KDDockWidgets::Views { + +/** + * @brief Event filter which redirects mouse events from one QObject to another. + * Needed for QtQuick to redirect the events from MouseArea to our KDDW classes which derive from Draggable. + * For QtWidgets it's not needed, as the Draggables are QWidgets themselves. + */ +class MouseEventRedirector : public QObject +{ + Q_OBJECT +public: + explicit MouseEventRedirector(QObject *eventSource, QObject *eventTarget) + : QObject(eventTarget) + , m_eventSource(eventSource) + , m_eventTarget(eventTarget) + { + eventSource->installEventFilter(this); + + // Each source can only have one MouseEventRedirector + auto oldRedirector = s_mouseEventRedirectors.take(eventSource); + if (oldRedirector) { + eventSource->removeEventFilter(oldRedirector); + oldRedirector->deleteLater(); + } + + s_mouseEventRedirectors.insert(eventSource, this); + } + + static MouseEventRedirector *redirectorForSource(QObject *eventSource) + { + return s_mouseEventRedirectors.value(eventSource); + } + + ~MouseEventRedirector() override; + + bool eventFilter(QObject *source, QEvent *ev) override + { + QMouseEvent *me = mouseEvent(ev); + if (!me) + return false; + + // MouseArea.enable is different from Item.enabled. The former still lets the events + // go through event loops. So query MouseArea.enable here and bail out if false. + const QVariant v = source->property("enabled"); + if (v.isValid() && !v.toBool()) + return false; + + // Finally send the event + m_eventTarget->setProperty("cursorPosition", m_eventSource->property("cursorPosition")); + qApp->sendEvent(m_eventTarget, me); + m_eventTarget->setProperty("cursorPosition", CursorPosition_Undefined); + + return false; + } + + QObject *const m_eventSource; + QObject *const m_eventTarget; + static QHash s_mouseEventRedirectors; +}; + +QHash MouseEventRedirector::s_mouseEventRedirectors = {}; + +MouseEventRedirector::~MouseEventRedirector() +{ + s_mouseEventRedirectors.remove(m_eventSource); +} + +} + +static bool flagsAreTopLevelFlags(Qt::WindowFlags flags) +{ + return flags & (Qt::Window | Qt::Tool); +} + +static QQuickItem *actualParentItem(QQuickItem *candidateParentItem, Qt::WindowFlags flags) +{ + // When we have a top-level, such as FloatingWindow, we only want to set QObject parentship + // and not parentItem. + return flagsAreTopLevelFlags(flags) ? nullptr + : candidateParentItem; +} + View_qtquick::View_qtquick(KDDockWidgets::Controller *controller, Type type, QQuickItem *parent, - Qt::WindowFlags) - : QQuickItem(parent) + Qt::WindowFlags flags) + : QQuickItem(actualParentItem(parent, flags)) , View(controller, type, this) + , m_windowFlags(flags) { - qApp->installEventFilter(this); - setVisible(false); + if (parent && flagsAreTopLevelFlags(flags)) { + // See comment in actualParentItem(). We set only the QObject parent. Mimics QWidget behaviour + QObject::setParent(parent); + } - // setSize(800, 800); + connect(this, &QQuickItem::widthChanged, this, [this] { + onResize(View::size()); + updateGeometry(); + }); + + connect(this, &QQuickItem::heightChanged, this, [this] { + if (!m_windowIsBeingDestroyed) { // If Window is being destroyed we don't bother + onResize(View::size()); + updateGeometry(); + } + }); + + qApp->installEventFilter(this); + setSize(800, 800); } void View_qtquick::setGeometry(QRect rect) @@ -32,4 +131,208 @@ void View_qtquick::setGeometry(QRect rect) setWidth(rect.width()); setHeight(rect.height()); move(rect.topLeft()); -} \ No newline at end of file +} + +QQuickItem *View_qtquick::createItem(QQmlEngine *engine, const QString &filename) +{ + QQmlComponent component(engine, filename); + QObject *obj = component.create(); + if (!obj) { + qWarning() << Q_FUNC_INFO << component.errorString(); + return nullptr; + } + + return qobject_cast(obj); +} + +void View_qtquick::redirectMouseEvents(QObject *source) +{ + if (auto existingRedirector = MouseEventRedirector::redirectorForSource(source)) { + if (existingRedirector->m_eventTarget == this) { + // Nothing to do. The specified event source is already redirecting to this instance + return; + } + } + + new MouseEventRedirector(source, this); +} + +void View_qtquick::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + QQuickItem::itemChange(change, data); + + // Emulate the QWidget behaviour as QQuickItem doesn't receive some QEvents. + switch (change) { + case QQuickItem::ItemParentHasChanged: { + QEvent ev(QEvent::ParentChange); + qApp->sendEvent(this, &ev); // Not calling event() directly, otherwise it would skip event filters + Q_EMIT parentChanged(this); + break; + } + case QQuickItem::ItemVisibleHasChanged: { + if (m_inSetParent) { + // Setting parent to nullptr will emit visible true in QtQuick + // which we don't want, as we're going to hide it (as we do with QtWidgets) + break; + } + + QEvent ev(isVisible() ? QEvent::Show : QEvent::Hide); + event(&ev); + break; + } + default: + break; + } +} + +void View_qtquick::onResizeEvent(QResizeEvent *event) +{ + QWindow *window = QQuickItem::window(); + if (!window) { + return; + } + + if (isNormalWindowState(m_oldWindowState)) { + QRect geo = normalGeometry(); + + auto curState = window->windowState(); + if (isNormalWindowState(curState)) { + geo.setSize(event->size()); + } else { + geo.setSize(event->oldSize()); + } + + setNormalGeometry(geo); + } +} +void View_qtquick::onMoveEvent(QMoveEvent *event) +{ + QWindow *window = QQuickItem::window(); + if (!window) { + return; + } + + if (isNormalWindowState(m_oldWindowState)) { + QRect geo = normalGeometry(); + + auto windowCurrentState = window->windowState(); + if (isNormalWindowState(windowCurrentState)) { + geo.moveTopLeft(event->pos()); + } else { + geo.moveTopLeft(event->oldPos()); + } + + setNormalGeometry(geo); + } +} + +void View_qtquick::move(int x, int y) +{ + if (isRootView()) { + if (QWindow *w = QQuickItem::window()) { + w->setPosition(x, y); + return; + } + } + + setX(x); + setY(y); + setAttribute(Qt::WA_Moved); +} + +bool View_qtquick::event(QEvent *ev) +{ + if (ev->type() == QEvent::Close) + onCloseEvent(static_cast(ev)); + + return QQuickItem::event(ev); +} + +bool View_qtquick::eventFilter(QObject *watched, QEvent *ev) +{ + if (qobject_cast(watched)) { + if (m_mouseTrackingEnabled) { + switch (ev->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + ev->ignore(); + qApp->sendEvent(this, ev); + // qDebug() << "Mouse event" << ev; + if (ev->isAccepted()) + return true; + break; + default: + break; + } + } + + if (ev->type() == QEvent::Resize) { + onResizeEvent(static_cast(ev)); + } else if (ev->type() == QEvent::Move) { + onMoveEvent(static_cast(ev)); + } else if (ev->type() == QEvent::WindowStateChange) { + onWindowStateChangeEvent(static_cast(ev)); + } + } + + return QQuickItem::eventFilter(watched, ev); +} + +bool View_qtquick::close() +{ + QCloseEvent ev; + onCloseEvent(&ev); + + if (ev.isAccepted()) { + setVisible(false); + return true; + } + + return false; +} + +void View_qtquick::QQUICKITEMgeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + // Send a few events manually, since QQuickItem doesn't do it for us. + QQuickItem::QQUICKITEMgeometryChanged(newGeometry, oldGeometry); + + // Not calling event() directly, otherwise it would skip event filters + + if (newGeometry.size() != oldGeometry.size()) { + QEvent ev(QEvent::Resize); + qApp->sendEvent(this, &ev); + } + + if (newGeometry.topLeft() != oldGeometry.topLeft()) { + QEvent ev(QEvent::Move); + qApp->sendEvent(this, &ev); + } + + Q_EMIT itemGeometryChanged(); +} + +bool View_qtquick::isVisible() const +{ + if (QWindow *w = QQuickItem::window()) { + if (!w->isVisible()) + return false; + } + + return QQuickItem::isVisible(); +} + +void View_qtquick::setVisible(bool is) +{ + if (isRootView()) { + if (QWindow *w = QQuickItem::window()) { + if (!w->isVisible()) { + w->show(); + } + } + } + + QQuickItem::setVisible(is); +} + +#include "View_qtquick.moc" diff --git a/src/qtquick/views/View_qtquick.h b/src/qtquick/views/View_qtquick.h index 910e1ac7..eae30162 100644 --- a/src/qtquick/views/View_qtquick.h +++ b/src/qtquick/views/View_qtquick.h @@ -14,6 +14,7 @@ #include "Controller.h" #include "View.h" #include "ViewWrapper_qtquick.h" +#include "private/multisplitter/Item_p.h" #include "qtquick/Window_qtquick.h" #include @@ -24,11 +25,14 @@ #include #include #include +#include #include namespace KDDockWidgets::Views { +class MouseEventRedirector; + inline QQuickItem *asQQuickItem(View *view) { if (!view) @@ -45,6 +49,7 @@ inline std::shared_ptr asQQuickWrapper(QQuickItem *item) class DOCKS_EXPORT View_qtquick : public QQuickItem, public View { + Q_OBJECT public: using View::close; using View::height; @@ -74,12 +79,32 @@ public: QSize minSize() const override { - return {}; + if (m_isWrapper) { + const auto children = childItems(); + if (!children.isEmpty()) { + const QSize min = children.constFirst()->property("kddockwidgets_min_size").toSize(); + return min.expandedTo(Layouting::Item::hardcodedMinimumSize); + } + } + + const QSize min = property("kddockwidgets_min_size").toSize(); + return min.expandedTo(Layouting::Item::hardcodedMinimumSize); } QSize maxSizeHint() const override { - return {}; + if (m_isWrapper) { + const auto children = childItems(); + if (!children.isEmpty()) { + const QSize max = children.constFirst()->property("kddockwidgets_max_size").toSize(); + return max.isEmpty() ? Layouting::Item::hardcodedMaximumSize + : max.boundedTo(Layouting::Item::hardcodedMaximumSize); + } + } + + const QSize max = property("kddockwidgets_max_size").toSize(); + return max.isEmpty() ? Layouting::Item::hardcodedMaximumSize + : max.boundedTo(Layouting::Item::hardcodedMaximumSize); } QSize maximumSize() const override @@ -89,7 +114,15 @@ public: QRect geometry() const override { - return {}; + if (isRootView()) { + if (QWindow *w = QQuickItem::window()) { + return w->geometry(); + } + } + + QRect r(QPoint(0, 0), QQuickItem::size().toSize()); + + return r; } QRect normalGeometry() const override @@ -104,25 +137,18 @@ public: void setGeometry(QRect) override; - void setMaximumSize(QSize) override + void setMaximumSize(QSize sz) override { + if (maximumSize() != sz) { + setProperty("kddockwidgets_max_size", sz); + updateGeometry(); + } } - bool isVisible() const override - { - return QQuickItem::isVisible(); - } + bool isVisible() const override; + void setVisible(bool is) override; - void setVisible(bool is) override - { - QQuickItem::setVisible(is); - } - - void move(int x, int y) override - { - Q_UNUSED(x) - Q_UNUSED(y) - } + void move(int x, int y) override; void move(QPoint) override { @@ -163,6 +189,11 @@ public: setVisible(false); } + void updateGeometry() + { + Q_EMIT geometryUpdated(); + } + // TODOv2: Check if this is even called from controllers void update() override { @@ -186,14 +217,30 @@ public: void raiseAndActivate() override { + if (QWindow *w = QQuickItem::window()) { + w->raise(); + w->requestActivate(); + } } void activateWindow() override { + if (QWindow *w = QQuickItem::window()) + w->requestActivate(); } void raise() override { + if (isRootView()) { + if (QWindow *w = QQuickItem::window()) + w->raise(); + } else if (auto p = QQuickItem::parentItem()) { + // It's not a top-level, so just increase its Z-order + const auto siblings = p->childItems(); + QQuickItem *last = siblings.last(); + if (last != this) + stackAfter(last); + } } QVariant property(const char *name) const override @@ -203,7 +250,16 @@ public: bool isRootView() const override { - return {}; + QQuickItem *parent = parentItem(); + if (!parent) + return true; + + if (QQuickView *w = quickView()) { + if (parent == w->contentItem() || parent == w->rootObject()) + return true; + } + + return false; } QQuickView *quickView() const @@ -260,10 +316,7 @@ public: return {}; } - bool close() override - { - return {}; - } + bool close() override; void setFlag(Qt::WindowType f, bool on = true) override { @@ -292,39 +345,60 @@ public: return m_windowFlags; } - void setWindowTitle(const QString &) override + void setWindowTitle(const QString &title) override { + if (QWindow *w = QQuickItem::window()) + w->setTitle(title); } - void setWindowIcon(const QIcon &) override + void setWindowIcon(const QIcon &icon) override { + if (QWindow *w = QQuickItem::window()) + w->setIcon(icon); } bool isActiveWindow() const override { - return {}; + if (QWindow *w = QQuickItem::window()) + return w->isActive(); + + return false; } - void showNormal() override + Q_INVOKABLE void redirectMouseEvents(QObject *from); + + Q_INVOKABLE void showNormal() override { + if (QWindow *w = QQuickItem::window()) + w->showNormal(); } - void showMinimized() override + Q_INVOKABLE void showMinimized() override { + if (QWindow *w = QQuickItem::window()) + w->showMinimized(); } - void showMaximized() override + Q_INVOKABLE void showMaximized() override { + if (QWindow *w = QQuickItem::window()) + w->showMaximized(); } bool isMinimized() const override { - return {}; + if (QWindow *w = QQuickItem::window()) + return w->windowStates() & Qt::WindowMinimized; + + return false; } bool isMaximized() const override { - return {}; + if (QWindow *w = QQuickItem::window()) + return w->windowStates() & Qt::WindowMaximized; + + return false; } std::shared_ptr windowHandle() const override @@ -394,7 +468,10 @@ public: QScreen *screen() const override { - return {}; + if (QWindow *w = QQuickItem::window()) + return w->screen(); + + return nullptr; } void setFocus(Qt::FocusReason reason) override @@ -418,13 +495,16 @@ public: return QQuickItem::objectName(); } - void setMinimumSize(QSize) override + void setMinimumSize(QSize sz) override { + if (minSize() != sz) { + setProperty("kddockwidgets_min_size", sz); + updateGeometry(); + } } void render(QPainter *) override { - // TODOv2 qWarning() << Q_FUNC_INFO << "Implement me"; } @@ -490,12 +570,34 @@ public: anchors->setProperty("fill", QVariant::fromValue(parentItem)); } -protected: - bool event(QEvent *e) override + void onWindowStateChangeEvent(QWindowStateChangeEvent *) { - return QQuickItem::event(e); + if (QWindow *window = QQuickItem::window()) { + m_oldWindowState = window->windowState(); + } } + /// @brief Convenience to create a QQuickItem + static QQuickItem *createItem(QQmlEngine *engine, const QString &filename); + +Q_SIGNALS: + void geometryUpdated(); // similar to QLayout stuff, when size constraints change + void itemGeometryChanged(); // emitted when the geometry changes. QQuickItem::geometryChanged() + // isn't a signal, so prefixed item + +protected: + void QQUICKITEMgeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override; + bool eventFilter(QObject *watched, QEvent *ev) override; + bool event(QEvent *) override; + + // TODOv2: Port these + void onCloseEvent(QCloseEvent *) + { + } + void onMoveEvent(QMoveEvent *); + void onResizeEvent(QResizeEvent *); + private: Q_DISABLE_COPY(View_qtquick) bool m_inSetParent = false; @@ -509,6 +611,22 @@ private: bool m_isWrapper = false; // TODOv2: What's this about QRect m_normalGeometry; Qt::WindowStates m_oldWindowState = Qt::WindowState::WindowNoState; + MouseEventRedirector *m_mouseEventRedirector = nullptr; }; +inline qreal logicalDpiFactor(const QQuickItem *item) +{ +#ifndef Q_OS_MACOS + if (QQuickWindow *window = item->window()) { + if (QScreen *s = window->screen()) { + return s->logicalDotsPerInch() / 96.0; + } + } +#endif + + // It's always 72 on mac + Q_UNUSED(item); + return 1; +} + } // namespace KDDockWidgets::Views diff --git a/src/qtquick/views/old/QWidgetAdapter_quick.cpp b/src/qtquick/views/old/QWidgetAdapter_quick.cpp deleted file mode 100644 index 51a5d994..00000000 --- a/src/qtquick/views/old/QWidgetAdapter_quick.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* - This file is part of KDDockWidgets. - - SPDX-FileCopyrightText: 2019-2022 Klarälvdalens Datakonsult AB, a KDAB Group company - Author: Sérgio Martins - - SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only - - Contact KDAB at for commercial licensing options. -*/ - -/** - * @file - * @brief A class that is QWidget when building for QtWidgets, and QObject when building for QtQuick. - * - * Allows to have the same code base supporting both stacks. - * - * @author Sérgio Martins \ - */ - - -#include "MainWindow.h" - -#include "../DockRegistry_p.h" -#include "../Utils_p.h" -#include "../FloatingWindow_p.h" -#include "../multisplitter/Item_p.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace KDDockWidgets; - -namespace KDDockWidgets { - -/** - * @brief Event filter which redirects mouse events from one QObject to another. - * Needed for QtQuick to redirect the events from MouseArea to our KDDW classes which derive from Draggable. - * For QtWidgets it's not needed, as the Draggables are QWidgets themselves. - */ -class MouseEventRedirector : public QObject -{ - Q_OBJECT -public: - explicit MouseEventRedirector(QObject *eventSource, QObject *eventTarget) - : QObject(eventTarget) - , m_eventSource(eventSource) - , m_eventTarget(eventTarget) - { - eventSource->installEventFilter(this); - - // Each source can only have one MouseEventRedirector - auto oldRedirector = s_mouseEventRedirectors.take(eventSource); - if (oldRedirector) { - eventSource->removeEventFilter(oldRedirector); - oldRedirector->deleteLater(); - } - - s_mouseEventRedirectors.insert(eventSource, this); - } - - static MouseEventRedirector *redirectorForSource(QObject *eventSource) - { - return s_mouseEventRedirectors.value(eventSource); - } - - ~MouseEventRedirector() override; - - bool eventFilter(QObject *source, QEvent *ev) override - { - QMouseEvent *me = mouseEvent(ev); - if (!me) - return false; - - // MouseArea.enable is different from Item.enabled. The former still lets the events - // go through event loops. So query MouseArea.enable here and bail out if false. - const QVariant v = source->property("enabled"); - if (v.isValid() && !v.toBool()) - return false; - - // Finally send the event - m_eventTarget->setProperty("cursorPosition", m_eventSource->property("cursorPosition")); - qApp->sendEvent(m_eventTarget, me); - m_eventTarget->setProperty("cursorPosition", CursorPosition_Undefined); - - return false; - } - - QObject *const m_eventSource; - QObject *const m_eventTarget; - static QHash s_mouseEventRedirectors; -}; - -QHash MouseEventRedirector::s_mouseEventRedirectors = {}; - -MouseEventRedirector::~MouseEventRedirector() -{ - s_mouseEventRedirectors.remove(m_eventSource); -} - -} - -static bool flagsAreTopLevelFlags(Qt::WindowFlags flags) -{ - return flags & (Qt::Window | Qt::Tool); -} - -static QQuickItem *actualParentItem(QQuickItem *candidateParentItem, Qt::WindowFlags flags) -{ - // When we have a top-level, such as FloatingWindow, we only want to set QObject parentship - // and not parentItem. - return flagsAreTopLevelFlags(flags) ? nullptr - : candidateParentItem; -} - -QWidgetAdapter::QWidgetAdapter(QQuickItem *parent, Qt::WindowFlags flags) - : QQuickItem(actualParentItem(parent, flags)) - , m_windowFlags(flags) -{ - if (parent && flagsAreTopLevelFlags(flags)) { - // See comment in actualParentItem(). We set only the QObject parent. Mimics QWidget behaviour - QObject::setParent(parent); - } - - connect(this, &QQuickItem::widthChanged, this, [this] { - onResize(size()); - updateGeometry(); - }); - - connect(this, &QQuickItem::heightChanged, this, [this] { - if (!m_windowIsBeingDestroyed) { // If Window is being destroyed we don't bother - onResize(size()); - updateGeometry(); - } - }); - - qApp->installEventFilter(this); - - setSize(QSize(800, 800)); -} - -void QWidgetAdapter::raiseAndActivate() -{ - if (QWindow *w = windowHandle()) { - w->raise(); - w->requestActivate(); - } -} - -void QWidgetAdapter::setWindowOpacity(qreal level) -{ - if (QWindow *w = windowHandle()) - w->setOpacity(level); -} - -bool QWidgetAdapter::onResize(QSize) -{ - return false; -} -void QWidgetAdapter::onLayoutRequest() -{ -} -void QWidgetAdapter::onMousePress() -{ -} -void QWidgetAdapter::onMouseMove(QPoint) -{ -} -void QWidgetAdapter::onMouseRelease() -{ -} -void QWidgetAdapter::onCloseEvent(QCloseEvent *) -{ -} -void QWidgetAdapter::onResizeEvent(QResizeEvent *event) -{ - QWindow *window = windowHandle(); - if (!window) { - return; - } - - if (isNormalWindowState(m_oldWindowState)) { - QRect geo = normalGeometry(); - - auto curState = window->windowState(); - if (isNormalWindowState(curState)) { - geo.setSize(event->size()); - } else { - geo.setSize(event->oldSize()); - } - - setNormalGeometry(geo); - } -} -void QWidgetAdapter::onMoveEvent(QMoveEvent *event) -{ - QWindow *window = windowHandle(); - if (!window) { - return; - } - - if (isNormalWindowState(m_oldWindowState)) { - QRect geo = normalGeometry(); - - auto windowCurrentState = window->windowState(); - if (isNormalWindowState(windowCurrentState)) { - geo.moveTopLeft(event->pos()); - } else { - geo.moveTopLeft(event->oldPos()); - } - - setNormalGeometry(geo); - } -} -void QWidgetAdapter::onWindowStateChangeEvent(QWindowStateChangeEvent *) -{ - QWindow *window = windowHandle(); - if (!window) { - return; - } - - m_oldWindowState = window->windowState(); -} - -void QWidgetAdapter::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) -{ - QQuickItem::itemChange(change, data); - - // Emulate the QWidget behaviour as QQuickItem doesn't receive some QEvents. - switch (change) { - case QQuickItem::ItemParentHasChanged: { - QEvent ev(QEvent::ParentChange); - qApp->sendEvent(this, &ev); // Not calling event() directly, otherwise it would skip event filters - Q_EMIT parentChanged(this); - break; - } - case QQuickItem::ItemVisibleHasChanged: { - if (m_inSetParent) { - // Setting parent to nullptr will emit visible true in QtQuick - // which we don't want, as we're going to hide it (as we do with QtWidgets) - break; - } - - QEvent ev(isVisible() ? QEvent::Show : QEvent::Hide); - event(&ev); - break; - } - default: - break; - } -} - -void QWidgetAdapter::QQUICKITEMgeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - // Send a few events manually, since QQuickItem doesn't do it for us. - QQuickItem::QQUICKITEMgeometryChanged(newGeometry, oldGeometry); - - // Not calling event() directly, otherwise it would skip event filters - - if (newGeometry.size() != oldGeometry.size()) { - QEvent ev(QEvent::Resize); - qApp->sendEvent(this, &ev); - } - - if (newGeometry.topLeft() != oldGeometry.topLeft()) { - QEvent ev(QEvent::Move); - qApp->sendEvent(this, &ev); - } - - Q_EMIT itemGeometryChanged(); -} - -void QWidgetAdapter::raise() -{ - if (isTopLevel()) { - if (QWindow *w = windowHandle()) - w->raise(); - } else if (auto p = QQuickItem::parentItem()) { - // It's not a top-level, so just increase its Z-order - const auto siblings = p->childItems(); - QQuickItem *last = siblings.last(); - if (last != this) - stackAfter(last); - } -} - -QSize QWidgetAdapter::minimumSize() const -{ - if (m_isWrapper) { - const auto children = childItems(); - if (!children.isEmpty()) { - const QSize min = children.constFirst()->property("kddockwidgets_min_size").toSize(); - return min.expandedTo(Layouting::Item::hardcodedMinimumSize); - } - } - - const QSize min = property("kddockwidgets_min_size").toSize(); - return min.expandedTo(Layouting::Item::hardcodedMinimumSize); -} - -QSize QWidgetAdapter::maximumSize() const -{ - if (m_isWrapper) { - const auto children = childItems(); - if (!children.isEmpty()) { - const QSize max = children.constFirst()->property("kddockwidgets_max_size").toSize(); - return max.isEmpty() ? Layouting::Item::hardcodedMaximumSize - : max.boundedTo(Layouting::Item::hardcodedMaximumSize); - } - } - - const QSize max = property("kddockwidgets_max_size").toSize(); - return max.isEmpty() ? Layouting::Item::hardcodedMaximumSize - : max.boundedTo(Layouting::Item::hardcodedMaximumSize); -} - -WId QWidgetAdapter::winId() const -{ - if (QWindow *w = windowHandle()) - return w->winId(); - - return WId(-1); -} - -FloatingWindow *QWidgetAdapter::floatingWindow() const -{ - if (auto fw = qobject_cast(window())) - return fw; - - return nullptr; -} - -QRect QWidgetAdapter::geometry() const -{ - if (isTopLevel()) { - if (QWindow *w = windowHandle()) { - return w->geometry(); - } - } - - return KDDockWidgets::Private::geometry(this); -} - -QRect QWidgetAdapter::rect() const -{ - return QRectF(0, 0, width(), height()).toRect(); -} - -QPoint QWidgetAdapter::pos() const -{ - return geometry().topLeft(); -} - - -QRect QWidgetAdapter::frameGeometry() const -{ - if (QWindow *w = windowHandle()) - return w->frameGeometry(); - - return geometry(); -} - - -void QWidgetAdapter::setMinimumSize(QSize sz) -{ - if (minimumSize() != sz) { - setProperty("kddockwidgets_min_size", sz); - updateGeometry(); - } -} - -void QWidgetAdapter::setMaximumSize(QSize sz) -{ - if (maximumSize() != sz) { - setProperty("kddockwidgets_max_size", sz); - updateGeometry(); - } -} - -void QWidgetAdapter::updateGeometry() -{ - Q_EMIT geometryUpdated(); -} - -bool QWidgetAdapter::isWindow() const -{ - QQuickItem *parent = parentItem(); - if (!parent) - return true; - - if (QQuickView *w = quickView()) { - if (parent == w->contentItem() || parent == w->rootObject()) - return true; - } - - return false; -} - -bool QWidgetAdapter::isMaximized() const -{ - if (QWindow *w = windowHandle()) - return w->windowStates() & Qt::WindowMaximized; - - return false; -} - -bool QWidgetAdapter::isMinimized() const -{ - if (QWindow *w = windowHandle()) - return w->windowStates() & Qt::WindowMinimized; - - return false; -} - -bool KDDockWidgets::QWidgetAdapter::isActiveWindow() const -{ - if (QWindow *w = windowHandle()) - return w->isActive(); - - return false; -} - -void QWidgetAdapter::showMaximized() -{ - if (QWindow *w = windowHandle()) - w->showMaximized(); -} - -void QWidgetAdapter::showMinimized() -{ - if (QWindow *w = windowHandle()) - w->showMinimized(); -} - -void QWidgetAdapter::showNormal() -{ - if (QWindow *w = windowHandle()) - w->showNormal(); -} - -QWindow *QWidgetAdapter::windowHandle() const -{ - return QQuickItem::window(); -} - -QWidgetAdapter *QWidgetAdapter::window() const -{ - // We return the top-most QWidgetAdapter - - if (QWidgetAdapter *w = parentWidget(/*includeTransient =*/false)) - return w->window(); - - return const_cast(this); -} - -QWidgetAdapter *QWidgetAdapter::parentWidget(bool includeTransient) const -{ - QQuickItem *p = parentItem(); - while (p) { - if (auto qa = qobject_cast(p)) - return qa; - - p = p->parentItem(); - } - - if (includeTransient) { - if (QQuickView *w = quickView()) { - // Here we're mimicking QWidget::parentWidget(), which can return the transient parent of the QWindow. - MainWindowBase *mw = DockRegistry::self()->mainWindowForHandle(w->transientParent()); - if (mw) - return mw; - } - } - - - return nullptr; -} - - -void QWidgetAdapter::setWindowTitle(const QString &title) -{ - if (QWindow *window = windowHandle()) - window->setTitle(title); -} - -void QWidgetAdapter::setWindowIcon(const QIcon &icon) -{ - if (QWindow *window = windowHandle()) - window->setIcon(icon); -} - -bool QWidgetAdapter::close() -{ - QCloseEvent ev; - onCloseEvent(&ev); - - if (ev.isAccepted()) { - setVisible(false); - return true; - } - - return false; -} - - -void QWidgetAdapter::move(int x, int y) -{ - if (isTopLevel()) { - if (QWindow *w = windowHandle()) { - w->setPosition(x, y); - return; - } - } - - setX(x); - setY(y); - setAttribute(Qt::WA_Moved); -} - -void QWidgetAdapter::activateWindow() -{ - if (QWindow *w = windowHandle()) - w->requestActivate(); -} - -bool QWidgetAdapter::hasFocus() const -{ - return hasActiveFocus(); -} - -/** static */ -QQuickItem *QWidgetAdapter::createItem(QQmlEngine *engine, const QString &filename) -{ - QQmlComponent component(engine, filename); - QObject *obj = component.create(); - if (!obj) { - qWarning() << Q_FUNC_INFO << component.errorString(); - return nullptr; - } - - return qobject_cast(obj); -} - - -bool QWidgetAdapter::event(QEvent *ev) -{ - if (ev->type() == QEvent::Close) - onCloseEvent(static_cast(ev)); - - return QQuickItem::event(ev); -} - -bool QWidgetAdapter::eventFilter(QObject *watched, QEvent *ev) -{ - if (qobject_cast(watched)) { - if (m_mouseTrackingEnabled) { - switch (ev->type()) { - case QEvent::MouseMove: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - ev->ignore(); - qApp->sendEvent(this, ev); - // qDebug() << "Mouse event" << ev; - if (ev->isAccepted()) - return true; - break; - default: - break; - } - } - - if (ev->type() == QEvent::Resize) { - onResizeEvent(static_cast(ev)); - } else if (ev->type() == QEvent::Move) { - onMoveEvent(static_cast(ev)); - } else if (ev->type() == QEvent::WindowStateChange) { - onWindowStateChangeEvent(static_cast(ev)); - } - } - - return QQuickItem::eventFilter(watched, ev); -} - -QScreen *QWidgetAdapter::screen() const -{ - if (QQuickView *w = quickView()) { - return w->screen(); - } - - return nullptr; -} - -QQuickItem *KDDockWidgets::Private::widgetForWindow(QWindow *window) -{ - if (!window) - return nullptr; - - return window->property("kddockwidgets_qwidget").value(); -} - -void QWidgetAdapter::redirectMouseEvents(QObject *source) -{ - if (auto existingRedirector = MouseEventRedirector::redirectorForSource(source)) { - if (existingRedirector->m_eventTarget == this) { - // Nothing to do. The specified event source is already redirecting to this instance - return; - } - } - - new MouseEventRedirector(source, this); -} diff --git a/src/qtquick/views/old/QWidgetAdapter_quick_p.h b/src/qtquick/views/old/QWidgetAdapter_quick_p.h deleted file mode 100644 index 954df389..00000000 --- a/src/qtquick/views/old/QWidgetAdapter_quick_p.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - This file is part of KDDockWidgets. - - SPDX-FileCopyrightText: 2019-2022 Klarälvdalens Datakonsult AB, a KDAB Group company - Author: Sérgio Martins - - SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only - - Contact KDAB at for commercial licensing options. -*/ - -/** - * @file - * @brief A class that is QWidget when building for QtWidgets, and QObject when building for QtQuick. - * - * Allows to have the same code base supporting both stacks. - * - * @author Sérgio Martins \ - */ - -#ifndef KDDOCKWIDGETS_QWIDGETADAPTERQUICK_P_H -#define KDDOCKWIDGETS_QWIDGETADAPTERQUICK_P_H - -#include "kddockwidgets/docks_export.h" -#include "kddockwidgets/Qt5Qt6Compat_p.h" - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QWindow; -class QQmlEngine; -class QQuickView; -QT_END_NAMESPACE - -namespace KDDockWidgets { - -class MouseEventRedirector; - -namespace Private { - -DOCKS_EXPORT QQuickItem *widgetForWindow(QWindow *window); - -/// @brief Helper since QQuickItem::parentItem() has a different name than QWidget::parentWidget() -inline QQuickItem *parentWidget(QQuickItem *item) -{ - return item ? item->parentItem() : nullptr; -} - -inline bool isMinimized(const QQuickItem *item) -{ - QWindow *window = item ? item->window() : nullptr; - return KDDockWidgets::Private::isMinimized(window); -} - -inline QRect geometry(const QQuickItem *item) -{ - QRect r(QPoint(0, 0), item->size().toSize()); - r.moveTopLeft(QPointF(item->x(), item->y()).toPoint()); - return r; -} - -inline QRect parentGeometry(const QQuickItem *item) -{ - if (!item || !item->parentItem()) - return QRect(); - - - return geometry(item->parentItem()); -} - -inline QWindow *windowForWidget(const QQuickItem *item) -{ - return item ? item->window() : nullptr; -} - -/// @brief sets the geometry on the QWindow containing the specified item -inline void setTopLevelGeometry(QRect geometry, const QQuickItem *item) -{ - if (!item) - return; - - if (QWindow *window = item->window()) - window->setGeometry(geometry); -} - -} // namespace Private - -class FloatingWindow; -class DOCKS_EXPORT QWidgetAdapter : public QQuickItem -{ - Q_OBJECT -public: - Q_INVOKABLE void showMaximized(); - Q_INVOKABLE void showMinimized(); - Q_INVOKABLE void showNormal(); - Q_INVOKABLE void redirectMouseEvents(QObject *from); - - -Q_SIGNALS: - void geometryUpdated(); // similar to QLayout stuff, when size constraints change - void itemGeometryChanged(); // emitted when the geometry changes. QQuickItem::geometryChanged() - // isn't a signal, so prefixed item - -protected: - void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override; - -private: - MouseEventRedirector *m_mouseEventRedirector = nullptr; -}; - -inline qreal logicalDpiFactor(const QQuickItem *item) -{ -#ifndef Q_OS_MACOS - if (QQuickWindow *window = item->window()) { - if (QScreen *s = window->screen()) { - return s->logicalDotsPerInch() / 96.0; - } - } -#endif - - // It's always 72 on mac - Q_UNUSED(item); - return 1; -} - -} - -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cf6af516..b35f96ca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -58,5 +58,5 @@ target_link_libraries(tests_launcher Qt${Qt_VERSION_MAJOR}::Core) add_executable(tst_viewguard tst_viewguard.cpp) target_link_libraries(tst_viewguard kddockwidgets) -add_executable(tst_view tst_view.cpp) +add_executable(tst_view tst_view.cpp ${TESTING_RESOURCES}) target_link_libraries(tst_view kddockwidgets)