WIP: Anchor.cpp now builds
This commit is contained in:
@@ -38,18 +38,15 @@
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
bool Anchor::s_isResizing = false;
|
||||
const QString Anchor::s_magicMarker = QStringLiteral("e520c60e-cf5d-4a30-b1a7-588d2c569851");
|
||||
|
||||
Anchor::Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter, Type type)
|
||||
Anchor::Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter)
|
||||
: QObject(multiSplitter->multiSplitter())
|
||||
, m_orientation(orientation)
|
||||
, m_type(type)
|
||||
, m_layout(multiSplitter)
|
||||
, m_separatorWidget(Config::self().frameworkWidgetFactory()->createSeparator(this, multiSplitter->multiSplitter()))
|
||||
, m_lazyResize(Config::self().flags() & Config::Flag_LazyResize)
|
||||
, m_lazyResizeRubberBand(m_lazyResize ? new QRubberBand(QRubberBand::Line, multiSplitter->multiSplitter()) : nullptr)
|
||||
{
|
||||
multiSplitter->insertAnchor(this);
|
||||
connect(this, &QObject::objectNameChanged, m_separatorWidget, &QObject::setObjectName);
|
||||
}
|
||||
|
||||
@@ -58,7 +55,6 @@ Anchor::~Anchor()
|
||||
m_separatorWidget->setEnabled(false);
|
||||
m_separatorWidget->deleteLater();
|
||||
qCDebug(multisplittercreation) << "~Anchor; this=" << this << "; m_to=" << m_to << "; m_from=" << m_from;
|
||||
m_layout->removeAnchor(this);
|
||||
}
|
||||
|
||||
void Anchor::setFrom(Anchor *from)
|
||||
@@ -122,47 +118,6 @@ void Anchor::setGeometry(QRect r)
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::updateItemSizes()
|
||||
{
|
||||
if (!m_initialized) {
|
||||
// setPosition() hasn't been called yet, don't bother
|
||||
return;
|
||||
}
|
||||
|
||||
if (LayoutSaver::restoreInProgress()) {
|
||||
// Nothing to do. The LayoutSaver is setting up the whole layout.
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(anchors) << Q_FUNC_INFO << this << "; o=" << orientation();
|
||||
|
||||
int position = this->position() + m_positionOffset;
|
||||
for (Item *item : qAsConst(m_side2Items)) {
|
||||
QRect geo = item->geometry();
|
||||
|
||||
const QPoint topLeft = isVertical() ? QPoint(position + thickness(), item->y())
|
||||
: QPoint(item->x(), position + thickness());
|
||||
|
||||
geo.setTopLeft(topLeft);
|
||||
if (!item->isPlaceholder())
|
||||
item->setGeometry(geo);
|
||||
}
|
||||
|
||||
position = this->position() - m_positionOffset;
|
||||
|
||||
for (Item *item : qAsConst(m_side1Items)) {
|
||||
QRect geo = item->geometry();
|
||||
|
||||
// -1 as the widget is right next to the anchor, and not on top
|
||||
const QPoint bottomRight = isVertical() ? QPoint(position - 1, geo.bottom())
|
||||
: QPoint(geo.right(), position - 1);
|
||||
geo.setBottomRight(bottomRight);
|
||||
if (!item->isPlaceholder()) {
|
||||
item->setGeometry(geo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::debug_updateItemNames()
|
||||
{
|
||||
// I call this in the unit-tests, when running them on gammaray
|
||||
@@ -194,23 +149,10 @@ Qt::Orientation Anchor::orientation() const
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
void Anchor::setPosition(int p, SetPositionOptions options)
|
||||
void Anchor::setPosition(int)
|
||||
{
|
||||
}
|
||||
|
||||
void Anchor::updatePositionPercentage()
|
||||
{
|
||||
const int layoutLength = m_layout->length(m_orientation);
|
||||
m_positionPercentage = (position() * 1.0) / layoutLength;
|
||||
|
||||
if (position() > layoutLength) {
|
||||
// This warning makes the unit-tests fail if some invalid m_positionPercentage ever appears.
|
||||
// Bug fixed now though.
|
||||
qWarning() << Q_FUNC_INFO << "Weird position percentage" << m_positionPercentage
|
||||
<< position() << layoutLength;
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::position() const
|
||||
{
|
||||
const QPoint topLeft = m_geometry.topLeft();
|
||||
@@ -225,48 +167,6 @@ void Anchor::setVisible(bool v)
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::minPosition() const
|
||||
{
|
||||
const int smallestSqueeze = smallestAvailableItemSqueeze(Side1);
|
||||
return position() - smallestSqueeze;
|
||||
}
|
||||
|
||||
int Anchor::smallestAvailableItemSqueeze(Anchor::Side side) const
|
||||
{
|
||||
int smallest = 0;
|
||||
bool firstElement = true;
|
||||
for (Item *item : items(side)) {
|
||||
const int length = item->length(m_orientation);
|
||||
const int minLength = item->minLength(m_orientation);
|
||||
const int availableSqueeze = length - minLength;
|
||||
if (availableSqueeze < smallest || firstElement) {
|
||||
smallest = availableSqueeze;
|
||||
firstElement = false;
|
||||
}
|
||||
}
|
||||
return smallest;
|
||||
}
|
||||
|
||||
void Anchor::ensureBounded()
|
||||
{
|
||||
// TODO: Probably delete this unused method. It was used in the old days before discovering it
|
||||
// was flawed: Separators being in between bounds doesn't imply that all min sizes are being
|
||||
// Honoured. Use MultiSplitterLayout::ensureItemsMinSize() instead
|
||||
if (!isStatic() && !isFollowing()) {
|
||||
const QPair<int,int> bounds = m_layout->boundPositionsForAnchor(this);
|
||||
|
||||
if (position() < bounds.first) {
|
||||
setPosition(bounds.first);
|
||||
} else if (position() > bounds.second) {
|
||||
setPosition(bounds.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (Item *item : items(Side2)) {
|
||||
item->anchorAtSide(Side2, orientation())->ensureBounded();
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::length() const
|
||||
{
|
||||
Q_ASSERT(m_to);
|
||||
@@ -285,46 +185,7 @@ int Anchor::thickness() const
|
||||
: m_separatorWidget->height();
|
||||
}
|
||||
|
||||
bool Anchor::hasItems(Anchor::Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return !m_side1Items.isEmpty();
|
||||
case Side2:
|
||||
return !m_side2Items.isEmpty();
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Anchor::onlyHasPlaceholderItems(Anchor::Side side) const
|
||||
{
|
||||
auto &items = side == Side1 ? m_side1Items
|
||||
: m_side2Items;
|
||||
|
||||
for (Item *item : items) {
|
||||
if (!item->isPlaceholder())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Anchor::hasNonPlaceholderItems(Anchor::Side side) const
|
||||
{
|
||||
auto &items = side == Side1 ? m_side1Items
|
||||
: m_side2Items;
|
||||
|
||||
for (Item *item : items) {
|
||||
if (!item->isPlaceholder())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Anchor::containsItem(const Item *item, Anchor::Side side) const
|
||||
bool Anchor::containsItem(const Item *item, Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
@@ -337,15 +198,7 @@ bool Anchor::containsItem(const Item *item, Anchor::Side side) const
|
||||
}
|
||||
}
|
||||
|
||||
bool Anchor::isStaticOrFollowsStatic() const
|
||||
{
|
||||
if (isStatic())
|
||||
return true;
|
||||
|
||||
return m_followee && m_followee->isStaticOrFollowsStatic();
|
||||
}
|
||||
|
||||
const ItemList Anchor::items(Anchor::Side side) const
|
||||
const ItemList Anchor::items(Side side) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
@@ -358,296 +211,28 @@ const ItemList Anchor::items(Anchor::Side side) const
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::consume(Anchor *other)
|
||||
{
|
||||
QPointer<Anchor> otherp = other; // Just to check if it wasn't deleted meanwhile. Which doesn't happen, but we silence a clang-tidy warning this way.
|
||||
consume(other, Side1);
|
||||
if (otherp)
|
||||
consume(other, Side2);
|
||||
}
|
||||
|
||||
void Anchor::consume(Anchor *other, Side side)
|
||||
{
|
||||
auto items = other->items(side);
|
||||
other->removeItems(side);
|
||||
addItems(items, side);
|
||||
if (other->isUnneeded()) {
|
||||
// Before deleting an unneeded anchor, we must check if there's anchors following it, and make them follow us instead
|
||||
Anchor::List anchorsFollowingOther = m_layout->anchorsFollowing(other);
|
||||
for (Anchor *follower : anchorsFollowingOther) {
|
||||
if (follower != this)
|
||||
follower->setFollowee(this);
|
||||
}
|
||||
|
||||
delete other;
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::swapItems(Anchor *other)
|
||||
{
|
||||
auto other1 = other->m_side1Items;
|
||||
auto other2 = other->m_side2Items;
|
||||
auto my1 = m_side1Items;
|
||||
auto my2 = m_side2Items;
|
||||
|
||||
removeAllItems();
|
||||
other->removeAllItems();
|
||||
|
||||
other->addItems(my1, Side1);
|
||||
other->addItems(my2, Side2);
|
||||
addItems(other1, Side1);
|
||||
addItems(other2, Side1);
|
||||
}
|
||||
|
||||
void Anchor::removeAllItems()
|
||||
{
|
||||
removeItems(Side1);
|
||||
removeItems(Side2);
|
||||
}
|
||||
|
||||
/** static */
|
||||
Anchor *Anchor::createFrom(Anchor *other, Item *relativeTo)
|
||||
{
|
||||
Q_ASSERT(other);
|
||||
auto anchor = new Anchor(other->orientation(), other->m_layout);
|
||||
anchor->setFrom(other->m_from);
|
||||
anchor->setTo(other->m_to);
|
||||
|
||||
if (relativeTo) {
|
||||
if (other->containsItem(relativeTo, Side1)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Side1);
|
||||
} else if (other->containsItem(relativeTo, Side2)) {
|
||||
other->removeItem(relativeTo);
|
||||
anchor->addItem(relativeTo, Side2);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
} else {
|
||||
auto other1 = other->m_side1Items;
|
||||
auto other2 = other->m_side2Items;
|
||||
other->removeAllItems();
|
||||
anchor->addItems(other1, Side1);
|
||||
anchor->addItems(other2, Side2);
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
void Anchor::setPositionOffset(int value)
|
||||
{
|
||||
if (value != m_positionOffset) {
|
||||
m_positionOffset = value;
|
||||
updateItemSizes();
|
||||
}
|
||||
}
|
||||
|
||||
bool Anchor::isBeingDragged() const
|
||||
{
|
||||
return m_layout->anchorBeingDragged() == this;
|
||||
}
|
||||
|
||||
int Anchor::cumulativeMinLength(Anchor::Side side) const
|
||||
{
|
||||
if (isStatic() && isEmpty()) {
|
||||
// There's no widget, but minimum is the space occupied by left+right anchors (or top+bottom).
|
||||
const int staticAnchorThickness = Anchor::thickness(/*static=*/true);
|
||||
if ((side == Side2 && (m_type & (Type_LeftStatic | Type_TopStatic))) ||
|
||||
(side == Side1 && (m_type & (Type_RightStatic | Type_BottomStatic))))
|
||||
return 2 * staticAnchorThickness;
|
||||
}
|
||||
const CumulativeMin result = cumulativeMinLength_recursive(side);
|
||||
|
||||
const int numNonStaticAnchors = result.numItems >= 2 ? result.numItems - 1
|
||||
: 0;
|
||||
|
||||
int r = Anchor::thickness(isStatic()) + Anchor::thickness(true)
|
||||
+ numNonStaticAnchors*Anchor::thickness(false)
|
||||
+ result.minLength;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Anchor::CumulativeMin Anchor::cumulativeMinLength_recursive(Anchor::Side side) const
|
||||
{
|
||||
const auto items = this->items(side);
|
||||
CumulativeMin result = { 0, 0 };
|
||||
|
||||
for (auto item : items) {
|
||||
Anchor *oppositeAnchor = item->anchorAtSide(side, orientation());
|
||||
if (!oppositeAnchor) {
|
||||
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
|
||||
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
CumulativeMin candidateMin = { 0, 0 };
|
||||
if (!item->isPlaceholder()) {
|
||||
candidateMin.numItems++;
|
||||
candidateMin.minLength = item->minLength(orientation());
|
||||
}
|
||||
|
||||
candidateMin += oppositeAnchor->cumulativeMinLength_recursive(side);
|
||||
|
||||
if (candidateMin.minLength >= result.minLength) {
|
||||
result = candidateMin;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Anchor::setFollowee(Anchor *followee)
|
||||
{
|
||||
Q_ASSERT(this != followee);
|
||||
if (m_followee == followee)
|
||||
return;
|
||||
|
||||
qCDebug(placeholder) << Q_FUNC_INFO << "follower="
|
||||
<< this << "; followee=" << followee;
|
||||
|
||||
if (m_followee) {
|
||||
disconnect(m_followee, &Anchor::positionChanged, this, &Anchor::onFolloweePositionChanged);
|
||||
disconnect(m_followee, &Anchor::thicknessChanged, this, &Anchor::setThickness);
|
||||
disconnect(m_followeeDestroyedConnection);
|
||||
}
|
||||
|
||||
m_followee = followee;
|
||||
setThickness();
|
||||
if (m_followee) {
|
||||
Q_ASSERT(orientation() == m_followee->orientation());
|
||||
setVisible(false);
|
||||
setPosition(m_followee->position());
|
||||
connect(m_followee, &Anchor::positionChanged, this, &Anchor::onFolloweePositionChanged);
|
||||
connect(m_followee, &Anchor::thicknessChanged, this, &Anchor::setThickness);
|
||||
m_followeeDestroyedConnection = connect(m_followee, &QObject::destroyed, this, [this] {
|
||||
setFollowee(nullptr);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
Q_EMIT followeeChanged();
|
||||
}
|
||||
|
||||
const Anchor::List Anchor::followers() const
|
||||
{
|
||||
Anchor::List result;
|
||||
for (Anchor *a : m_layout->anchors()) {
|
||||
if (a->followee() == this)
|
||||
result.push_back(a);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Anchor *Anchor::endFollowee() const
|
||||
{
|
||||
Anchor *a = m_followee;
|
||||
while (a) {
|
||||
if (!a->followee())
|
||||
return a;
|
||||
|
||||
a = a->followee();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Anchor::findAnchor(Anchor *anchor, Anchor::Side side) const
|
||||
{
|
||||
if (!anchor)
|
||||
return false;
|
||||
|
||||
Q_ASSERT(anchor != this);
|
||||
Q_ASSERT(anchor->orientation() == orientation());
|
||||
|
||||
for (Item *item : items(side)) {
|
||||
Anchor *a = item->anchorAtSide(side, orientation());
|
||||
if (anchor == a)
|
||||
return true;
|
||||
|
||||
if (a->findAnchor(anchor, side))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Anchor *Anchor::findNearestAnchorWithItems(Anchor::Side side) const
|
||||
{
|
||||
Anchor *candidate = nullptr;
|
||||
for (Item *item : items(side)) {
|
||||
Anchor *a = item->anchorAtSide(side, orientation());
|
||||
if (!a->hasNonPlaceholderItems(side))
|
||||
a = a->findNearestAnchorWithItems(side);
|
||||
|
||||
if (!candidate || (side == Side1 && a->position() > candidate->position()) || (side == Side2 && a->position() < candidate->position()) ) {
|
||||
candidate = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidate)
|
||||
candidate = m_layout->staticAnchor(side, orientation());
|
||||
|
||||
Q_ASSERT(candidate->isStatic() || candidate->hasNonPlaceholderItems(side));
|
||||
return candidate;
|
||||
}
|
||||
|
||||
void Anchor::clear()
|
||||
{
|
||||
m_side1Items.clear();
|
||||
m_side2Items.clear();
|
||||
}
|
||||
|
||||
void Anchor::onFolloweePositionChanged(int pos)
|
||||
{
|
||||
Q_ASSERT(isFollowing());
|
||||
setPosition(pos);
|
||||
}
|
||||
|
||||
int Anchor::thickness(bool staticAnchor)
|
||||
{
|
||||
return Config::self().separatorThickness(staticAnchor);
|
||||
}
|
||||
|
||||
void Anchor::setLayout(MultiSplitterLayout *layout)
|
||||
{
|
||||
m_layout->removeAnchor(this);
|
||||
m_layout = layout;
|
||||
setParent(layout->multiSplitter());
|
||||
m_separatorWidget->setParent(layout->multiSplitter());
|
||||
m_layout->insertAnchor(this);
|
||||
m_layout->setAnchorBeingDragged(nullptr);
|
||||
}
|
||||
|
||||
Separator *Anchor::separatorWidget() const
|
||||
{
|
||||
return m_separatorWidget;
|
||||
}
|
||||
|
||||
void Anchor::setThickness()
|
||||
{
|
||||
const int value = isFollowing() ? m_followee->thickness()
|
||||
: thickness(isStatic());
|
||||
|
||||
const int oldValue = thickness();
|
||||
|
||||
if (value != oldValue) {
|
||||
if (isVertical()) {
|
||||
m_separatorWidget->setFixedWidth(value);
|
||||
m_geometry.setWidth(value);
|
||||
} else {
|
||||
m_separatorWidget->setFixedHeight(value);
|
||||
m_geometry.setHeight(value);
|
||||
}
|
||||
|
||||
Q_EMIT thicknessChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::setLazyPosition(int pos)
|
||||
{
|
||||
if (m_lazyPosition != pos) {
|
||||
@@ -669,57 +254,6 @@ int Anchor::position(QPoint p) const
|
||||
return isVertical() ? p.x() : p.y();
|
||||
}
|
||||
|
||||
void Anchor::addItem(Item *item, Anchor::Side side)
|
||||
{
|
||||
Q_ASSERT(side != Side_None);
|
||||
auto &items = (side == Side1) ? m_side1Items : m_side2Items;
|
||||
if (!items.contains(item)) {
|
||||
items << item;
|
||||
item->anchorGroup().setAnchor(this, orientation(), side);
|
||||
Q_EMIT itemsChanged(side);
|
||||
updateItemSizes();
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::addItems(const ItemList &list, Side side)
|
||||
{
|
||||
for (Item *item : list)
|
||||
addItem(item, side);
|
||||
}
|
||||
|
||||
void Anchor::removeItem(Item *item)
|
||||
{
|
||||
if (m_side1Items.removeOne(item)) {
|
||||
item->anchorGroup().setAnchor(nullptr, orientation(), Side1);
|
||||
Q_EMIT itemsChanged(Side1);
|
||||
} else {
|
||||
if (m_side2Items.removeOne(item)) {
|
||||
item->anchorGroup().setAnchor(nullptr, orientation(), Side2);
|
||||
Q_EMIT itemsChanged(Side2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::removeItems(Side side)
|
||||
{
|
||||
const auto &items = this->items(side);
|
||||
for (Item *item : items)
|
||||
removeItem(item);
|
||||
}
|
||||
|
||||
Anchor::Side Anchor::oppositeSide(Side side)
|
||||
{
|
||||
switch (side) {
|
||||
case Side1:
|
||||
return Side2;
|
||||
case Side2:
|
||||
return Side1;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return Side_None;
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::onMousePress()
|
||||
{
|
||||
s_isResizing = true;
|
||||
@@ -743,9 +277,9 @@ void Anchor::onMouseReleased()
|
||||
m_layout->setAnchorBeingDragged(nullptr);
|
||||
}
|
||||
|
||||
void Anchor::onMouseMoved(QPoint pt)
|
||||
void Anchor::onMouseMoved(QPoint)
|
||||
{
|
||||
if (!isBeingDragged() || isStatic())
|
||||
if (!isBeingDragged())
|
||||
return;
|
||||
|
||||
if (!(qApp->mouseButtons() & Qt::LeftButton)) {
|
||||
@@ -764,8 +298,8 @@ void Anchor::onMouseMoved(QPoint pt)
|
||||
}
|
||||
#endif
|
||||
|
||||
const int positionToGoTo = position(pt);
|
||||
auto bounds = m_layout->boundPositionsForAnchor(this);
|
||||
// const int positionToGoTo = position(pt);
|
||||
/*auto bounds = m_layout->boundPositionsForAnchor(this);
|
||||
|
||||
if (positionToGoTo < bounds.first || positionToGoTo > bounds.second) {
|
||||
// qDebug() << "Out of bounds" << bounds.first << bounds.second << positionToGoTo << "; currentPos" << position() << "; window size" << window()->size();
|
||||
@@ -779,7 +313,7 @@ void Anchor::onMouseMoved(QPoint pt)
|
||||
if (m_lazyResize)
|
||||
setLazyPosition(positionToGoTo);
|
||||
else
|
||||
setPosition(positionToGoTo);
|
||||
setPosition(positionToGoTo);*/
|
||||
}
|
||||
|
||||
void Anchor::onWidgetMoved(int p)
|
||||
@@ -795,62 +329,3 @@ bool Anchor::isResizing()
|
||||
{
|
||||
return s_isResizing;
|
||||
}
|
||||
|
||||
|
||||
Anchor *Anchor::deserialize(const LayoutSaver::Anchor &a, MultiSplitterLayout *layout)
|
||||
{
|
||||
auto anchor = new Anchor(Qt::Orientation(a.orientation), layout, Anchor::Type(a.type));
|
||||
anchor->setObjectName(a.objectName);
|
||||
anchor->setGeometry(a.geometry);
|
||||
anchor->m_positionPercentage = a.positionPercentage;
|
||||
|
||||
anchor->setProperty("indexFrom", a.indexOfFrom);
|
||||
anchor->setProperty("indexTo", a.indexOfTo);
|
||||
anchor->setProperty("indexFolowee", a.indexOfFollowee);
|
||||
|
||||
ItemList side1Items;
|
||||
ItemList side2Items;
|
||||
const ItemList allItems = layout->items();
|
||||
side1Items.reserve(a.side1Items.size());
|
||||
for (int index : qAsConst(a.side1Items)) {
|
||||
side1Items.push_back(allItems.at(index));
|
||||
}
|
||||
side2Items.reserve(a.side2Items.size());
|
||||
for (int index : qAsConst(a.side2Items)) {
|
||||
side2Items.push_back(allItems.at(index));
|
||||
}
|
||||
|
||||
anchor->m_side1Items = side1Items;
|
||||
anchor->m_side2Items = side2Items;
|
||||
anchor->m_initialized = true;
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
LayoutSaver::Anchor Anchor::serialize() const
|
||||
{
|
||||
LayoutSaver::Anchor a;
|
||||
const Anchor::List allAnchors = m_layout->anchors();
|
||||
const ItemList allItems = m_layout->items();
|
||||
|
||||
a.objectName = objectName();
|
||||
a.type = type();
|
||||
a.geometry = geometry();
|
||||
a.orientation = orientation();
|
||||
a.indexOfFrom = allAnchors.indexOf(from());
|
||||
a.indexOfTo = allAnchors.indexOf(to());
|
||||
a.indexOfFollowee = followee() ? allAnchors.indexOf(followee()) : -1;
|
||||
a.positionPercentage = m_positionPercentage;
|
||||
|
||||
a.side1Items.clear();
|
||||
a.side1Items.reserve(this->side1Items().size());
|
||||
for (Item *item : this->side1Items())
|
||||
a.side1Items.push_back(allItems.indexOf(item));
|
||||
|
||||
a.side2Items.clear();
|
||||
a.side2Items.reserve(this->side2Items().size());
|
||||
for (Item *item : this->side2Items())
|
||||
a.side2Items.push_back(allItems.indexOf(item));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
#include "Item_p.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
@@ -96,40 +97,11 @@ class DOCKS_EXPORT_FOR_UNIT_TESTS Anchor : public QObject // clazy:exclude=ctor-
|
||||
Q_PROPERTY(Anchor* to READ to WRITE setTo NOTIFY toChanged)
|
||||
Q_PROPERTY(int position READ position WRITE setPosition NOTIFY positionChanged)
|
||||
Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT)
|
||||
Q_PROPERTY(Anchor *followee READ followee NOTIFY followeeChanged)
|
||||
public:
|
||||
///@brief represents the Anchor type
|
||||
///An anchor can be of 2 types:
|
||||
/// - Normal: Anchor that can be resized via mouse
|
||||
/// - static: this is the top, left, right, bottom borders of the main window. They are called static because they don't move.
|
||||
enum Type {
|
||||
Type_None = 0, ///< The anchor is normal, and can be resized.
|
||||
Type_LeftStatic = 1, ///< The anchor is static and represents the left mainwindow margin
|
||||
Type_RightStatic = 2, ///< The anchor is static and represents the right mainwindow margin
|
||||
Type_TopStatic = 4, ///< The anchor is static and represents the top mainwindow margin
|
||||
Type_BottomStatic = 8, ///< The anchor is static and represents the bottom mainwindow margin
|
||||
Type_Static = Type_TopStatic | Type_LeftStatic | Type_RightStatic | Type_BottomStatic ///< The anchor is static, one of the 4 previous ones
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
|
||||
enum Side {
|
||||
Side_None = 0,
|
||||
Side1,
|
||||
Side2
|
||||
};
|
||||
Q_ENUM(Side)
|
||||
|
||||
enum SetPositionOption {
|
||||
SetPositionOption_None = 0,
|
||||
SetPositionOption_DontRecalculatePercentage = 1
|
||||
};
|
||||
Q_DECLARE_FLAGS(SetPositionOptions, SetPositionOption)
|
||||
|
||||
typedef QVector<Anchor *> List;
|
||||
explicit Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter, Type = Type_None);
|
||||
explicit Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter);
|
||||
~Anchor() override;
|
||||
static Anchor* deserialize(const LayoutSaver::Anchor &, MultiSplitterLayout *layout);
|
||||
LayoutSaver::Anchor serialize() const;
|
||||
|
||||
void setFrom(Anchor *);
|
||||
Anchor *from() const { return m_from; }
|
||||
@@ -141,14 +113,11 @@ public:
|
||||
void removeItem(Item *w);
|
||||
void removeItems(Side);
|
||||
bool isVertical() const { return m_orientation == Qt::Vertical; }
|
||||
void setPosition(int p, SetPositionOptions = SetPositionOption_None);
|
||||
void setPosition(int);
|
||||
void updatePositionPercentage();
|
||||
int position() const;
|
||||
|
||||
void setVisible(bool);
|
||||
qreal positionPercentage() const { return m_positionPercentage; }
|
||||
|
||||
void ensureBounded();
|
||||
|
||||
/**
|
||||
* @brief Sets the new layout. Called when we're dropping a source layout into a target one.
|
||||
@@ -159,21 +128,6 @@ public:
|
||||
///@brief returns the separator widget
|
||||
Separator* separatorWidget() const;
|
||||
|
||||
/**
|
||||
* Returns how far left or top an anchor can go and still respecting it's Side1 widgets min-size.
|
||||
* This function doesn't count with shifting other anchors, for that use MultiSplitterLayout::boundPositionsForAnchor()
|
||||
* which is is recursive and returns the bounds after simulating that intermediary anchors to the left/top were
|
||||
* also resized (each still respecting widgets min sizes though).
|
||||
*/
|
||||
int minPosition() const;
|
||||
|
||||
/**
|
||||
* A squeeze is a widget's width (or height for horizontal anchors) minus its minimum width.
|
||||
* This function iterates through all widgets of the specified side and returns the minimum
|
||||
* available squeeze.
|
||||
*/
|
||||
int smallestAvailableItemSqueeze(Anchor::Side) const;
|
||||
|
||||
/**
|
||||
* @brief The length of this anchor. The distance between @ref from and @ref to.
|
||||
* @return the anchor's length
|
||||
@@ -191,122 +145,39 @@ public:
|
||||
*/
|
||||
int thickness() const;
|
||||
|
||||
/**
|
||||
* @brief Checks if this Anchor is static.
|
||||
* @return true if this Anchor is static.
|
||||
*/
|
||||
bool isStatic() const { return m_type & Type_Static; }
|
||||
|
||||
bool isUnneeded() const { return !isStatic() && (!hasItems(Side1) || !hasItems(Side2)); }
|
||||
bool isEmpty() const { return !hasItems(Side1) && !hasItems(Side2); }
|
||||
bool hasItems(Side) const;
|
||||
bool hasNonPlaceholderItems(Side) const;
|
||||
bool onlyHasPlaceholderItems(Anchor::Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this Anchor should follow another one. That happens if one of it's side is empty or only has placeholders
|
||||
* Also, it can't be a static anchor.
|
||||
*/
|
||||
bool shouldFollow() const { return !isStatic() && (onlyHasPlaceholderItems(Side1) || onlyHasPlaceholderItems(Side2)); }
|
||||
|
||||
bool containsItem(const Item *w, Side side) const;
|
||||
bool isStaticOrFollowsStatic() const;
|
||||
|
||||
const ItemList items(Side side) const;
|
||||
const ItemList side1Items() const { return m_side1Items; }
|
||||
const ItemList side2Items() const { return m_side2Items; }
|
||||
|
||||
void consume(Anchor *other);
|
||||
void consume(Anchor *other, Side);
|
||||
void swapItems(Anchor *other);
|
||||
void removeAllItems();
|
||||
|
||||
static Anchor *createFrom(Anchor *other, Item *relativeTo = nullptr);
|
||||
void setPositionOffset(int);
|
||||
bool isBeingDragged() const;
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
int cumulativeMinLength(Anchor::Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Makes this separator follow another one. This one will be made invisible.
|
||||
* Used when the item in the layout is just a placeholder remembering a previous dock widget position.
|
||||
* Pass nullptr do make it not follow and visible again.
|
||||
*/
|
||||
void setFollowee(Anchor *);
|
||||
|
||||
/**
|
||||
* @brief getter for the followee
|
||||
*/
|
||||
Anchor *followee() const { return m_followee; }
|
||||
|
||||
/**
|
||||
* @brief Returns the list of anchors following this one.
|
||||
*/
|
||||
const List followers() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the last followee in the chain.
|
||||
*/
|
||||
Anchor *endFollowee() const;
|
||||
|
||||
/**
|
||||
* @brief Recursively looks for an anchor in the whole layout but only looking at side @p side
|
||||
*
|
||||
* This allows us to know if there's an anchor on the top or left of us (side1) or right or bottom
|
||||
* (side2), in the whole layout.
|
||||
*
|
||||
* Returns false if @p anchor is nullptr
|
||||
*/
|
||||
bool findAnchor(Anchor *anchor, Side side) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the nearest Anchor with non-placeholder items on side @p side
|
||||
* If nothing is found then returns the static anchor on that side
|
||||
*/
|
||||
Anchor *findNearestAnchorWithItems(Side side) const;
|
||||
|
||||
///@brief removes the side1 and side2 items. Doesn't delete them
|
||||
void clear();
|
||||
|
||||
static int thickness(bool staticAnchor);
|
||||
static Anchor::Side oppositeSide(Side side);
|
||||
void onFolloweePositionChanged(int pos);
|
||||
bool isFollowing() const { return m_followee != nullptr; }
|
||||
|
||||
void onMousePress();
|
||||
void onMouseReleased();
|
||||
void onMouseMoved(QPoint pt);
|
||||
void onWidgetMoved(int p);
|
||||
|
||||
|
||||
///@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();
|
||||
|
||||
private:
|
||||
struct CumulativeMin {
|
||||
int minLength;
|
||||
int numItems;
|
||||
CumulativeMin& operator+=(CumulativeMin other) {
|
||||
minLength += other.minLength;
|
||||
numItems += other.numItems;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
CumulativeMin cumulativeMinLength_recursive(Anchor::Side side) const;
|
||||
|
||||
void setThickness();
|
||||
void setLazyPosition(int);
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(int pos);
|
||||
void itemsChanged(Anchor::Side);
|
||||
void itemsChanged(Side);
|
||||
void fromChanged();
|
||||
void toChanged();
|
||||
void debug_itemNamesChanged();
|
||||
void followeeChanged();
|
||||
void thicknessChanged();
|
||||
|
||||
public:
|
||||
int position(QPoint) const;
|
||||
@@ -323,28 +194,20 @@ public:
|
||||
ItemList m_side2Items;
|
||||
QPointer<Anchor> m_from;// QPointer just so we can assert. They should never be null.
|
||||
QPointer<Anchor> m_to;
|
||||
const Type m_type;
|
||||
qreal m_positionPercentage = 0.0; // Should be between 0 and 1
|
||||
|
||||
// Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise.
|
||||
Side m_lastMoveDirection = Side_None;
|
||||
Side m_lastMoveDirection = Side1;
|
||||
|
||||
MultiSplitterLayout *m_layout = nullptr;
|
||||
bool m_showingSide1Rubberband = false;
|
||||
bool m_showingSide2Rubberband = false;
|
||||
bool m_initialized = false;
|
||||
static bool s_isResizing;
|
||||
static const QString s_magicMarker; // Just to validate serialize is symmetric to deserialize
|
||||
|
||||
// For when being animated. They are not displayed at their pos, but with an offset.
|
||||
int m_positionOffset = 0;
|
||||
|
||||
QString m_debug_side1ItemNames;
|
||||
QString m_debug_side2ItemNames;
|
||||
Separator *const m_separatorWidget;
|
||||
QRect m_geometry;
|
||||
Anchor *m_followee = nullptr;
|
||||
QMetaObject::Connection m_followeeDestroyedConnection;
|
||||
const bool m_lazyResize;
|
||||
int m_lazyPosition = 0;
|
||||
QRubberBand *const m_lazyResizeRubberBand;
|
||||
|
||||
@@ -538,276 +538,13 @@ int MultiSplitterLayout::placeholderCount() const
|
||||
return count() - visibleCount();
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::removeAnchor(Anchor *anchor)
|
||||
{
|
||||
if (!m_inDestructor)
|
||||
m_anchors.removeOne(anchor);
|
||||
}
|
||||
|
||||
QPair<int, int> MultiSplitterLayout::boundPositionsForAnchor(Anchor *anchor) const
|
||||
{
|
||||
if (anchor->isStatic()) {
|
||||
if (anchor == m_leftAnchor || anchor == m_topAnchor) {
|
||||
return {0, 0};
|
||||
} else if (anchor == m_rightAnchor || anchor == m_bottomAnchor) {
|
||||
const int max = length(anchor->orientation()) - Anchor::thickness(true);
|
||||
return {max, max};
|
||||
}
|
||||
}
|
||||
|
||||
if (anchor->isFollowing())
|
||||
anchor = anchor->endFollowee();
|
||||
|
||||
const int minSide1Length = anchor->cumulativeMinLength(Anchor::Side1);
|
||||
const int minSide2Length = anchor->cumulativeMinLength(Anchor::Side2);
|
||||
const int length = anchor->isVertical() ? width() : height();
|
||||
|
||||
const int bound1 = qMax(0, minSide1Length - anchor->thickness());
|
||||
const int bound2 = qMax(0, length - minSide2Length);
|
||||
|
||||
if (bound2 < bound1) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid bounds"
|
||||
<< "; bound1=" << bound1
|
||||
<< "; bound2=" << bound2
|
||||
<< "; layout.size=" << size()
|
||||
<< "; layout.min=" << minimumSize()
|
||||
<< "; anchor=" << anchor
|
||||
<< "; orientation=" << anchor->orientation()
|
||||
<< "; minSide1Length=" << minSide1Length
|
||||
<< "; minSide2Length=" << minSide2Length
|
||||
<< "; side1=" << anchor->side1Items()
|
||||
<< "; side2=" << anchor->side2Items()
|
||||
<< "; followee=" << anchor->followee()
|
||||
<< "; thickness=" << anchor->thickness();
|
||||
}
|
||||
|
||||
return { bound1, bound2 };
|
||||
}
|
||||
|
||||
QHash<Anchor *, QPair<int, int> > MultiSplitterLayout::boundPositionsForAllAnchors() const
|
||||
{
|
||||
QHash<Anchor *, QPair<int, int> > result;
|
||||
for (Anchor *anchor : m_anchors)
|
||||
result.insert(anchor, boundPositionsForAnchor(anchor));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int MultiSplitterLayout::boundPositionForAnchor(Anchor *anchor, Anchor::Side direction) const
|
||||
{
|
||||
auto bounds = boundPositionsForAnchor(anchor);
|
||||
return direction == Anchor::Side1 ? bounds.first
|
||||
: bounds.second;
|
||||
}
|
||||
|
||||
MultiSplitterLayout::Length MultiSplitterLayout::availableLengthForDrop(Location location, const Item *relativeTo) const
|
||||
{
|
||||
Length result;
|
||||
|
||||
const bool relativeToThis = relativeTo == nullptr;
|
||||
|
||||
AnchorGroup anchors = relativeToThis ? staticAnchorGroup()
|
||||
: relativeTo->anchorGroup();
|
||||
|
||||
Anchor *anchor = nullptr;
|
||||
|
||||
int thisLength = 0;
|
||||
|
||||
switch (location) {
|
||||
case KDDockWidgets::Location_None:
|
||||
qWarning() << "MultiSplitterLayout::availableLengthForDrop invalid location for dropping";
|
||||
return result;
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
anchor = anchors.left;
|
||||
thisLength = width();
|
||||
break;
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
anchor = anchors.top;
|
||||
thisLength = height();
|
||||
break;
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
anchor = anchors.right;
|
||||
thisLength = width();
|
||||
break;
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
anchor = anchors.bottom;
|
||||
thisLength = height();
|
||||
break;
|
||||
}
|
||||
|
||||
anchor = anchor->isFollowing() ? anchor->endFollowee() : anchor;
|
||||
|
||||
const int minForAlreadyOccupied1 = anchor->cumulativeMinLength(Anchor::Side1) - anchor->thickness(); // TODO: Check if this is correct, we're discounting the anchor twice
|
||||
const int minForAlreadyOccupied2 = anchor->cumulativeMinLength(Anchor::Side2) - anchor->thickness();
|
||||
|
||||
const int side1AvailableLength = anchor->position() - minForAlreadyOccupied1;
|
||||
const int side2AvailableLength = thisLength - (anchor->position() + anchor->thickness()) - minForAlreadyOccupied2;
|
||||
|
||||
const bool needsNewAnchor = hasVisibleItems(); // If a new anchor is needed then we need space for the drag handle and such.
|
||||
const int newAnchorThickness = needsNewAnchor ? Anchor::thickness(/*static=*/false) : 0;
|
||||
|
||||
// This useless space doesn't belong to side1 or side2 specifically. So account for it separately.
|
||||
const int unusableSpace = newAnchorThickness;
|
||||
|
||||
const int usableLength = qMax(0, side1AvailableLength + side2AvailableLength - unusableSpace);
|
||||
if (usableLength > 0) {
|
||||
qreal factor = (side1AvailableLength * 1.0) / (side1AvailableLength + side2AvailableLength);
|
||||
result.side1Length = int(qRound(usableLength * factor)); // rounding not really needed, but makes things more fair probably
|
||||
result.side2Length = usableLength - result.side1Length;
|
||||
}
|
||||
|
||||
qCDebug(sizing) << Q_FUNC_INFO
|
||||
<< "; available=" << result.length() << result.side1Length << result.side2Length
|
||||
<< "; side1AvailableLength=" << side1AvailableLength
|
||||
<< "; side2AvailableLength=" << side2AvailableLength
|
||||
<< "; minForAlreadyOccupied1=" << minForAlreadyOccupied1
|
||||
<< "; minForAlreadyOccupied2=" << minForAlreadyOccupied2
|
||||
<< "; thisLength=" << thisLength
|
||||
<< "; anchorPos=" << anchor->position()
|
||||
<< "; unusableSpace=" << unusableSpace;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int MultiSplitterLayout::availableLengthForOrientation(Qt::Orientation orientation) const
|
||||
{
|
||||
Length l = availableLengthForDrop(orientation == Qt::Vertical ? Location_OnLeft
|
||||
: Location_OnTop, nullptr);
|
||||
|
||||
return l.length();
|
||||
}
|
||||
|
||||
QSize MultiSplitterLayout::availableSize() const
|
||||
{
|
||||
return { availableLengthForOrientation(Qt::Vertical), availableLengthForOrientation(Qt::Horizontal) };
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the width or height the widget will get when dropped.
|
||||
*/
|
||||
MultiSplitterLayout::Length MultiSplitterLayout::lengthForDrop(const QWidgetOrQuick *widget, Location location,
|
||||
const Item *relativeTo) const
|
||||
{
|
||||
Q_ASSERT(location != Location_None);
|
||||
|
||||
const Qt::Orientation anchorOrientation = anchorOrientationForLocation(location);
|
||||
const int widgetCurrentLength = widgetLength(widget, anchorOrientation);
|
||||
Length available = availableLengthForDrop(location, relativeTo);
|
||||
|
||||
const int requiredAtLeast = widgetMinLength(widget, anchorOrientation);
|
||||
if (available.length() < requiredAtLeast) {
|
||||
qCDebug(sizing) << Q_FUNC_INFO
|
||||
<< "\n Not enough space. available=" << available.length()
|
||||
<< "; required=" << requiredAtLeast
|
||||
<< "; m_size=" << m_size;
|
||||
return {};
|
||||
}
|
||||
|
||||
const int suggestedLength = qMin(widgetCurrentLength, int(0.4 * length(anchorOrientation)));
|
||||
available.setLength(qBound(requiredAtLeast, suggestedLength, available.length()));
|
||||
|
||||
qCDebug(sizing) << "MultiSplitterLayout::lengthForDrop length=" << available.length()
|
||||
<< "; s1=" << available.side1Length << "; s2="<< available.side2Length
|
||||
<< "; relativeTo=" << relativeTo
|
||||
<< "; relativeTo.geo=" << (relativeTo ? relativeTo->geometry() : QRect())
|
||||
<< "; widgetCurrentLength=" << widgetCurrentLength;
|
||||
return available;
|
||||
}
|
||||
|
||||
QRect MultiSplitterLayout::rectForDrop(MultiSplitterLayout::Length lfd, Location location, QRect relativeToRect) const
|
||||
{
|
||||
QRect result;
|
||||
const int widgetLength = lfd.length();
|
||||
const int newAnchorThickness = isEmpty() ? 0 : Anchor::thickness(/*static=*/false);
|
||||
const int side1Length = lfd.side1Length;
|
||||
const int staticAnchorThickness = Anchor::thickness(/**static=*/true);
|
||||
|
||||
switch (location) {
|
||||
case Location_OnLeft:
|
||||
result = QRect(qMax(0, relativeToRect.x() - side1Length), relativeToRect.y(),
|
||||
widgetLength, relativeToRect.height());
|
||||
break;
|
||||
case Location_OnTop:
|
||||
result = QRect(relativeToRect.x(), qMax(0, relativeToRect.y() - side1Length),
|
||||
relativeToRect.width(), widgetLength);
|
||||
break;
|
||||
case Location_OnRight:
|
||||
result = QRect(qMin(relativeToRect.right() + 1 - side1Length + newAnchorThickness,
|
||||
width() - widgetLength - staticAnchorThickness), relativeToRect.y(), widgetLength, relativeToRect.height());
|
||||
break;
|
||||
case Location_OnBottom:
|
||||
result = QRect(relativeToRect.x(), qMin(relativeToRect.bottom() + 1 - side1Length + newAnchorThickness,
|
||||
height() - widgetLength - staticAnchorThickness),
|
||||
relativeToRect.width(), widgetLength);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
qCDebug(sizing) << "MultiSplitterLayout::rectForDrop rect=" << result
|
||||
<< "; result.bottomRight=" << result.bottomRight()
|
||||
<< "; location=" << location
|
||||
<< "; s1=" << side1Length
|
||||
<< "; relativeToRect.bottomRight=" << relativeToRect.bottomRight();
|
||||
return result;
|
||||
}
|
||||
|
||||
QRect MultiSplitterLayout::rectForDrop(const QWidgetOrQuick *widgetBeingDropped, Location location,
|
||||
const Item *relativeTo) const
|
||||
{
|
||||
Q_ASSERT(widgetBeingDropped);
|
||||
Length lfd = lengthForDrop(widgetBeingDropped, location, relativeTo);
|
||||
const bool needsMoreSpace = lfd.isNull();
|
||||
if (needsMoreSpace) {
|
||||
// This is the case with the drop indicators. If there's not enough space let's still
|
||||
// draw some indicator drop. The window will resize to accommodate the drop.
|
||||
lfd.side1Length = INDICATOR_MINIMUM_LENGTH / 2;
|
||||
lfd.side2Length = INDICATOR_MINIMUM_LENGTH - lfd.side1Length;
|
||||
}
|
||||
|
||||
const int staticAnchorThickness = Anchor::thickness(/**static=*/true);
|
||||
const bool relativeToThis = relativeTo == nullptr;
|
||||
const QRect relativeToRect = relativeToThis ? m_multiSplitter->rect().adjusted(staticAnchorThickness, staticAnchorThickness,
|
||||
-staticAnchorThickness, -staticAnchorThickness)
|
||||
: relativeTo->geometry();
|
||||
|
||||
// This function is split in two just so we can unit-test the math in the second one, which is more involved
|
||||
QRect result = rectForDrop(lfd, location, relativeToRect);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::setAnchorBeingDragged(Anchor *anchor)
|
||||
{
|
||||
m_anchorBeingDragged = anchor;
|
||||
}
|
||||
|
||||
Anchor::List MultiSplitterLayout::anchorsFollowing(Anchor *followee) const
|
||||
{
|
||||
if (!followee)
|
||||
return {};
|
||||
|
||||
Anchor::List followers;
|
||||
|
||||
for (Anchor *a : m_anchors) {
|
||||
if (a->followee() == followee)
|
||||
followers.push_back(a);
|
||||
}
|
||||
|
||||
return followers;
|
||||
}
|
||||
|
||||
int MultiSplitterLayout::numAchorsFollowing() const
|
||||
{
|
||||
int count = 0;
|
||||
for (Anchor *a : m_anchors) {
|
||||
if (a->isFollowing())
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int MultiSplitterLayout::numVisibleAnchors() const
|
||||
{
|
||||
int count = 0;
|
||||
@@ -819,193 +556,15 @@ int MultiSplitterLayout::numVisibleAnchors() const
|
||||
return count;
|
||||
}
|
||||
|
||||
Anchor *MultiSplitterLayout::staticAnchor(Anchor::Type type) const
|
||||
{
|
||||
if (type == Anchor::Type_TopStatic)
|
||||
return m_topAnchor;
|
||||
|
||||
if (type == Anchor::Type_BottomStatic)
|
||||
return m_bottomAnchor;
|
||||
|
||||
if (type == Anchor::Type_LeftStatic)
|
||||
return m_leftAnchor;
|
||||
|
||||
if (type == Anchor::Type_RightStatic)
|
||||
return m_rightAnchor;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::dumpDebug() const
|
||||
{
|
||||
Q_EMIT aboutToDumpDebug();
|
||||
qDebug() << Q_FUNC_INFO << "m_size=" << m_size
|
||||
<< "; minimumSize=" << minimumSize()
|
||||
<< "; parentWidget.size=" << multiSplitter()->size()
|
||||
<< "; window=" << multiSplitter()->window()
|
||||
<< "; window.size=" << multiSplitter()->window()->size();
|
||||
|
||||
qDebug() << "Items:";
|
||||
for (auto item : items()) {
|
||||
qDebug() <<" " << item
|
||||
<< "; min.width=" << item->minLength(Qt::Vertical)
|
||||
<< "; min.height=" << item->minLength(Qt::Horizontal)
|
||||
<< "; geometry=" << item->geometry()
|
||||
<< "; isPlaceholder=" << item->isPlaceholder()
|
||||
<< "; refCount=" << item->refCount();
|
||||
|
||||
if (Frame *frame = item->frame())
|
||||
frame->dumpDebug();
|
||||
}
|
||||
|
||||
qDebug() << "Anchors:";
|
||||
for (Anchor *anchor : m_anchors) {
|
||||
auto side1Widgets = anchor->items(Anchor::Side1);
|
||||
auto side2Widgets = anchor->items(Anchor::Side2);
|
||||
auto bounds = anchor->isStatic() ? QPair<int, int>() : boundPositionsForAnchor(anchor);
|
||||
qDebug() << "\n " << anchor
|
||||
<< "; side1=" << side1Widgets
|
||||
<< "; side2=" << side2Widgets
|
||||
<< "; pos=" << anchor->position()
|
||||
<< "; sepWidget.pos=" << (anchor->isVertical() ? anchor->separatorWidget()->x()
|
||||
: anchor->separatorWidget()->y())
|
||||
<< "; sepWidget.visible=" << anchor->separatorWidget()->isVisible()
|
||||
<< "; geo=" << anchor->geometry()
|
||||
<< "; sep.geo=" << anchor->separatorWidget()->geometry()
|
||||
<< "; bounds=" << bounds
|
||||
<< "; orientation=" << anchor->orientation()
|
||||
<< "; isFollowing=" << anchor->isFollowing()
|
||||
<< "; followee=" << anchor->followee()
|
||||
<< "; from=" << ((void*)anchor->from())
|
||||
<< "; to=" << ((void*)anchor->to())
|
||||
<< "; positionPercentage=" << anchor->positionPercentage();
|
||||
}
|
||||
|
||||
qDebug() << "Num Frame:" << Frame::dbg_numFrames();
|
||||
qDebug() << "Num FloatingWindow:" << FloatingWindow::dbg_numFrames();
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::positionStaticAnchors()
|
||||
{
|
||||
qCDebug(sizing) << Q_FUNC_INFO;
|
||||
m_leftAnchor->setPosition(0);
|
||||
m_topAnchor->setPosition(0);
|
||||
m_bottomAnchor->setPosition(height() - m_bottomAnchor->thickness());
|
||||
m_rightAnchor->setPosition(width() - m_rightAnchor->thickness());
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::redistributeSpace()
|
||||
{
|
||||
positionStaticAnchors();
|
||||
redistributeSpace_recursive(m_leftAnchor, 0);
|
||||
redistributeSpace_recursive(m_topAnchor, 0);
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::redistributeSpace(QSize oldSize, QSize newSize)
|
||||
{
|
||||
positionStaticAnchors();
|
||||
if (oldSize == newSize || !oldSize.isValid() || !newSize.isValid())
|
||||
return;
|
||||
|
||||
qCDebug(sizing) << Q_FUNC_INFO << "old=" << oldSize << "; new=" << newSize;
|
||||
|
||||
const bool widthChanged = oldSize.width() != newSize.width();
|
||||
const bool heightChanged = oldSize.height() != newSize.height();
|
||||
|
||||
if (widthChanged)
|
||||
redistributeSpace_recursive(m_leftAnchor, 0);
|
||||
if (heightChanged)
|
||||
redistributeSpace_recursive(m_topAnchor, 0);
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::redistributeSpace_recursive(Anchor *fromAnchor, int minAnchorPos)
|
||||
{
|
||||
for (Item *item : fromAnchor->items(Anchor::Side2)) {
|
||||
Anchor *nextAnchor = item->anchorAtSide(Anchor::Side2, fromAnchor->orientation());
|
||||
if (nextAnchor->isStatic())
|
||||
continue;
|
||||
|
||||
// We use the minPos of the Anchor that had non-placeholder items on its side1.
|
||||
if (nextAnchor->hasNonPlaceholderItems(Anchor::Side1))
|
||||
minAnchorPos = nextAnchor->minPosition();
|
||||
|
||||
if (nextAnchor->hasNonPlaceholderItems(Anchor::Side2) && !nextAnchor->isFollowing()) {
|
||||
const int newPosition = int(nextAnchor->positionPercentage() * length(nextAnchor->orientation()));
|
||||
|
||||
// But don't let the anchor go out of bounds, it must respect its widgets min sizes
|
||||
auto bounds = boundPositionsForAnchor(nextAnchor);
|
||||
|
||||
// For the bounding, use Anchor::minPosition, as we're not making the anchors on the left/top shift, which boundsPositionsForAnchor() assumes.
|
||||
const int newPositionBounded = qMax(bounds.first, qBound(minAnchorPos, newPosition, bounds.second));
|
||||
|
||||
qCDebug(sizing) << Q_FUNC_INFO << nextAnchor << "bounds.first=" << bounds.first
|
||||
<< "; newPosition=" << newPosition
|
||||
<< "; bounds.first=" << bounds.first
|
||||
<< "; bounds.second=" << bounds.second
|
||||
<< "; newPositionBounded=" << newPositionBounded
|
||||
<< "; oldPosition=" << nextAnchor->position()
|
||||
<< "; size=" << m_size
|
||||
<< "; nextAnchor.minPosition=" << minAnchorPos;
|
||||
|
||||
nextAnchor->setPosition(newPositionBounded, Anchor::SetPositionOption_DontRecalculatePercentage);
|
||||
}
|
||||
|
||||
redistributeSpace_recursive(nextAnchor, minAnchorPos);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::updateSizeConstraints()
|
||||
{
|
||||
const int minH = m_topAnchor->cumulativeMinLength(Anchor::Side2);
|
||||
const int minW = m_leftAnchor->cumulativeMinLength(Anchor::Side2);
|
||||
|
||||
const QSize newMinSize = QSize(minW, minH);
|
||||
const QSize newMinSize = m_rootItem->minSize();
|
||||
qCDebug(sizing) << Q_FUNC_INFO << "Updating size constraints from" << m_minSize
|
||||
<< "to" << newMinSize;
|
||||
|
||||
setMinimumSize(newMinSize);
|
||||
}
|
||||
|
||||
int MultiSplitterLayout::wastedSpacing(Qt::Orientation orientation) const
|
||||
{
|
||||
// Wasted spacing due to using splitters:
|
||||
int numAnchors = 0;
|
||||
for (Anchor *anchor : m_anchors) {
|
||||
if (anchor->orientation() == orientation)
|
||||
numAnchors++;
|
||||
}
|
||||
|
||||
return (2 * Anchor::thickness(/*static=*/ true)) +
|
||||
((numAnchors - 2) * Anchor::thickness(/*static=*/ false)); // 2 of the anchors are always static
|
||||
}
|
||||
|
||||
AnchorGroup MultiSplitterLayout::staticAnchorGroup() const
|
||||
{
|
||||
return m_staticAnchorGroup;
|
||||
}
|
||||
|
||||
Anchor::List MultiSplitterLayout::anchors(Qt::Orientation orientation, bool includeStatic,
|
||||
bool includePlaceholders) const
|
||||
{
|
||||
Anchor::List result;
|
||||
for (Anchor *anchor : m_anchors) {
|
||||
if ((includeStatic || !anchor->isStatic()) && (includePlaceholders || !anchor->isFollowing()) && anchor->orientation() == orientation)
|
||||
result << anchor;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::blockItemPropagateGeo(bool block)
|
||||
{
|
||||
for (Item *item : m_items) {
|
||||
if (block)
|
||||
item->beginBlockPropagateGeo();
|
||||
else
|
||||
item->endBlockPropagateGeo();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::emitVisibleWidgetCountChanged()
|
||||
{
|
||||
if (!m_inDestructor)
|
||||
@@ -1040,7 +599,7 @@ Frame::List MultiSplitterLayout::frames() const
|
||||
Frame::List result;
|
||||
|
||||
for (Item *item : m_items) {
|
||||
if (Frame *f = item->frame())
|
||||
if (auto f = static_cast<Frame*>(item->frame()))
|
||||
result.push_back(f);
|
||||
}
|
||||
|
||||
@@ -1063,22 +622,6 @@ bool MultiSplitterLayout::checkSanity() const
|
||||
return m_rootItem->checkSanity();
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::ensureHasAvailableSize(QSize needed)
|
||||
{
|
||||
const QSize availableSize = this->availableSize();
|
||||
|
||||
qCDebug(placeholder) << Q_FUNC_INFO << "; needed=" << needed << availableSize;
|
||||
|
||||
const int deltaWidth = needed.width() > availableSize.width() ? (needed.width() - availableSize.width())
|
||||
: 0;
|
||||
|
||||
const int deltaHeight = needed.height() > availableSize.height() ? (needed.height() - availableSize.height())
|
||||
: 0;
|
||||
|
||||
const QSize newSize = size() + QSize(deltaWidth, deltaHeight);
|
||||
setSize(newSize);
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::unrefOldPlaceholders(const Frame::List &framesBeingAdded) const
|
||||
{
|
||||
for (Frame *frame : framesBeingAdded) {
|
||||
@@ -1151,107 +694,6 @@ void MultiSplitterLayout::setMinimumSize(QSize sz)
|
||||
qCDebug(sizing) << Q_FUNC_INFO << "minSize = " << m_minSize;
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::clearAnchorsFollowing()
|
||||
{
|
||||
for (Anchor *anchor : qAsConst(m_anchors))
|
||||
anchor->setFollowee(nullptr);
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::updateAnchorFollowing(const AnchorGroup &groupBeingRemoved)
|
||||
{
|
||||
clearAnchorsFollowing();
|
||||
|
||||
QHash<Anchor *, int> newPositionsWhenGroupRemoved;
|
||||
|
||||
for (Anchor *anchor : qAsConst(m_anchors)) {
|
||||
if (anchor->isStatic())
|
||||
continue;
|
||||
|
||||
if (anchor->onlyHasPlaceholderItems(Anchor::Side2)) {
|
||||
Anchor *toFollow = anchor->findNearestAnchorWithItems(Anchor::Side2);
|
||||
if (toFollow->followee() != anchor) {
|
||||
if (!toFollow->isStatic() && groupBeingRemoved.containsAnchor(anchor, Anchor::Side1)) {
|
||||
// A group is being removed, instead of simply shifting the left/top anchor all the way, let's make it use half the space
|
||||
if (toFollow->onlyHasPlaceholderItems(Anchor::Side1)) { // Means it can move!
|
||||
const int delta = toFollow->position() - anchor->position() - anchor->thickness();
|
||||
const int halfDelta = int(delta / 2.0);
|
||||
if (halfDelta > 0) {
|
||||
newPositionsWhenGroupRemoved.insert(toFollow, toFollow->position() - halfDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchor->setFollowee(toFollow);
|
||||
}
|
||||
} else if (anchor->onlyHasPlaceholderItems(Anchor::Side1)) {
|
||||
Anchor *toFollow = anchor->findNearestAnchorWithItems(Anchor::Side1);
|
||||
if (toFollow->followee() != anchor) {
|
||||
|
||||
if (!toFollow->isStatic() && groupBeingRemoved.containsAnchor(anchor, Anchor::Side2)) {
|
||||
// A group is being removed, instead of simply shifting the right/bottom anchor all the way, let's make it use half the space
|
||||
if (toFollow->onlyHasPlaceholderItems(Anchor::Side2)) { // Means it can move!
|
||||
const int delta = anchor->position() - toFollow->position() - toFollow->thickness();
|
||||
const int halfDelta = int(delta / 2.0);
|
||||
if (halfDelta > 0) {
|
||||
newPositionsWhenGroupRemoved.insert(toFollow, toFollow->position() + halfDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchor->setFollowee(toFollow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto it = newPositionsWhenGroupRemoved.begin(), end = newPositionsWhenGroupRemoved.end(); it != end; ++it) {
|
||||
Anchor *anchorToShift = it.key();
|
||||
const int newPosition = it.value();
|
||||
const Anchor::Side sideToShiftTo = newPosition < anchorToShift->position() ? Anchor::Side1
|
||||
: Anchor::Side2;
|
||||
bool doShift = true;
|
||||
for (Anchor *follower : anchorToShift->followers()) {
|
||||
if (follower->hasNonPlaceholderItems(sideToShiftTo) && !groupBeingRemoved.containsAnchor(follower, sideToShiftTo)) {
|
||||
doShift = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (doShift && !anchorToShift->isFollowing())
|
||||
anchorToShift->setPosition(newPosition);
|
||||
}
|
||||
|
||||
updateSizeConstraints();
|
||||
ensureAnchorsBounded();
|
||||
}
|
||||
|
||||
QHash<Anchor*, Anchor*> MultiSplitterLayout::anchorsShouldFollow() const
|
||||
{
|
||||
QHash<Anchor*, Anchor*> followers;
|
||||
|
||||
for (Anchor *anchor : m_anchors) {
|
||||
if (anchor->isStatic())
|
||||
continue;
|
||||
|
||||
if (anchor->onlyHasPlaceholderItems(Anchor::Side2)) {
|
||||
Anchor *toFollow = anchor->findNearestAnchorWithItems(Anchor::Side2);
|
||||
if (followers.value(toFollow) != anchor)
|
||||
followers.insert(anchor, toFollow);
|
||||
} else if (anchor->onlyHasPlaceholderItems(Anchor::Side1)) {
|
||||
Anchor *toFollow = anchor->findNearestAnchorWithItems(Anchor::Side1);
|
||||
if (followers.value(toFollow) != anchor)
|
||||
followers.insert(anchor, toFollow);
|
||||
}
|
||||
}
|
||||
|
||||
return followers;
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::insertAnchor(Anchor *anchor)
|
||||
{
|
||||
m_anchors.append(anchor);
|
||||
}
|
||||
|
||||
const ItemList MultiSplitterLayout::items() const
|
||||
{
|
||||
return m_items;
|
||||
@@ -1314,10 +756,6 @@ bool MultiSplitterLayout::deserialize(const LayoutSaver::MultiSplitterLayout &ms
|
||||
}
|
||||
}
|
||||
|
||||
m_staticAnchorGroup.left = m_leftAnchor;
|
||||
m_staticAnchorGroup.top = m_topAnchor;
|
||||
m_staticAnchorGroup.right = m_rightAnchor;
|
||||
m_staticAnchorGroup.bottom = m_bottomAnchor;
|
||||
|
||||
m_items.clear(); // Now properly set the items, which installs needed event filters, etc.
|
||||
addItems_internal(items, false, false); // Add the items only after we have the static anchors set
|
||||
|
||||
@@ -58,28 +58,6 @@ inline int widgetLength(const T *w, Qt::Orientation orientation)
|
||||
return (orientation == Qt::Vertical) ? w->width() : w->height();
|
||||
}
|
||||
|
||||
inline int lengthFromSize(QSize sz, Qt::Orientation orientation)
|
||||
{
|
||||
return orientation == Qt::Vertical ? sz.width()
|
||||
: sz.height();
|
||||
}
|
||||
|
||||
inline Anchor::Side sideForLocation(Location loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case KDDockWidgets::Location_OnLeft:
|
||||
case KDDockWidgets::Location_OnTop:
|
||||
return Anchor::Side1;
|
||||
case KDDockWidgets::Location_OnRight:
|
||||
case KDDockWidgets::Location_OnBottom:
|
||||
return Anchor::Side2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Anchor::Side_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* A MultiSplitter is like a QSplitter but supports mixing vertical and horizontal splitters in
|
||||
* any combination.
|
||||
@@ -223,22 +201,9 @@ public:
|
||||
///@brief returns list of separators
|
||||
const Anchor::List anchors() const { return m_anchors; }
|
||||
|
||||
/**
|
||||
* @brief Returns the list of anchors that are following @p followee
|
||||
*/
|
||||
Anchor::List anchorsFollowing(Anchor *followee) const;
|
||||
|
||||
///@brief returns the number of anchors that are following others, just for tests.
|
||||
int numAchorsFollowing() const;
|
||||
|
||||
///@brief returns the number of anchors that are following others, just for tests.
|
||||
int numVisibleAnchors() const;
|
||||
|
||||
///@brief returns either the left, top, right or bottom separator, depending on the @p type
|
||||
Anchor *staticAnchor(Anchor::Type type) const;
|
||||
|
||||
Anchor *staticAnchor(Anchor::Side side, Qt::Orientation orientation) const;
|
||||
|
||||
///@brief a function that all code paths adding Items will call.
|
||||
///It's mostly for code reuse, so we don't duplicate what's done here. But it's also nice to
|
||||
///have a central place that we know will be called
|
||||
@@ -421,47 +386,6 @@ private:
|
||||
|
||||
void emitVisibleWidgetCountChanged();
|
||||
|
||||
/**
|
||||
* @brief Returns the size that the widget will get when dropped at this specific location.
|
||||
*
|
||||
* When location is Left or Right then the length represents a width, otherwise an height.
|
||||
* This function is also called to know the size of the rubberband when hovering over a location.
|
||||
*/
|
||||
MultiSplitterLayout::Length lengthForDrop(const QWidgetOrQuick *widget, KDDockWidgets::Location location,
|
||||
const Item *relativeTo) const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ensures that this layout's size is enough for dropping @p widget to @p location,
|
||||
* relative to @p relativeToItem.
|
||||
*
|
||||
* It may increase size or do notying, never decrease.
|
||||
*/
|
||||
void ensureEnoughSize(const QWidgetOrQuick *widget, KDDockWidgets::Location location,
|
||||
const Item *relativeToItem);
|
||||
|
||||
|
||||
void insertAnchor(Anchor *);
|
||||
void removeAnchor(Anchor *);
|
||||
|
||||
/**
|
||||
* Returns the min or max position that an anchor can go to (due to minimum size restriction on the widgets).
|
||||
* For example, if the anchor is vertical and direction is Side1 then it returns the minimum x
|
||||
* that the anchor can have. If direction is Side2 then it returns the maximum width. If horizontal
|
||||
* then the height.
|
||||
*/
|
||||
int boundPositionForAnchor(Anchor *, Anchor::Side direction) const;
|
||||
|
||||
/**
|
||||
* Similar to boundPositionForAnchor, but returns both the min and the max width (or height)
|
||||
*/
|
||||
QPair<int, int> boundPositionsForAnchor(Anchor *) const;
|
||||
|
||||
/**
|
||||
* @brief similar to @ref boundPositionsForAnchor but returns for all anchors
|
||||
*/
|
||||
QHash<Anchor*, QPair<int,int>> boundPositionsForAllAnchors() const;
|
||||
|
||||
/** Returns how much is available for the new drop. It already counts with the space for new anchor that will be created.
|
||||
* So it returns this layout's width() (or height), minus the minimum-sizes of all widgets, minus the thickness of all anchors
|
||||
* minus the thickness of the anchor that would be created.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Item.h"
|
||||
#include "Item_p.h"
|
||||
#include <QtTest/QtTest>
|
||||
#include <memory.h>
|
||||
|
||||
@@ -83,7 +83,7 @@ void TestMultiSplitter::tst_insertOne()
|
||||
{
|
||||
auto root = createRoot();
|
||||
auto item = createItem("1");
|
||||
root->insertItem(item, Location_Top);
|
||||
root->insertItem(item, Location_OnTop);
|
||||
QCOMPARE(root->numChildren(), 1);
|
||||
QVERIFY(item->isWidget());
|
||||
QVERIFY(!item->isContainer());
|
||||
@@ -104,9 +104,9 @@ void TestMultiSplitter::tst_insertThreeSideBySide()
|
||||
auto item2 = new Item();
|
||||
auto item3 = new Item();
|
||||
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
root->insertItem(item3, Location_Right);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
root->insertItem(item3, Location_OnRight);
|
||||
|
||||
QVERIFY(root->checkSanity());
|
||||
QCOMPARE(root->numChildren(), 3);
|
||||
@@ -121,9 +121,9 @@ void TestMultiSplitter::tst_insertOnWidgetItem1()
|
||||
auto item1 = new Item();
|
||||
auto item2 = new Item();
|
||||
auto item3 = new Item();
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
|
||||
QVERIFY(item3->x() > item2->x());
|
||||
QCOMPARE(item3->y(), item2->y());
|
||||
@@ -140,9 +140,9 @@ void TestMultiSplitter::tst_insertOnWidgetItem2()
|
||||
auto item1 = new Item();
|
||||
auto item2 = new Item();
|
||||
auto item3 = new Item();
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Left);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnLeft);
|
||||
|
||||
QVERIFY(item1->x() < item3->x());
|
||||
QVERIFY(item3->x() < item2->x());
|
||||
@@ -162,10 +162,10 @@ void TestMultiSplitter::tst_insertOnWidgetItem1DifferentOrientation()
|
||||
auto item2 = createItem("2");
|
||||
auto item3 = createItem("3");
|
||||
auto item31 = createItem("3.2");
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
|
||||
auto container3 = item3->parentContainer();
|
||||
QVERIFY(container3->isContainer());
|
||||
@@ -204,12 +204,12 @@ void TestMultiSplitter::tst_insertOnWidgetItem2DifferentOrientation()
|
||||
auto item3 = createItem("3");
|
||||
auto item31 = createItem("3.1");
|
||||
auto item32 = createItem("3.2");
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
auto container3Parent = item3->parentContainer();
|
||||
item3->insertItem(item32, Location_Right);
|
||||
item3->insertItem(item32, Location_OnRight);
|
||||
auto container3 = item3->parentContainer();
|
||||
|
||||
QCOMPARE(container3->parentContainer(), container3Parent);
|
||||
@@ -256,12 +256,12 @@ void TestMultiSplitter::tst_insertOnRootDifferentOrientation()
|
||||
auto item31 = createItem("3.1");
|
||||
auto item32 = createItem("3.2");
|
||||
auto item4 = createItem("4");
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
item3->insertItem(item32, Location_Right);
|
||||
root->insertItem(item4, Location_Top);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
item3->insertItem(item32, Location_OnRight);
|
||||
root->insertItem(item4, Location_OnTop);
|
||||
|
||||
QCOMPARE(item4->parentContainer(), root.get());
|
||||
QCOMPARE(item4->pos(), root->pos());
|
||||
@@ -283,12 +283,12 @@ void TestMultiSplitter::tst_removeItem1()
|
||||
auto item31 = createItem("3.1");
|
||||
auto item32 = createItem("3.2");
|
||||
auto item4 = createItem("4");
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
item3->insertItem(item32, Location_Right);
|
||||
root->insertItem(item4, Location_Top);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
item3->insertItem(item32, Location_OnRight);
|
||||
root->insertItem(item4, Location_OnTop);
|
||||
|
||||
QCOMPARE(root->numChildren(), 2);
|
||||
root->removeItem(item4);
|
||||
@@ -322,10 +322,10 @@ void TestMultiSplitter::tst_removeItem2()
|
||||
auto item2 = createItem("2");
|
||||
auto item3 = createItem("3");
|
||||
auto item31 = createItem("3.1");
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item3, Location_Right);
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item3, Location_OnRight);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
item31->parentContainer()->removeItem(item31);
|
||||
item3->parentContainer()->removeItem(item3);
|
||||
}
|
||||
@@ -341,9 +341,9 @@ void TestMultiSplitter::tst_minSize()
|
||||
item2->m_sizingInfo.minSize = {200, 300};
|
||||
item22->m_sizingInfo.minSize = {100, 100};
|
||||
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
item2->insertItem(item22, Location_Bottom);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
item2->insertItem(item22, Location_OnBottom);
|
||||
|
||||
QCOMPARE(item2->minSize(), QSize(200, 300));
|
||||
QCOMPARE(item2->parentContainer()->minSize(), QSize(200, 300+100+st));
|
||||
@@ -359,9 +359,9 @@ void TestMultiSplitter::tst_resize()
|
||||
auto item3 = createItem("3");
|
||||
auto item31 = createItem("31");
|
||||
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
root->insertItem(item3, Location_Right);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
root->insertItem(item3, Location_OnRight);
|
||||
|
||||
const int item1Percentage = item1->width() / root->width();
|
||||
const int item2Percentage = item1->width() / root->width();
|
||||
@@ -379,7 +379,7 @@ void TestMultiSplitter::tst_resize()
|
||||
QCOMPARE(item2->height(), 505);
|
||||
QCOMPARE(item3->height(), 505);
|
||||
|
||||
item3->insertItem(item31, Location_Bottom);
|
||||
item3->insertItem(item31, Location_OnBottom);
|
||||
|
||||
QVERIFY(root->checkSanity());
|
||||
root->resize({2500, 505});
|
||||
@@ -394,7 +394,7 @@ void TestMultiSplitter::tst_resizeWithConstraints()
|
||||
auto root = createRoot();
|
||||
auto item1 = createItem("1");
|
||||
item1->setMinSize(QSize(500, 500));
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
QVERIFY(root->checkSanity());
|
||||
|
||||
root->resize(item1->minSize()); // Still fits
|
||||
@@ -411,9 +411,9 @@ void TestMultiSplitter::tst_resizeWithConstraints()
|
||||
item1->setMinSize(QSize(500, 500));
|
||||
item2->setMinSize(QSize(500, 500));
|
||||
item3->setMinSize(QSize(500, 500));
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item2, Location_Right);
|
||||
root->insertItem(item3, Location_Right);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
root->insertItem(item3, Location_OnRight);
|
||||
QVERIFY(root->checkSanity());
|
||||
|
||||
// TODO: Resize further
|
||||
@@ -433,7 +433,7 @@ void TestMultiSplitter::tst_availableSize()
|
||||
item2->m_sizingInfo.minSize = {100, 100};
|
||||
item3->m_sizingInfo.minSize = {100, 100};
|
||||
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
QCOMPARE(root->availableSize(), QSize(900, 900));
|
||||
QCOMPARE(root->minSize(), QSize(100, 100));
|
||||
QCOMPARE(root->neighboursLengthFor(item1, Side1, Qt::Horizontal), 0);
|
||||
@@ -450,7 +450,7 @@ void TestMultiSplitter::tst_availableSize()
|
||||
QCOMPARE(root->neighboursLengthFor_recursive(item1, Side1, Qt::Horizontal), 0);
|
||||
QCOMPARE(root->neighboursLengthFor_recursive(item1, Side2, Qt::Horizontal), 0);
|
||||
|
||||
root->insertItem(item2, Location_Left);
|
||||
root->insertItem(item2, Location_OnLeft);
|
||||
QCOMPARE(root->availableSize(), QSize(800 - st, 900));
|
||||
QCOMPARE(root->minSize(), QSize(200 + st, 100));
|
||||
QCOMPARE(root->neighboursLengthFor(item1, Side1, Qt::Horizontal), item2->width());
|
||||
@@ -467,7 +467,7 @@ void TestMultiSplitter::tst_availableSize()
|
||||
QCOMPARE(root->neighboursLengthFor_recursive(item1, Side1, Qt::Horizontal), item2->width());
|
||||
QCOMPARE(root->neighboursLengthFor_recursive(item1, Side2, Qt::Horizontal), 0);
|
||||
|
||||
root->insertItem(item3, Location_Bottom);
|
||||
root->insertItem(item3, Location_OnBottom);
|
||||
QCOMPARE(root->availableSize(), QSize(800 - st, 800 - st));
|
||||
QCOMPARE(root->minSize(), QSize(200 + st, 100 + 100 + st));
|
||||
QCOMPARE(item3->parentContainer()->neighboursMinLengthFor(item3, Side1, Qt::Vertical), item1->minSize().height());
|
||||
@@ -481,8 +481,8 @@ void TestMultiSplitter::tst_availableSize()
|
||||
// More nesting
|
||||
auto item4 = createItem("4");
|
||||
auto item5 = createItem("5");
|
||||
item3->insertItem(item4, Location_Right);
|
||||
item4->insertItem(item5, Location_Bottom);
|
||||
item3->insertItem(item4, Location_OnRight);
|
||||
item4->insertItem(item5, Location_OnBottom);
|
||||
|
||||
auto container4 = item4->parentContainer();
|
||||
QCOMPARE(container4->neighboursLengthFor_recursive(item4, Side1, Qt::Vertical), item1->height());
|
||||
@@ -526,7 +526,7 @@ void TestMultiSplitter::tst_missingSize()
|
||||
QCOMPARE(root->missingSizeFor(item3, Qt::Vertical), QSize(100, 200));
|
||||
|
||||
// Test with an existing item
|
||||
root->insertItem(item1, Location_Top);
|
||||
root->insertItem(item1, Location_OnTop);
|
||||
QCOMPARE(root->missingSizeFor(item2, Qt::Vertical), item1->minSize() + QSize(0, st));
|
||||
QCOMPARE(root->missingSizeFor(item3, Qt::Vertical), item1->minSize() + QSize(0, st) + QSize(100, 200));
|
||||
}
|
||||
@@ -541,7 +541,7 @@ void TestMultiSplitter::tst_ensureEnoughSize()
|
||||
|
||||
// Insert to empty layout:
|
||||
|
||||
root->insertItem(item1, Location_Left);
|
||||
root->insertItem(item1, Location_OnLeft);
|
||||
QCOMPARE(root->size(), QSize(2000, 1000));
|
||||
QCOMPARE(item1->size(), QSize(2000, 1000));
|
||||
QCOMPARE(item1->minSize(), root->minSize());
|
||||
@@ -550,7 +550,7 @@ void TestMultiSplitter::tst_ensureEnoughSize()
|
||||
// Insert to non-empty layout
|
||||
Item *item2 = createItem("2");
|
||||
item2->setMinSize({2000, 2000});
|
||||
root->insertItem(item2, Location_Right);
|
||||
root->insertItem(item2, Location_OnRight);
|
||||
QVERIFY(root->checkSanity());
|
||||
QCOMPARE(root->size(), QSize(item1->minSize().width() + item2->minSize().width() + st, item2->minSize().height()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user