Ensure that layouts don't move widgets outside of their parent

When using a style that wants to draw into the layout's margin (like
macOS style does with group box titles), parts of the widgets would be
clipped by the parent if the available margin is smaller than necessary.

This moves the x/y coordinates to at least 0/0, and adjusts width and
height accordingly.

[ChangeLog][QtWidgets][QLayout] Prevent clipping of group box titles
on macOS (and similar styles that draw into layout margins)

Change-Id: I32148a92858c13fb2325da4d0a2a58996e0e8930
Fixes: QTBUG-67608
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
bb10
Volker Hilsheimer 2019-03-19 17:31:56 +01:00
parent 4054759aec
commit d0f016ebfb
2 changed files with 75 additions and 0 deletions

View File

@ -502,6 +502,17 @@ void QWidgetItem::setGeometry(const QRect &rect)
else if (!(align & Qt::AlignTop))
y = y + (r.height() - s.height()) / 2;
// Make sure we don't move outside of the parent, e.g when styles demand
// surplus space that exceeds the available margins (f.ex macOS with QGroupBox)
if (x < 0) {
s.rwidth() += x;
x = 0;
}
if (y < 0) {
s.rheight() += y;
y = 0;
}
wid->setGeometry(x, y, s.width(), s.height());
}

View File

@ -47,6 +47,7 @@ private slots:
void sizeConstraints();
void setGeometry();
void setStyleShouldChangeSpacing();
void widgetSurplus();
void testLayoutEngine_data();
void testLayoutEngine();
@ -236,6 +237,69 @@ void tst_QBoxLayout::setStyleShouldChangeSpacing()
QTRY_COMPARE(spacing(), 10);
}
class MarginEatingStyle : public QProxyStyle
{
public:
MarginEatingStyle() : QProxyStyle(QStyleFactory::create("windows"))
{
}
virtual QRect subElementRect(SubElement sr, const QStyleOption *opt,
const QWidget *widget) const
{
QRect rect = opt->rect;
switch (sr) {
case SE_GroupBoxLayoutItem:
// this is a simplifed version of what the macOS style does
rect.setTop(rect.top() + 20);
rect.setLeft(rect.left() + 20);
rect.setRight(rect.right() - 20);
rect.setBottom(rect.bottom() - 20);
break;
default:
return QProxyStyle::subElementRect(sr, opt, widget);
}
return rect;
}
};
void tst_QBoxLayout::widgetSurplus()
{
// Test case for QTBUG-67608 - a style requests space in the margin
QDialog window;
QScopedPointer<MarginEatingStyle> marginEater(new MarginEatingStyle);
QVBoxLayout *vbox = new QVBoxLayout(&window);
vbox->setMargin(0);
vbox->setSpacing(0);
QLabel *hiddenLabel = new QLabel(tr("Invisible label"));
hiddenLabel->setVisible(false);
QGroupBox *groupBox = new QGroupBox(tr("Groupbox Title"));
groupBox->setStyle(marginEater.data());
groupBox->setObjectName("Test group box");
QPushButton *button1 = new QPushButton(tr("Button 1"));
QPushButton *button2 = new QPushButton(tr("Button 2"));
QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->addWidget(button1);
groupLayout->addWidget(button2);
groupBox->setLayout(groupLayout);
QLabel *label = new QLabel(tr("Visible label"));
vbox->addWidget(hiddenLabel);
vbox->addWidget(groupBox);
vbox->addWidget(label);
window.setLayout(vbox);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QCOMPARE(groupBox->y(), 0);
QCOMPARE(groupBox->x(), 0);
}
void tst_QBoxLayout::taskQTBUG_7103_minMaxWidthNotRespected()
{
QLabel *label = new QLabel("Qt uses standard C++, but makes extensive use of the C pre-processor to enrich the language. Qt can also be used in several other programming languages via language bindings. It runs on all major platforms, and has extensive internationalization support. Non-GUI features include SQL database access, XML parsing, thread management, network support and a unified cross-platform API for file handling.");