diff --git a/examples/dockwidgets/MyFrameworkWidgetFactory.cpp b/examples/dockwidgets/MyFrameworkWidgetFactory.cpp index a684b6ad..95d71d0e 100644 --- a/examples/dockwidgets/MyFrameworkWidgetFactory.cpp +++ b/examples/dockwidgets/MyFrameworkWidgetFactory.cpp @@ -65,8 +65,7 @@ public: class MySeparator : public KDDockWidgets::SeparatorWidget { public: - explicit MySeparator(KDDockWidgets::Anchor *anchor, - KDDockWidgets::QWidgetAdapter *parent = nullptr) + explicit MySeparator(KDDockWidgets::QWidgetAdapter *parent = nullptr) : KDDockWidgets::SeparatorWidget(anchor, parent) { } @@ -89,8 +88,7 @@ KDDockWidgets::TitleBar * CustomWidgetFactory::createTitleBar(KDDockWidgets::Flo return new MyTitleBar(fw); } /* -KDDockWidgets::Separator * CustomWidgetFactory::createSeparator(KDDockWidgets::Anchor *anchor, - KDDockWidgets::QWidgetAdapter *parent) const +KDDockWidgets::Separator * CustomWidgetFactory::createSeparator(KDDockWidgets::QWidgetAdapter *parent) const { return new MySeparator(anchor, parent); } diff --git a/src/Config.cpp b/src/Config.cpp index ea19e0ac..d5241491 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -28,7 +28,7 @@ #include "Config.h" #include "DockRegistry_p.h" #include "FrameworkWidgetFactory.h" -#include "Anchor_p.h" +#include "Separator_p.h" #include #include @@ -70,11 +70,11 @@ Config::Config() d->fixFlags(); // stuff in multisplitter/ can't include the framework widget factory, so set it here - auto separatorCreator = [](Layouting::Anchor *a, QWidget *parent){ - return Config::self().frameworkWidgetFactory()->createSeparator(a, parent); + auto separatorCreator = [](QWidget *parent){ + return Config::self().frameworkWidgetFactory()->createSeparator(parent); }; - Layouting::Anchor::setSeparatorFactoryFunc(separatorCreator); + Layouting::Separator::setSeparatorFactoryFunc(separatorCreator); } Config& Config::self() diff --git a/src/FrameworkWidgetFactory.cpp b/src/FrameworkWidgetFactory.cpp index 9b3062a9..091b4011 100644 --- a/src/FrameworkWidgetFactory.cpp +++ b/src/FrameworkWidgetFactory.cpp @@ -78,9 +78,9 @@ TabWidget *DefaultWidgetFactory::createTabWidget(Frame *parent) const return new TabWidgetWidget(parent); } -Layouting::Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidget *parent) const +Layouting::Separator *DefaultWidgetFactory::createSeparator(QWidget *parent) const { - return new SeparatorWidget(anchor, parent); + return new SeparatorWidget(parent); } FloatingWindow *DefaultWidgetFactory::createFloatingWindow(MainWindowBase *parent) const @@ -124,9 +124,9 @@ TabWidget *DefaultWidgetFactory::createTabWidget(Frame *frame) const return new TabWidgetQuick(frame); } -Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidgetAdapter *parent) const +Separator *DefaultWidgetFactory::createSeparator(QWidgetAdapter *parent) const { - return new SeparatorQuick(anchor, parent); + return new SeparatorQuick(parent); } FloatingWindow *DefaultWidgetFactory::createFloatingWindow(QWidgetOrQuick *parent) const diff --git a/src/FrameworkWidgetFactory.h b/src/FrameworkWidgetFactory.h index def42c74..206d1b0c 100644 --- a/src/FrameworkWidgetFactory.h +++ b/src/FrameworkWidgetFactory.h @@ -33,7 +33,6 @@ */ namespace Layouting { -class Anchor; class Separator; } @@ -106,7 +105,7 @@ public: /// the user to resize nested dock widgets. ///@param anchor Just forward to Sepataror's constructor. ///@param parent Just forward to Separator's constructor. - virtual Layouting::Separator* createSeparator(Layouting::Anchor *anchor, QWidget *parent = nullptr) const = 0; + virtual Layouting::Separator* createSeparator(QWidget *parent = nullptr) const = 0; ///@brief Called internally by the framework to create a FloatingWindow /// Override to provide your own FloatingWindow sub-class. If overridden then @@ -138,7 +137,7 @@ public: TitleBar *createTitleBar(FloatingWindow *) const override; TabBar *createTabBar(TabWidget *parent) const override; TabWidget *createTabWidget(Frame *parent) const override; - Layouting::Separator *createSeparator(Layouting::Anchor *anchor, QWidget *parent = nullptr) const override; + Layouting::Separator *createSeparator(QWidget *parent = nullptr) const override; FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override; FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override; DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override; diff --git a/src/private/DockRegistry_p.h b/src/private/DockRegistry_p.h index b9fd891b..27d362c0 100644 --- a/src/private/DockRegistry_p.h +++ b/src/private/DockRegistry_p.h @@ -99,13 +99,13 @@ public: QVector topLevels(bool excludeFloatingDocks = false) const; /** - * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors. + * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Separators. * This is called before restoring a layout. */ void clear(); /** - * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors with the specified affinities. + * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Separators with the specified affinities. */ void clear(QStringList affinities); diff --git a/src/private/multisplitter/Anchor.cpp b/src/private/multisplitter/Anchor.cpp deleted file mode 100644 index c169adcb..00000000 --- a/src/private/multisplitter/Anchor.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - This file is part of KDDockWidgets. - - Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com - Author: Sérgio Martins - - 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) any later version. - - 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 "Anchor_p.h" -#include "Logging_p.h" -#include "Separator_p.h" - -#include -#include -#include - -#ifdef Q_OS_WIN -# include -#endif - -using namespace KDDockWidgets; -using namespace Layouting; - -bool Anchor::s_isResizing = false; -Anchor* Anchor::s_separatorBeingDragged = nullptr; - -static SeparatorFactoryFunc s_separatorFactoryFunc = nullptr; - -static Separator* createSeparator(Anchor *a, QWidget *parent) -{ - if (s_separatorFactoryFunc) - return s_separatorFactoryFunc(a, parent); - - return new Separator(a, parent); -} - -Anchor::Anchor(ItemContainer *parentContainer, Qt::Orientation orientation, - Options options, QWidget *hostWidget) - : QObject(hostWidget) - , m_orientation(orientation) - , m_hostWidget(hostWidget) - , m_separatorWidget(createSeparator(this, m_hostWidget)) - , m_options(options) - , m_lazyResizeRubberBand((options & Option::LazyResize) ? new QRubberBand(QRubberBand::Line, hostWidget) : nullptr) - , m_parentContainer(parentContainer) -{ - connect(this, &QObject::objectNameChanged, m_separatorWidget, &QObject::setObjectName); -} - -Anchor::~Anchor() -{ - delete m_separatorWidget; - if (s_separatorBeingDragged == this) - s_separatorBeingDragged = nullptr; -} - -QWidget *Anchor::hostWidget() const -{ - return m_hostWidget; -} - -void Anchor::setGeometry(QRect r) -{ - if (r != m_geometry) { - if (position() < 0) { - qCDebug(separators) << Q_FUNC_INFO << "Old position was negative" << position() << "; new=" << r; - } - - m_geometry = r; - m_separatorWidget->setGeometry(r); - m_separatorWidget->setVisible(true); - Q_EMIT geometryChanged(r); - } -} - -bool Anchor::isVertical() const -{ - return m_orientation == Qt::Vertical; -} - -Qt::Orientation Anchor::orientation() const -{ - return m_orientation; -} - -void Anchor::setGeometry(int pos, int pos2, int length) -{ - QRect newGeo = m_geometry; - if (isVertical()) { - // The separator itself is horizontal - newGeo.setSize(QSize(length, Item::separatorThickness())); - newGeo.moveTo(pos2, pos); - } else { - // The separator itself is vertical - newGeo.setSize(QSize(Item::separatorThickness(), length)); - newGeo.moveTo(pos, pos2); - } - - setGeometry(newGeo); -} - -int Anchor::position() const -{ - const QPoint topLeft = m_geometry.topLeft(); - return isVertical() ? topLeft.y() : topLeft.x(); -} - -bool Anchor::isBeingDragged() const -{ - return s_separatorBeingDragged == this; -} - -bool Anchor::lazyResizeEnabled() const -{ - return m_options & Option::LazyResize; -} - -Separator *Anchor::separatorWidget() const -{ - return m_separatorWidget; -} - -void Anchor::setLazyPosition(int pos) -{ - if (m_lazyPosition != pos) { - m_lazyPosition = pos; - - QRect geo = m_separatorWidget->geometry(); - if (isVertical()) { - geo.moveLeft(pos); - } else { - geo.moveTop(pos); - } - - m_lazyResizeRubberBand->setGeometry(geo); - } -} - -int Anchor::position(QPoint p) const -{ - return isVertical() ? p.y() : p.x(); -} - -void Anchor::setPosition(int p) -{ - QRect geo = m_geometry; - QPoint pt = geo.topLeft(); - if (isVertical()) - pt.setY(p); - else - pt.setX(p); - - geo.moveTopLeft(pt); - setGeometry(geo); -} - -void Anchor::onMousePress() -{ - s_separatorBeingDragged = this; - - qCDebug(separators) << "Drag started"; - - if (lazyResizeEnabled()) { - setLazyPosition(position()); - m_lazyResizeRubberBand->show(); - } -} - -void Anchor::onMouseReleased() -{ - if (m_lazyResizeRubberBand) { - m_lazyResizeRubberBand->hide(); - setPosition(m_lazyPosition); - } - - s_separatorBeingDragged = nullptr; -} - -void Anchor::onMouseMoved(QPoint pt) -{ - if (!isBeingDragged()) - return; - - if (!(qApp->mouseButtons() & Qt::LeftButton)) { - qCDebug(separators) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent"; - onMouseReleased(); - return; - } - -#ifdef Q_OS_WIN - // Try harder, Qt can be wrong, if mixed with MFC - const bool mouseButtonIsReallyDown = (GetKeyState(VK_LBUTTON) & 0x8000) || (GetKeyState(VK_RBUTTON) & 0x8000); - if (!mouseButtonIsReallyDown) { - qCDebug(mouseevents) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent"; - onMouseReleased(); - return; - } -#endif - - const int positionToGoTo = position(pt); - const int minPos = m_parentContainer->minPosForSeparator_global(this); - const int maxPos = m_parentContainer->maxPosForSeparator_global(this); - - qDebug() << "foo" << minPos << positionToGoTo << maxPos; - - if (positionToGoTo < minPos || positionToGoTo > maxPos) - return; - - m_lastMoveDirection = positionToGoTo < position() ? Side1 - : (positionToGoTo > position() ? Side2 - : Side2); // Last case shouldn't happen though. - - if (/*m_lazyResize*/ false) // TODO - setLazyPosition(positionToGoTo); - else - setPosition(positionToGoTo); -} - -void Anchor::onWidgetMoved(int p) -{ - if (!isResizing()) // We only care if it's being dragged by mouse - return; - - setPosition(p); -} - -QRect Anchor::geometry() const -{ - return m_geometry; -} - -bool Anchor::isResizing() -{ - return s_isResizing; -} - -void Anchor::setSeparatorFactoryFunc(SeparatorFactoryFunc func) -{ - s_separatorFactoryFunc = func; -} diff --git a/src/private/multisplitter/Anchor_p.h b/src/private/multisplitter/Anchor_p.h deleted file mode 100644 index 622eff16..00000000 --- a/src/private/multisplitter/Anchor_p.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - This file is part of KDDockWidgets. - - Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com - Author: Sérgio Martins - - 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) any later version. - - 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 . -*/ - -#ifndef KD_MULTISPLITTER_ANCHOR_P_H -#define KD_MULTISPLITTER_ANCHOR_P_H - -#include "Item_p.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QRubberBand; -QT_END_NAMESPACE - -namespace KDDockWidgets { -class MultiSplitterLayout; -} - -namespace Layouting { - -class Anchor : public QObject // clazy:exclude=ctor-missing-parent-argument -{ - Q_OBJECT - - Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) - Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT) -public: - - enum class Option { - None = 0, - LazyResize - }; - Q_DECLARE_FLAGS(Options, Option); - - typedef QVector List; - explicit Anchor(ItemContainer *parentContainer, Qt::Orientation orientation, - Options options, QWidget *hostWidget); - - ~Anchor() override; - - QWidget *hostWidget() const; - - Qt::Orientation orientation() const; - void setGeometry(int pos, int pos2, int length); - int position() const; - - ///@brief returns the separator widget - Separator* separatorWidget() const; - - void setPositionOffset(int); - bool isBeingDragged() const; - - bool lazyResizeEnabled() const; - - void onMousePress(); - void onMouseReleased(); - void onMouseMoved(QPoint pt); - void onWidgetMoved(int p); - - QRect geometry() const; - bool isVertical() const; - - ///@brief Returns whether we're dragging a separator. Can be useful for the app to stop other work while we're not in the final size - static bool isResizing(); - static void setSeparatorFactoryFunc(SeparatorFactoryFunc); - -Q_SIGNALS: - void geometryChanged(QRect); - -private: - void setLazyPosition(int); - void setGeometry(QRect); - int position(QPoint) const; - void setPosition(int p); - - const Qt::Orientation m_orientation; - - // Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise. - Layouting::Side m_lastMoveDirection = Side1; - - QWidget *const m_hostWidget; - static bool s_isResizing; - static Anchor* s_separatorBeingDragged; - - Separator *const m_separatorWidget; - QRect m_geometry; - int m_lazyPosition = 0; - const Options m_options; - QRubberBand *const m_lazyResizeRubberBand; - ItemContainer *const m_parentContainer; -}; - -} - -#endif diff --git a/src/private/multisplitter/CMakeLists.txt b/src/private/multisplitter/CMakeLists.txt index b12f2483..04e8a7bd 100644 --- a/src/private/multisplitter/CMakeLists.txt +++ b/src/private/multisplitter/CMakeLists.txt @@ -1,6 +1,4 @@ set(MULTISPLITTER_SRCS - Anchor.cpp - Anchor_p.h Item.cpp Item_p.h Logging.cpp diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index faf776eb..a680a2d7 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -19,7 +19,7 @@ */ #include "Item_p.h" -#include "Anchor_p.h" +#include "Separator_p.h" #include #include @@ -859,7 +859,7 @@ bool ItemContainer::checkSanity() const int pos2 = Layouting::pos(mapToRoot(QPoint(0, 0)), oppositeOrientation(m_orientation)); for (int i = 0; i < m_separators.size(); ++i) { - Anchor *separator = m_separators.at(i); + Separator *separator = m_separators.at(i); Item *item = visibleChildren.at(i); const int expectedSeparatorPos = mapToRoot(item->m_sizingInfo.edge(m_orientation) + 1, m_orientation); @@ -2024,10 +2024,10 @@ QSize ItemContainer::missingSizeFor(Item *item, Qt::Orientation o) const { QSize missing = {0, 0}; const QSize available = availableSize(); - const int anchorWasteW = (o == Qt::Vertical || !hasVisibleChildren()) ? 0 : Item::separatorThickness(); - const int anchorWasteH = (o == Qt::Vertical && hasVisibleChildren()) ? Item::separatorThickness() : 0; - missing.setWidth(qMax(item->minSize().width() - available.width() + anchorWasteW, 0)); - missing.setHeight(qMax(item->minSize().height() - available.height() + anchorWasteH, 0)); + const int separatorWasteW = (o == Qt::Vertical || !hasVisibleChildren()) ? 0 : Item::separatorThickness(); + const int separatorWasteH = (o == Qt::Vertical && hasVisibleChildren()) ? Item::separatorThickness() : 0; + missing.setWidth(qMax(item->minSize().width() - available.width() + separatorWasteW, 0)); + missing.setHeight(qMax(item->minSize().height() - available.height() + separatorWasteH, 0)); return missing; } @@ -2318,20 +2318,21 @@ void ItemContainer::updateSeparators() // Instead of just creating N missing ones at the end of the list, let's minimize separators // having their position changed, to minimize flicker - Anchor::List newSeparators; + Separator::List newSeparators; newSeparators.reserve(numSeparators); const int pos2 = isVertical() ? mapToRoot(QPoint(0, 0)).x() : mapToRoot(QPoint(0, 0)).y(); for (int position : positions) { - Anchor *separator = separatorAt(position); + Separator *separator = separatorAt(position); if (separator) { // Already existing, reuse newSeparators.push_back(separator); m_separators.removeOne(separator); } else { - separator = new Anchor(this, m_orientation, Anchor::Option::None, hostWidget()); + separator = Separator::createSeparator(hostWidget()); + separator->init(this, m_orientation, SeparatorOption::None); newSeparators.push_back(separator); } separator->setGeometry(position, pos2, oppositeLength()); @@ -2373,9 +2374,9 @@ void ItemContainer::updateSeparators_recursive() } } -Anchor *ItemContainer::separatorAt(int p) const +Separator *ItemContainer::separatorAt(int p) const { - for (Anchor *separator : m_separators) { + for (Separator *separator : m_separators) { if (separator->position() == p) return separator; } @@ -2393,24 +2394,24 @@ bool ItemContainer::isHorizontal() const return m_orientation == Qt::Horizontal; } -int ItemContainer::indexOf(Anchor *separator) const +int ItemContainer::indexOf(Separator *separator) const { return m_separators.indexOf(separator); } -int ItemContainer::minPosForSeparator(Anchor *separator) const +int ItemContainer::minPosForSeparator(Separator *separator) const { const int globalMin = minPosForSeparator_global(separator); return mapFromRoot(globalMin, m_orientation); } -int ItemContainer::maxPosForSeparator(Anchor *separator) const +int ItemContainer::maxPosForSeparator(Separator *separator) const { const int globalMax = maxPosForSeparator_global(separator); return mapFromRoot(globalMax, m_orientation); } -int ItemContainer::minPosForSeparator_global(Anchor *separator) const +int ItemContainer::minPosForSeparator_global(Separator *separator) const { const int separatorIndex = indexOf(separator); Q_ASSERT(separatorIndex != -1); @@ -2423,7 +2424,7 @@ int ItemContainer::minPosForSeparator_global(Anchor *separator) const return separator->position() - available1; } -int ItemContainer::maxPosForSeparator_global(Anchor *separator) const +int ItemContainer::maxPosForSeparator_global(Separator *separator) const { const int separatorIndex = indexOf(separator); Q_ASSERT(separatorIndex != -1); @@ -2475,9 +2476,9 @@ void ItemContainer::fillFromVariantMap(const QVariantMap &map, } } -QVector ItemContainer::separators_recursive() const +QVector ItemContainer::separators_recursive() const { - Layouting::Anchor::List separators = m_separators; + Layouting::Separator::List separators = m_separators; for (Item *item : m_children) { if (auto c = item->asContainer()) diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index b61dbee1..7e090c98 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -39,11 +39,8 @@ namespace Layouting { class ItemContainer; class Item; -class Anchor; class Separator; -typedef Separator* (*SeparatorFactoryFunc)(Layouting::Anchor*, QWidget *parent); - enum Location { Location_None, Location_OnLeft, ///> Left docking location @@ -102,6 +99,12 @@ enum class GrowthStrategy { BothSidesEqually }; +enum class SeparatorOption { + None = 0, + LazyResize +}; +Q_DECLARE_FLAGS(SeparatorOptions, SeparatorOption); + inline Qt::Orientation oppositeOrientation(Qt::Orientation o) { return o == Qt::Vertical ? Qt::Horizontal : Qt::Vertical; @@ -539,11 +542,11 @@ public: bool isVertical() const; bool isHorizontal() const; - int indexOf(Anchor *) const; - int minPosForSeparator(Anchor *) const; - int maxPosForSeparator(Anchor *) const; - int minPosForSeparator_global(Anchor *) const; - int maxPosForSeparator_global(Anchor *) const; + int indexOf(Separator *) const; + int minPosForSeparator(Separator *) const; + int maxPosForSeparator(Separator *) const; + int minPosForSeparator_global(Separator *) const; + int maxPosForSeparator_global(Separator *) const; void deleteSeparators_recursive(); void updateSeparators_recursive(); @@ -559,7 +562,7 @@ public: Item::List m_children; bool m_isResizing = false; bool m_blockUpdatePercentages = false; - QVector separators_recursive() const; + QVector separators_recursive() const; Qt::Orientation m_orientation = Qt::Vertical; private: void updateWidgets_recursive(); @@ -567,10 +570,10 @@ private: QVector requiredSeparatorPositions() const; void updateSeparators(); void deleteSeparators(); - Anchor* separatorAt(int p) const; + Separator* separatorAt(int p) const; QVector childPercentages() const; mutable bool m_checkSanityScheduled = false; - QVector m_separators; + QVector m_separators; bool m_convertingItemToContainer = false; }; diff --git a/src/private/multisplitter/MultiSplitterLayout.cpp b/src/private/multisplitter/MultiSplitterLayout.cpp index ec2a7d3f..57b22ca8 100644 --- a/src/private/multisplitter/MultiSplitterLayout.cpp +++ b/src/private/multisplitter/MultiSplitterLayout.cpp @@ -247,12 +247,7 @@ int MultiSplitterLayout::placeholderCount() const return count() - visibleCount(); } -void MultiSplitterLayout::setAnchorBeingDragged(Anchor *anchor) -{ - m_anchorBeingDragged = anchor; -} - -Anchor::List MultiSplitterLayout::anchors() const +Separator::List MultiSplitterLayout::separators() const { return m_rootItem->separators_recursive(); } diff --git a/src/private/multisplitter/MultiSplitterLayout_p.h b/src/private/multisplitter/MultiSplitterLayout_p.h index 3969c90e..5bc39c6c 100644 --- a/src/private/multisplitter/MultiSplitterLayout_p.h +++ b/src/private/multisplitter/MultiSplitterLayout_p.h @@ -33,7 +33,7 @@ #define KD_MULTISPLITTER_LAYOUT_P_H #include "../Frame_p.h" -#include "Anchor_p.h" +#include "Separator_p.h" #include "docks_export.h" #include "KDDockWidgets.h" #include "Item_p.h" @@ -55,9 +55,6 @@ class DebugWindow; * * It supports adding a widget to the left/top/bottom/right of the whole MultiSplitter or adding * relative to a single widget. - * - * A MultiSplitter is simply a list of Anchors, each one of them handling the resizing of widgets. - * See the documentation for Anchor. */ class DOCKS_EXPORT_FOR_UNIT_TESTS MultiSplitterLayout : public QObject // clazy:exclude=ctor-missing-parent-argument { @@ -121,7 +118,7 @@ public: Layouting::Item *itemAt(QPoint p) const; /** - * @brief Removes all Items, Anchors and Frames docked in this layout. + * @brief Removes all Items, Separators and Frames docked in this layout. * DockWidgets are closed but not deleted. */ void clear(); @@ -167,7 +164,7 @@ public: /** * Called by the indicators, so they draw the drop rubber band at the correct place. * The rect for the rubberband when dropping a widget at the specified location. - * Excludes the Anchor thickness, result is actually smaller than what needed. In other words, + * Excludes the Separator thickness, result is actually smaller than what needed. In other words, * the result will be exactly the same as the geometry the widget will get. */ QRect rectForDrop(const QWidgetOrQuick *widget, KDDockWidgets::Location location, const Layouting::Item *relativeTo) const; @@ -175,12 +172,8 @@ public: bool deserialize(const LayoutSaver::MultiSplitterLayout &); LayoutSaver::MultiSplitterLayout serialize() const; - void setAnchorBeingDragged(Layouting::Anchor *); - Layouting::Anchor *anchorBeingDragged() const { return m_anchorBeingDragged; } - bool anchorIsBeingDragged() const { return m_anchorBeingDragged != nullptr; } - ///@brief returns list of separators - Layouting::Anchor::List anchors() const; + Layouting::Separator::List separators() const; /** * @brief Updates the min size of this layout. @@ -298,7 +291,7 @@ Q_SIGNALS: void minimumSizeChanged(QSize); public: - Layouting::Anchor::List anchors(Qt::Orientation, bool includeStatic = false, bool includePlaceholders = true) const; + Layouting::Separator::List separators(Qt::Orientation, bool includeStatic = false, bool includePlaceholders = true) const; private: friend class TestDocks; friend class KDDockWidgets::Debug::DebugWindow; @@ -341,7 +334,6 @@ private: bool m_restoringPlaceholder = false; bool m_resizing = false; - QPointer m_anchorBeingDragged; Layouting::ItemContainer *m_rootItem = nullptr; }; diff --git a/src/private/multisplitter/Separator.cpp b/src/private/multisplitter/Separator.cpp index 9640a814..c49de71e 100644 --- a/src/private/multisplitter/Separator.cpp +++ b/src/private/multisplitter/Separator.cpp @@ -19,25 +19,28 @@ */ #include "Separator_p.h" -#include "Anchor_p.h" #include "Logging_p.h" #include "Item_p.h" #include +#include +#include using namespace Layouting; -Separator::Separator(Layouting::Anchor *anchor, QWidget *hostWidget) +bool Separator::s_isResizing = false; +Separator* Separator::s_separatorBeingDragged = nullptr; + +static SeparatorFactoryFunc s_separatorFactoryFunc = nullptr; + +Separator::Separator(QWidget *hostWidget) : QWidget(hostWidget) - , m_anchor(anchor) { - Q_ASSERT(anchor); - setVisible(true); } bool Separator::isVertical() const { - return m_anchor->isVertical(); + return m_orientation == Qt::Vertical; } void Separator::move(int p) @@ -49,17 +52,180 @@ void Separator::move(int p) } } +Qt::Orientation Separator::orientation() const +{ + return m_orientation; +} + void Separator::mousePressEvent(QMouseEvent *) { - m_anchor->onMousePress(); + s_separatorBeingDragged = this; + + qCDebug(separators) << "Drag started"; + + if (lazyResizeEnabled()) { + setLazyPosition(position()); + m_lazyResizeRubberBand->show(); + } } void Separator::mouseMoveEvent(QMouseEvent *ev) { - m_anchor->onMouseMoved(parentWidget()->mapFromGlobal(ev->globalPos())); + if (!isBeingDragged()) + return; + + if (!(qApp->mouseButtons() & Qt::LeftButton)) { + qCDebug(separators) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent"; + onMouseReleased(); + return; + } + +#ifdef Q_OS_WIN + // Try harder, Qt can be wrong, if mixed with MFC + const bool mouseButtonIsReallyDown = (GetKeyState(VK_LBUTTON) & 0x8000) || (GetKeyState(VK_RBUTTON) & 0x8000); + if (!mouseButtonIsReallyDown) { + qCDebug(mouseevents) << Q_FUNC_INFO << "Ignoring spurious mouse event. Someone ate our ReleaseEvent"; + onMouseReleased(); + return; + } +#endif + + const int positionToGoTo = position(ev->pos()); + const int minPos = m_parentContainer->minPosForSeparator_global(this); + const int maxPos = m_parentContainer->maxPosForSeparator_global(this); + + if (positionToGoTo < minPos || positionToGoTo > maxPos) + return; + + m_lastMoveDirection = positionToGoTo < position() ? Side1 + : (positionToGoTo > position() ? Side2 + : Side2); // Last case shouldn't happen though. + + if (/*m_lazyResize*/ false) // TODO + setLazyPosition(positionToGoTo); + else + setPosition(positionToGoTo); } void Separator::mouseReleaseEvent(QMouseEvent *) { - m_anchor->onMouseReleased(); + onMouseReleased(); +} + +void Separator::onMouseReleased() +{ + if (m_lazyResizeRubberBand) { + m_lazyResizeRubberBand->hide(); + setPosition(m_lazyPosition); + } + + s_separatorBeingDragged = nullptr; +} + +bool Separator::lazyResizeEnabled() const +{ + return m_options & SeparatorOption::LazyResize; +} + +void Separator::setGeometry(QRect r) +{ + if (r != m_geometry) { + m_geometry = r; + QWidget::setGeometry(r); + setVisible(true); + } +} + +int Separator::position() const +{ + const QPoint topLeft = m_geometry.topLeft(); + return isVertical() ? topLeft.y() : topLeft.x(); +} + +int Separator::position(QPoint p) const +{ + return isVertical() ? p.y() : p.x(); +} + +QWidget *Separator::hostWidget() const +{ + return parentWidget(); +} + +void Separator::init(ItemContainer *parentContainer, Qt::Orientation orientation, SeparatorOptions options) +{ + m_parentContainer = parentContainer; + m_orientation = orientation; + m_options = options; + m_lazyResizeRubberBand = (options & SeparatorOption::LazyResize) ? new QRubberBand(QRubberBand::Line, hostWidget()) + : nullptr; + setVisible(true); +} + +void Separator::setGeometry(int pos, int pos2, int length) +{ + QRect newGeo = m_geometry; + if (isVertical()) { + // The separator itself is horizontal + newGeo.setSize(QSize(length, Item::separatorThickness())); + newGeo.moveTo(pos2, pos); + } else { + // The separator itself is vertical + newGeo.setSize(QSize(Item::separatorThickness(), length)); + newGeo.moveTo(pos, pos2); + } + + setGeometry(newGeo); +} + +bool Separator::isResizing() +{ + return s_isResizing; +} + +void Separator::setSeparatorFactoryFunc(SeparatorFactoryFunc func) +{ + s_separatorFactoryFunc = func; +} + +Separator* Separator::createSeparator(QWidget *host) +{ + if (s_separatorFactoryFunc) + return s_separatorFactoryFunc(host); + + return new Separator(host); +} + +void Separator::setLazyPosition(int pos) +{ + if (m_lazyPosition != pos) { + m_lazyPosition = pos; + + QRect geo = geometry(); + if (isVertical()) { + geo.moveLeft(pos); + } else { + geo.moveTop(pos); + } + + m_lazyResizeRubberBand->setGeometry(geo); + } +} + +bool Separator::isBeingDragged() const +{ + return s_separatorBeingDragged == this; +} + +void Separator::setPosition(int p) +{ + QRect geo = m_geometry; + QPoint pt = geo.topLeft(); + if (isVertical()) + pt.setY(p); + else + pt.setX(p); + + geo.moveTopLeft(pt); + setGeometry(geo); } diff --git a/src/private/multisplitter/Separator_p.h b/src/private/multisplitter/Separator_p.h index ee34c143..c7d2a4b3 100644 --- a/src/private/multisplitter/Separator_p.h +++ b/src/private/multisplitter/Separator_p.h @@ -21,31 +21,64 @@ #ifndef KD_MULTISPLITTER_SEPARATOR_P_H #define KD_MULTISPLITTER_SEPARATOR_P_H -//#include "docks_export.h" TODO + +#include "Item_p.h" #include #include -namespace Layouting { -class Anchor; +QT_BEGIN_NAMESPACE +class QRubberBand; +QT_END_NAMESPACE -class /*DOCKS_EXPORT*/ Separator : public QWidget +namespace Layouting { + +typedef Separator* (*SeparatorFactoryFunc)(QWidget *parent); + +class Separator : public QWidget { Q_OBJECT Q_PROPERTY(bool isVertical READ isVertical CONSTANT) public: - explicit Separator(Layouting::Anchor *anchor, QWidget *hostWidget); + typedef QVector List; + bool isVertical() const; void move(int p); + Qt::Orientation orientation() const; + bool lazyResizeEnabled() const; + void setGeometry(int pos, int pos2, int length); + void setGeometry(QRect r); + int position() const; + QWidget *hostWidget() const; + + void init(Layouting::ItemContainer*, Qt::Orientation orientation, SeparatorOptions options); + + ///@brief Returns whether we're dragging a separator. Can be useful for the app to stop other work while we're not in the final size + static bool isResizing(); + static void setSeparatorFactoryFunc(SeparatorFactoryFunc); + static Separator* createSeparator(QWidget *host); protected: - const QPointer anchor() const { return m_anchor; } + explicit Separator(QWidget *hostWidget); void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; - private: - const QPointer m_anchor; // QPointer so we don't dereference invalid point in paintEvent() when Anchor is deleted. + static bool s_isResizing; + static Separator* s_separatorBeingDragged; + void onMouseReleased(); + int position(QPoint p) const; + void setLazyPosition(int); + void setPosition(int p); + bool isBeingDragged() const; + Qt::Orientation m_orientation; + QRect m_geometry; + int m_lazyPosition = 0; + SeparatorOptions m_options; + QRubberBand *m_lazyResizeRubberBand = nullptr; + ItemContainer *m_parentContainer = nullptr; + // Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise. + Layouting::Side m_lastMoveDirection = Side1; }; } diff --git a/src/private/widgets/SeparatorWidget.cpp b/src/private/widgets/SeparatorWidget.cpp index 0bcd230f..da03ef6a 100644 --- a/src/private/widgets/SeparatorWidget.cpp +++ b/src/private/widgets/SeparatorWidget.cpp @@ -19,7 +19,6 @@ */ #include "SeparatorWidget_p.h" -#include "multisplitter/Anchor_p.h" #include "Logging_p.h" #include @@ -28,17 +27,14 @@ using namespace KDDockWidgets; using namespace Layouting; -SeparatorWidget::SeparatorWidget(Layouting::Anchor *anchor, QWidget *parent) - : Separator(anchor, parent) +SeparatorWidget::SeparatorWidget(QWidget *parent) + : Separator(parent) { setMouseTracking(true); } void SeparatorWidget::paintEvent(QPaintEvent *) { - if (!anchor()) - return; - QPainter p(this); QStyleOption opt; @@ -56,10 +52,7 @@ void SeparatorWidget::paintEvent(QPaintEvent *) void SeparatorWidget::enterEvent(QEvent *) { - qCDebug(anchors) << Q_FUNC_INFO << anchor() << this; - if (!anchor()) - return; - + qCDebug(anchors) << Q_FUNC_INFO << this; if (isVertical()) setCursor(Qt::SizeVerCursor); else diff --git a/src/private/widgets/SeparatorWidget_p.h b/src/private/widgets/SeparatorWidget_p.h index aace2f59..8680b56d 100644 --- a/src/private/widgets/SeparatorWidget_p.h +++ b/src/private/widgets/SeparatorWidget_p.h @@ -34,7 +34,7 @@ class DOCKS_EXPORT SeparatorWidget : public Layouting::Separator { Q_OBJECT public: - explicit SeparatorWidget(Layouting::Anchor *anchor, QWidget *parent = nullptr); + explicit SeparatorWidget(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *) override; diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 29e17cce..0a66b66f 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -1128,7 +1128,7 @@ void TestDocks::tst_restoreEmpty() QVERIFY(saver.saveToFile(QStringLiteral("layout.json"))); saver.restoreFromFile(QStringLiteral("layout.json")); QVERIFY(m->multiSplitterLayout()->checkSanity()); - QCOMPARE(layout->anchors().size(), 0); + QCOMPARE(layout->separators().size(), 0); QCOMPARE(layout->count(), 0); QCOMPARE(m->size(), oldSize); QVERIFY(layout->checkSanity()); @@ -2749,11 +2749,11 @@ void TestDocks::tst_placeholdersAreRemovedProperly() dock1->setFloating(true); QVERIFY(item->isPlaceholder()); - QCOMPARE(layout->anchors().size(), 0); + QCOMPARE(layout->separators().size(), 0); QCOMPARE(layout->count(), 2); QCOMPARE(layout->placeholderCount(), 1); layout->removeItem(item); - QCOMPARE(layout->anchors().size(), 0); + QCOMPARE(layout->separators().size(), 0); QCOMPARE(layout->count(), 1); QCOMPARE(layout->placeholderCount(), 0); @@ -2762,7 +2762,7 @@ void TestDocks::tst_placeholdersAreRemovedProperly() dock1->setFloating(true); QPointer window1 = dock1->window(); delete dock1; - QCOMPARE(layout->anchors().size(), 0); + QCOMPARE(layout->separators().size(), 0); QCOMPARE(layout->count(), 1); QCOMPARE(layout->placeholderCount(), 0); layout->checkSanity(); @@ -3234,10 +3234,10 @@ void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() m->addDockWidget(dock3, Location_OnTop); m->addDockWidget(dock2, Location_OnTop); m->addDockWidget(dock1, Location_OnTop); - QCOMPARE(layout->anchors().size(), 2); + QCOMPARE(layout->separators().size(), 2); dock2->close(); Testing::waitForResize(dock3); - QCOMPARE(layout->anchors().size(), 1); + QCOMPARE(layout->separators().size(), 1); layout->checkSanity(); // Cleanup: @@ -3263,10 +3263,10 @@ void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() Item *item3 = layout->itemForFrame(dock3->frame()); Item *item4 = layout->itemForFrame(dock4->frame()); - const auto separators = layout->anchors(); + const auto separators = layout->separators(); QCOMPARE(separators.size(), 3); - Anchor *anchor1 = separators[0]; + Separator *anchor1 = separators[0]; int boundToTheRight = layout->rootItem()->maxPosForSeparator(anchor1); int expectedBoundToTheRight = layout->size().width() - 3*Item::separatorThickness() - @@ -4870,7 +4870,7 @@ void TestDocks::tst_resizeWindow2() m->addDockWidget(dock2, Location_OnBottom); auto layout = m->multiSplitterLayout(); - Anchor *anchor = layout->anchors().at(0); + Separator *anchor = layout->separators().at(0); const int oldPosY = anchor->position(); m->resize(m->width() + 10, m->height()); QCOMPARE(anchor->position(), oldPosY);