Improve SplitView resizing behavior
- Adjust sizes before applying them into items to keep the items inside minimum and maximum sizes. Also avoids calling items setWidth/setHeight multiple times during layout. - Enforce items staying inside SplitView size instead of having preferred size initially and enfocing only after handle resizes. - Don't resize handles in componentComplete as sizes are not correct yet and initial resizing is done in createHandleItem. - Adjust autotests to pass now that size limits are enforced. Width getting value of preferredWidth requires polish. Task-number: QTBUG-89699 Task-number: QTBUG-107836 Pick-to: 6.6 Change-Id: Ifaf3a6b185014216baa0671c6d4912f99465d0d5 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
57eeb608db
commit
2e1dbd7e0c
|
|
@ -225,6 +225,9 @@ Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
|
||||||
Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
|
Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
|
||||||
Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
|
Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
|
||||||
|
|
||||||
|
/*
|
||||||
|
Updates m_fillIndex to be between 0 .. (item count - 1).
|
||||||
|
*/
|
||||||
void QQuickSplitViewPrivate::updateFillIndex()
|
void QQuickSplitViewPrivate::updateFillIndex()
|
||||||
{
|
{
|
||||||
const int count = contentModel->count();
|
const int count = contentModel->count();
|
||||||
|
|
@ -232,10 +235,9 @@ void QQuickSplitViewPrivate::updateFillIndex()
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
|
qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
|
||||||
|
|
||||||
m_fillIndex = -1;
|
int fillIndex = -1;
|
||||||
int i = 0;
|
|
||||||
int lastVisibleIndex = -1;
|
int lastVisibleIndex = -1;
|
||||||
for (; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
|
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
|
||||||
if (!item->isVisible())
|
if (!item->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -248,19 +250,21 @@ void QQuickSplitViewPrivate::updateFillIndex()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
|
if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
|
||||||
m_fillIndex = i;
|
fillIndex = i;
|
||||||
qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << m_fillIndex;
|
qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << fillIndex;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fillIndex == -1) {
|
if (fillIndex == -1) {
|
||||||
// If there was no item with fillWidth/fillHeight set, m_fillIndex will be -1,
|
// If there was no item with fillWidth/fillHeight set, fillIndex will be -1,
|
||||||
// and we'll set it to the last visible item.
|
// and we'll set m_fillIndex to the last visible item.
|
||||||
// If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
|
// If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
|
||||||
m_fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
|
fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
|
||||||
qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << m_fillIndex;
|
qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << fillIndex;
|
||||||
}
|
}
|
||||||
|
// Take new fillIndex into use.
|
||||||
|
m_fillIndex = fillIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -344,35 +348,9 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
|
||||||
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
|
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
|
||||||
const qreal newItemSize = newHandlePos - leftEdge;
|
const qreal newItemSize = newHandlePos - leftEdge;
|
||||||
|
|
||||||
// Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
|
// We still need to use requestedSize in the width/height call below,
|
||||||
// will be used on the next layout (when it's no longer being resized).
|
|
||||||
if (!attached) {
|
|
||||||
// Force the attached object to be created since we rely on it.
|
|
||||||
attached = qobject_cast<QQuickSplitViewAttached*>(
|
|
||||||
qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Users could conceivably respond to size changes in items by setting attached
|
|
||||||
SplitView properties:
|
|
||||||
|
|
||||||
onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
|
|
||||||
|
|
||||||
We handle this by doing another layout after the current layout if the
|
|
||||||
attached/implicit size properties are set during this layout. However, we also
|
|
||||||
need to set preferredWidth/Height here (for reasons mentioned in the comment above),
|
|
||||||
but we don't want this to count as a request for a delayed layout, so we guard against it.
|
|
||||||
*/
|
|
||||||
m_ignoreNextLayoutRequest = true;
|
|
||||||
|
|
||||||
if (horizontal)
|
|
||||||
attached->setPreferredWidth(newItemSize);
|
|
||||||
else
|
|
||||||
attached->setPreferredHeight(newItemSize);
|
|
||||||
|
|
||||||
// We still need to use requestedWidth in the setWidth() call below,
|
|
||||||
// because sizeData has already been calculated and now contains an old
|
// because sizeData has already been calculated and now contains an old
|
||||||
// effectivePreferredWidth value.
|
// effectivePreferredWidth/Height value.
|
||||||
requestedSize = newItemSize;
|
requestedSize = newItemSize;
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
|
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
|
||||||
|
|
@ -405,22 +383,7 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
|
||||||
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
|
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
|
||||||
const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
|
const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
|
||||||
|
|
||||||
// Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
|
// We still need to use requestedSize in the width/height call below,
|
||||||
// will be used on the next layout (when it's no longer being resized).
|
|
||||||
if (!attached) {
|
|
||||||
// Force the attached object to be created since we rely on it.
|
|
||||||
attached = qobject_cast<QQuickSplitViewAttached*>(
|
|
||||||
qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ignoreNextLayoutRequest = true;
|
|
||||||
|
|
||||||
if (horizontal)
|
|
||||||
attached->setPreferredWidth(newItemSize);
|
|
||||||
else
|
|
||||||
attached->setPreferredHeight(newItemSize);
|
|
||||||
|
|
||||||
// We still need to use requestedSize in the setWidth()/setHeight() call below,
|
|
||||||
// because sizeData has already been calculated and now contains an old
|
// because sizeData has already been calculated and now contains an old
|
||||||
// effectivePreferredWidth/Height value.
|
// effectivePreferredWidth/Height value.
|
||||||
requestedSize = newItemSize;
|
requestedSize = newItemSize;
|
||||||
|
|
@ -443,20 +406,28 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index != m_fillIndex) {
|
if (index != m_fillIndex) {
|
||||||
|
LayoutData layoutData;
|
||||||
if (horizontal) {
|
if (horizontal) {
|
||||||
item->setWidth(qBound(
|
layoutData.width = qBound(
|
||||||
sizeData.effectiveMinimumWidth,
|
sizeData.effectiveMinimumWidth,
|
||||||
requestedSize,
|
requestedSize,
|
||||||
sizeData.effectiveMaximumWidth));
|
sizeData.effectiveMaximumWidth);
|
||||||
item->setHeight(height);
|
layoutData.height = height;
|
||||||
} else {
|
} else {
|
||||||
item->setWidth(width);
|
layoutData.width = width;
|
||||||
item->setHeight(qBound(
|
layoutData.height = qBound(
|
||||||
sizeData.effectiveMinimumHeight,
|
sizeData.effectiveMinimumHeight,
|
||||||
requestedSize,
|
requestedSize,
|
||||||
sizeData.effectiveMaximumHeight));
|
sizeData.effectiveMaximumHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark that this item has been manually resized. After this
|
||||||
|
// we can override the preferredWidth & preferredHeight
|
||||||
|
if (isBeingResized)
|
||||||
|
layoutData.wasResizedByHandle = true;
|
||||||
|
|
||||||
|
m_layoutData.insert(item, layoutData);
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
|
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
|
||||||
<< " (effective"
|
<< " (effective"
|
||||||
<< " minW=" << sizeData.effectiveMinimumWidth
|
<< " minW=" << sizeData.effectiveMinimumWidth
|
||||||
|
|
@ -468,9 +439,9 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
|
||||||
|
|
||||||
// Keep track of how much space has been used so far.
|
// Keep track of how much space has been used so far.
|
||||||
if (horizontal)
|
if (horizontal)
|
||||||
usedWidth += item->width();
|
usedWidth += layoutData.width;
|
||||||
else
|
else
|
||||||
usedHeight += item->height();
|
usedHeight += layoutData.height;
|
||||||
} else if (indexBeingResizedDueToDrag != m_fillIndex) {
|
} else if (indexBeingResizedDueToDrag != m_fillIndex) {
|
||||||
// The fill item is resized afterwards, outside of the loop.
|
// The fill item is resized afterwards, outside of the loop.
|
||||||
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
|
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
|
||||||
|
|
@ -526,20 +497,26 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
|
||||||
const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
|
const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
|
qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
|
||||||
const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
|
const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
|
||||||
|
|
||||||
|
LayoutData layoutData;
|
||||||
if (isHorizontal()) {
|
if (isHorizontal()) {
|
||||||
fillItem->setWidth(qBound(
|
layoutData.width = qBound(
|
||||||
fillSizeData.effectiveMinimumWidth,
|
fillSizeData.effectiveMinimumWidth,
|
||||||
width - usedWidth,
|
width - usedWidth,
|
||||||
fillSizeData.effectiveMaximumWidth));
|
fillSizeData.effectiveMaximumWidth);
|
||||||
fillItem->setHeight(height);
|
layoutData.height = height;
|
||||||
|
usedWidth += layoutData.width;
|
||||||
} else {
|
} else {
|
||||||
fillItem->setWidth(width);
|
layoutData.width = width;
|
||||||
fillItem->setHeight(qBound(
|
layoutData.height = qBound(
|
||||||
fillSizeData.effectiveMinimumHeight,
|
fillSizeData.effectiveMinimumHeight,
|
||||||
height - usedHeight,
|
height - usedHeight,
|
||||||
fillSizeData.effectiveMaximumHeight));
|
fillSizeData.effectiveMaximumHeight);
|
||||||
|
usedHeight += layoutData.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_layoutData.insert(fillItem, layoutData);
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
|
qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
|
||||||
<< ": resized split fill item " << fillItem << " (effective"
|
<< ": resized split fill item " << fillItem << " (effective"
|
||||||
<< " minW=" << fillSizeData.effectiveMinimumWidth
|
<< " minW=" << fillSizeData.effectiveMinimumWidth
|
||||||
|
|
@ -548,6 +525,91 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
|
||||||
<< ", maxH=" << fillSizeData.effectiveMaximumHeight << ")";
|
<< ", maxH=" << fillSizeData.effectiveMaximumHeight << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Limit the sizes if needed and apply them into items.
|
||||||
|
*/
|
||||||
|
void QQuickSplitViewPrivate::limitAndApplySizes(qreal usedWidth, qreal usedHeight)
|
||||||
|
{
|
||||||
|
const int count = contentModel->count();
|
||||||
|
const bool horizontal = isHorizontal();
|
||||||
|
|
||||||
|
const qreal maxSize = horizontal ? width : height;
|
||||||
|
const qreal usedSize = horizontal ? usedWidth : usedHeight;
|
||||||
|
if (usedSize > maxSize) {
|
||||||
|
// If items don't fit, reduce the size of non-filled items from
|
||||||
|
// right to left / bottom to top. At this point filled item is
|
||||||
|
// already at its minimum size or usedSize wouldn't be > maxSize.
|
||||||
|
qreal delta = usedSize - maxSize;
|
||||||
|
for (int index = count - 1; index >= 0; --index) {
|
||||||
|
if (index == m_fillIndex)
|
||||||
|
continue;
|
||||||
|
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
|
||||||
|
if (!item->isVisible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
|
||||||
|
QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
|
||||||
|
qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
|
||||||
|
const auto sizeData = effectiveSizeData(itemPrivate, attached);
|
||||||
|
const qreal maxReduce = horizontal ?
|
||||||
|
m_layoutData[item].width - sizeData.effectiveMinimumWidth :
|
||||||
|
m_layoutData[item].height - sizeData.effectiveMinimumHeight;
|
||||||
|
|
||||||
|
const qreal reduce = std::min(maxReduce, delta);
|
||||||
|
if (horizontal)
|
||||||
|
m_layoutData[item].width -= reduce;
|
||||||
|
else
|
||||||
|
m_layoutData[item].height -= reduce;
|
||||||
|
|
||||||
|
delta -= reduce;
|
||||||
|
if (delta <= 0) {
|
||||||
|
// Now all the items fit, so continue
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new sizes into items
|
||||||
|
for (int index = 0; index < count; ++index) {
|
||||||
|
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
|
||||||
|
if (!item->isVisible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
|
||||||
|
qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
|
||||||
|
LayoutData layoutData = m_layoutData.value(item);
|
||||||
|
if (layoutData.wasResizedByHandle) {
|
||||||
|
// Modify the preferredWidth/Height, otherwise the original implicit/preferred size
|
||||||
|
// will be used on the next layout (when it's no longer being resized).
|
||||||
|
if (!attached) {
|
||||||
|
// Force the attached object to be created since we rely on it.
|
||||||
|
attached = qobject_cast<QQuickSplitViewAttached*>(
|
||||||
|
qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Users could conceivably respond to size changes in items by setting attached
|
||||||
|
SplitView properties:
|
||||||
|
|
||||||
|
onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
|
||||||
|
|
||||||
|
We handle this by doing another layout after the current layout if the
|
||||||
|
attached/implicit size properties are set during this layout. However, we also
|
||||||
|
need to set preferredWidth/Height here, otherwise the original implicit/preferred sizes
|
||||||
|
will be used on the next layout (when it's no longer being resized).
|
||||||
|
But we don't want this to count as a request for a delayed layout, so we guard against it.
|
||||||
|
*/
|
||||||
|
m_ignoreNextLayoutRequest = true;
|
||||||
|
if (horizontal)
|
||||||
|
attached->setPreferredWidth(layoutData.width);
|
||||||
|
else
|
||||||
|
attached->setPreferredHeight(layoutData.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
item->setWidth(layoutData.width);
|
||||||
|
item->setHeight(layoutData.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Positions items by laying them out in a row or column.
|
Positions items by laying them out in a row or column.
|
||||||
|
|
||||||
|
|
@ -621,6 +683,19 @@ void QQuickSplitViewPrivate::requestLayout()
|
||||||
q->polish();
|
q->polish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Layout steps are (horizontal SplitView as an example):
|
||||||
|
1) layoutResizeSplitItems: Gives each non-filled item its preferredWidth
|
||||||
|
or if not set, implicitWidth. Sizes are kept between effectiveMinimumWidth
|
||||||
|
and effectiveMaximumWidth and stored into layoutData for now.
|
||||||
|
2) layoutResizeFillItem: Gives filled item all the remaining space. Size is
|
||||||
|
kept between effectiveMinimumWidth and effectiveMaximumWidth and stored
|
||||||
|
into layoutData for now.
|
||||||
|
3) limitAndApplySizes: If we have used more space than SplitView item has,
|
||||||
|
start reducing non-filled item sizes from right-to-left. Reduce them up
|
||||||
|
to minimumWidth or until SplitView item width is reached. Finally set the
|
||||||
|
new item sizes from layoutData.
|
||||||
|
*/
|
||||||
void QQuickSplitViewPrivate::layout()
|
void QQuickSplitViewPrivate::layout()
|
||||||
{
|
{
|
||||||
if (!componentComplete)
|
if (!componentComplete)
|
||||||
|
|
@ -648,13 +723,15 @@ void QQuickSplitViewPrivate::layout()
|
||||||
qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
|
qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
|
||||||
<< (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
|
<< (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
|
||||||
|
|
||||||
|
// Total sizes of items used during the layout operation.
|
||||||
qreal usedWidth = 0;
|
qreal usedWidth = 0;
|
||||||
qreal usedHeight = 0;
|
qreal usedHeight = 0;
|
||||||
int indexBeingResizedDueToDrag = -1;
|
int indexBeingResizedDueToDrag = -1;
|
||||||
|
m_layoutData.clear();
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView) << " resizing:";
|
qCDebug(qlcQQuickSplitView) << " resizing:";
|
||||||
|
|
||||||
// First, resize the items. We need to do this first because otherwise fill
|
// First, resize the non-filled items. We need to do this first because otherwise fill
|
||||||
// items would take up all of the remaining space as soon as they are encountered.
|
// items would take up all of the remaining space as soon as they are encountered.
|
||||||
layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
|
layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
|
||||||
|
|
||||||
|
|
@ -666,6 +743,9 @@ void QQuickSplitViewPrivate::layout()
|
||||||
QQuickItem *fillItem = qobject_cast<QQuickItem*>(contentModel->object(m_fillIndex));
|
QQuickItem *fillItem = qobject_cast<QQuickItem*>(contentModel->object(m_fillIndex));
|
||||||
layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
|
layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
|
||||||
|
|
||||||
|
// Reduce the sizes still if needed and apply them into items.
|
||||||
|
limitAndApplySizes(usedWidth, usedHeight);
|
||||||
|
|
||||||
qCDebug(qlcQQuickSplitView) << " positioning:";
|
qCDebug(qlcQQuickSplitView) << " positioning:";
|
||||||
|
|
||||||
// Position the items.
|
// Position the items.
|
||||||
|
|
@ -1365,7 +1445,6 @@ void QQuickSplitView::componentComplete()
|
||||||
{
|
{
|
||||||
Q_D(QQuickSplitView);
|
Q_D(QQuickSplitView);
|
||||||
QQuickControl::componentComplete();
|
QQuickControl::componentComplete();
|
||||||
d->resizeHandles();
|
|
||||||
d->updateFillIndex();
|
d->updateFillIndex();
|
||||||
d->updatePolish();
|
d->updatePolish();
|
||||||
}
|
}
|
||||||
|
|
@ -1918,9 +1997,10 @@ void QQuickSplitViewAttached::resetMaximumHeight()
|
||||||
This attached property controls whether the item takes the remaining space
|
This attached property controls whether the item takes the remaining space
|
||||||
in the split view after all other items have been laid out.
|
in the split view after all other items have been laid out.
|
||||||
|
|
||||||
By default, the last visible child of the split view will have this set,
|
By default, the last visible child of the split view will fill the view,
|
||||||
but it can be changed by explicitly setting \c fillWidth to \c true on
|
but it can be changed by explicitly setting \c fillWidth to \c true on
|
||||||
another item.
|
another item. If multiple items have \c fillWidth set to \c true, the
|
||||||
|
left-most item will fill the view.
|
||||||
|
|
||||||
The width of a split item with \c fillWidth set to \c true is still
|
The width of a split item with \c fillWidth set to \c true is still
|
||||||
restricted within its \l minimumWidth and \l maximumWidth.
|
restricted within its \l minimumWidth and \l maximumWidth.
|
||||||
|
|
@ -1953,9 +2033,10 @@ void QQuickSplitViewAttached::setFillWidth(bool fill)
|
||||||
This attached property controls whether the item takes the remaining space
|
This attached property controls whether the item takes the remaining space
|
||||||
in the split view after all other items have been laid out.
|
in the split view after all other items have been laid out.
|
||||||
|
|
||||||
By default, the last visible child of the split view will have this set,
|
By default, the last visible child of the split view will fill the view,
|
||||||
but it can be changed by explicitly setting \c fillHeight to \c true on
|
but it can be changed by explicitly setting \c fillHeight to \c true on
|
||||||
another item.
|
another item. If multiple items have \c fillHeight set to \c true, the
|
||||||
|
top-most item will fill the view.
|
||||||
|
|
||||||
The height of a split item with \c fillHeight set to \c true is still
|
The height of a split item with \c fillHeight set to \c true is still
|
||||||
restricted within its \l minimumHeight and \l maximumHeight.
|
restricted within its \l minimumHeight and \l maximumHeight.
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ public:
|
||||||
void updateFillIndex();
|
void updateFillIndex();
|
||||||
void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag);
|
void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag);
|
||||||
void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag);
|
void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag);
|
||||||
|
void limitAndApplySizes(qreal usedWidth, qreal usedHeight);
|
||||||
void layoutPositionItems(const QQuickItem *fillItem);
|
void layoutPositionItems(const QQuickItem *fillItem);
|
||||||
void requestLayout();
|
void requestLayout();
|
||||||
void layout();
|
void layout();
|
||||||
|
|
@ -59,6 +60,13 @@ public:
|
||||||
qreal effectiveMaximumHeight;
|
qreal effectiveMaximumHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used during the layout
|
||||||
|
struct LayoutData {
|
||||||
|
qreal width = 0;
|
||||||
|
qreal height = 0;
|
||||||
|
bool wasResizedByHandle = false;
|
||||||
|
};
|
||||||
|
|
||||||
EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate,
|
EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate,
|
||||||
const QQuickSplitViewAttached *attached) const;
|
const QQuickSplitViewAttached *attached) const;
|
||||||
|
|
||||||
|
|
@ -80,6 +88,7 @@ public:
|
||||||
Qt::Orientation m_orientation = Qt::Horizontal;
|
Qt::Orientation m_orientation = Qt::Horizontal;
|
||||||
QQmlComponent *m_handle = nullptr;
|
QQmlComponent *m_handle = nullptr;
|
||||||
QList<QQuickItem*> m_handleItems;
|
QList<QQuickItem*> m_handleItems;
|
||||||
|
QHash<QQuickItem*, LayoutData> m_layoutData;
|
||||||
int m_hoveredHandleIndex = -1;
|
int m_hoveredHandleIndex = -1;
|
||||||
int m_pressedHandleIndex = -1;
|
int m_pressedHandleIndex = -1;
|
||||||
int m_nextVisibleIndexAfterPressedHandle = -1;
|
int m_nextVisibleIndexAfterPressedHandle = -1;
|
||||||
|
|
|
||||||
|
|
@ -431,8 +431,7 @@ TestCase {
|
||||||
var splitViewHeight = testCase.height
|
var splitViewHeight = testCase.height
|
||||||
var data = [
|
var data = [
|
||||||
{
|
{
|
||||||
// When the combined size of items is too large, the non-fill items should just exceed
|
// When the combined size of items is too large, make them fit.
|
||||||
// the size of the SplitView, exactly as they would were they in a RowLayout, for example.
|
|
||||||
tag: "fillItemOnLeft",
|
tag: "fillItemOnLeft",
|
||||||
expectedGeometries: [
|
expectedGeometries: [
|
||||||
// We're the fill item, but since the combined implicitWidths
|
// We're the fill item, but since the combined implicitWidths
|
||||||
|
|
@ -445,8 +444,8 @@ TestCase {
|
||||||
// Second handle.
|
// Second handle.
|
||||||
{ x: 200 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
|
{ x: 200 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
|
||||||
height: splitViewHeight },
|
height: splitViewHeight },
|
||||||
// The third item also gets its implicitWidth.
|
// The third item is reduced from its implicitWidth to fit into SplitView.
|
||||||
{ x: 200 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
|
{ x: 200 + defaultHorizontalHandleWidth * 2, y: 0, width: 200 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -463,8 +462,8 @@ TestCase {
|
||||||
// Second handle.
|
// Second handle.
|
||||||
{ x: 0, y: 200 + defaultVerticalHandleHeight, width: splitViewWidth,
|
{ x: 0, y: 200 + defaultVerticalHandleHeight, width: splitViewWidth,
|
||||||
height: defaultVerticalHandleHeight },
|
height: defaultVerticalHandleHeight },
|
||||||
// The third item also gets its implicitHeight.
|
// The third item is reduced from its implicitWidth to fit into SplitView.
|
||||||
{ x: 0, y: 200 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
|
{ x: 0, y: 200 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 - 2 * defaultVerticalHandleHeight }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1103,7 +1102,7 @@ TestCase {
|
||||||
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
||||||
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
|
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: 200 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
|
||||||
],
|
],
|
||||||
expectedGeometriesAfterDrag: [
|
expectedGeometriesAfterDrag: [
|
||||||
// The fill item is to the left of the handle at index 1, so the handle belongs
|
// The fill item is to the left of the handle at index 1, so the handle belongs
|
||||||
|
|
@ -1136,7 +1135,7 @@ TestCase {
|
||||||
{ x: 0, y: 0, width: splitViewWidth, height: defaultVerticalHandleHeight },
|
{ x: 0, y: 0, width: splitViewWidth, height: defaultVerticalHandleHeight },
|
||||||
{ x: 0, y: defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
|
{ x: 0, y: defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
|
||||||
{ x: 0, y: 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
|
{ x: 0, y: 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
|
||||||
{ x: 0, y: 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
|
{ x: 0, y: 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 - defaultVerticalHandleHeight * 2 }
|
||||||
],
|
],
|
||||||
expectedGeometriesAfterDrag: [
|
expectedGeometriesAfterDrag: [
|
||||||
// The fill item is to the top of the handle at index 1, so the handle belongs
|
// The fill item is to the top of the handle at index 1, so the handle belongs
|
||||||
|
|
@ -1168,8 +1167,8 @@ TestCase {
|
||||||
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
||||||
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
// The second item's implicitWidth is 100, and ours is 200. The available width is 300,
|
// The second item's implicitWidth is 100, and ours is 200. The available width is 300,
|
||||||
// so both items get their implicit widths.
|
// so this item gets size 300 - 100 - 2 * 10 = 180.
|
||||||
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
|
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
|
||||||
],
|
],
|
||||||
// Should be unchanged.
|
// Should be unchanged.
|
||||||
expectedGeometriesAfterDrag: [
|
expectedGeometriesAfterDrag: [
|
||||||
|
|
@ -1177,7 +1176,7 @@ TestCase {
|
||||||
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
{ x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
||||||
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
|
{ x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1192,7 +1191,9 @@ TestCase {
|
||||||
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
||||||
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
|
// The first item is the filling one, with minimum size 25. Second item is 100.
|
||||||
|
// The available size is 300, so third item is 300 - 100 - 25 - 2 * 10 = 155.
|
||||||
|
{ x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 25 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
|
||||||
],
|
],
|
||||||
// Should be unchanged.
|
// Should be unchanged.
|
||||||
expectedGeometriesAfterDrag: [
|
expectedGeometriesAfterDrag: [
|
||||||
|
|
@ -1200,7 +1201,7 @@ TestCase {
|
||||||
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
{ x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
|
||||||
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
{ x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
|
||||||
{ x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
|
{ x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100 - 25 - 2 * defaultHorizontalHandleWidth, height: splitViewHeight }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -2530,6 +2531,9 @@ TestCase {
|
||||||
verify(control.resizing)
|
verify(control.resizing)
|
||||||
compare(firstItem.width, 0)
|
compare(firstItem.width, 0)
|
||||||
compare(secondItem.SplitView.preferredWidth, 50)
|
compare(secondItem.SplitView.preferredWidth, 50)
|
||||||
|
// Wait for polish so item width becomes preferredWidth
|
||||||
|
verify(isPolishScheduled(control))
|
||||||
|
verify(waitForItemPolished(control))
|
||||||
compare(secondItem.width, 50)
|
compare(secondItem.width, 50)
|
||||||
mouseRelease(targetHandle, -100, targetHandle.height / 2, Qt.LeftButton)
|
mouseRelease(targetHandle, -100, targetHandle.height / 2, Qt.LeftButton)
|
||||||
verify(!control.resizing)
|
verify(!control.resizing)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue