Replace Anchor with Separator

We don't have the concept of Anchors anymore. Code is now simpler
This commit is contained in:
Sergio Martins
2020-05-04 13:10:38 +01:00
parent 97c1ca30fd
commit 0595448bba
17 changed files with 282 additions and 471 deletions

View File

@@ -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);
}

View File

@@ -28,7 +28,7 @@
#include "Config.h"
#include "DockRegistry_p.h"
#include "FrameworkWidgetFactory.h"
#include "Anchor_p.h"
#include "Separator_p.h"
#include <QApplication>
#include <QDebug>
@@ -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()

View File

@@ -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

View File

@@ -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;

View File

@@ -99,13 +99,13 @@ public:
QVector<QWidget*> 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);

View File

@@ -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 <sergio.martins@kdab.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "Anchor_p.h"
#include "Logging_p.h"
#include "Separator_p.h"
#include <QRubberBand>
#include <QApplication>
#include <QDebug>
#ifdef Q_OS_WIN
# include <Windows.h>
#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;
}

View File

@@ -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 <sergio.martins@kdab.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef KD_MULTISPLITTER_ANCHOR_P_H
#define KD_MULTISPLITTER_ANCHOR_P_H
#include "Item_p.h"
#include <QObject>
#include <QPointer>
#include <QRect>
#include <QVector>
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<Anchor *> 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

View File

@@ -1,6 +1,4 @@
set(MULTISPLITTER_SRCS
Anchor.cpp
Anchor_p.h
Item.cpp
Item_p.h
Logging.cpp

View File

@@ -19,7 +19,7 @@
*/
#include "Item_p.h"
#include "Anchor_p.h"
#include "Separator_p.h"
#include <QEvent>
#include <QDebug>
@@ -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<Layouting::Anchor*> ItemContainer::separators_recursive() const
QVector<Separator *> 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())

View File

@@ -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<Layouting::Anchor*> separators_recursive() const;
QVector<Layouting::Separator*> separators_recursive() const;
Qt::Orientation m_orientation = Qt::Vertical;
private:
void updateWidgets_recursive();
@@ -567,10 +570,10 @@ private:
QVector<int> requiredSeparatorPositions() const;
void updateSeparators();
void deleteSeparators();
Anchor* separatorAt(int p) const;
Separator* separatorAt(int p) const;
QVector<double> childPercentages() const;
mutable bool m_checkSanityScheduled = false;
QVector<Layouting::Anchor*> m_separators;
QVector<Layouting::Separator*> m_separators;
bool m_convertingItemToContainer = false;
};

View File

@@ -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();
}

View File

@@ -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<Layouting::Anchor> m_anchorBeingDragged;
Layouting::ItemContainer *m_rootItem = nullptr;
};

View File

@@ -19,25 +19,28 @@
*/
#include "Separator_p.h"
#include "Anchor_p.h"
#include "Logging_p.h"
#include "Item_p.h"
#include <QMouseEvent>
#include <QRubberBand>
#include <QApplication>
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);
}

View File

@@ -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 <QWidget>
#include <QPointer>
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<Separator*> 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<Layouting::Anchor> anchor() const { return m_anchor; }
explicit Separator(QWidget *hostWidget);
void mousePressEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
private:
const QPointer<Layouting::Anchor> 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;
};
}

View File

@@ -19,7 +19,6 @@
*/
#include "SeparatorWidget_p.h"
#include "multisplitter/Anchor_p.h"
#include "Logging_p.h"
#include <QPainter>
@@ -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

View File

@@ -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;

View File

@@ -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<QWidget> 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);