Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
layout_propertysheet.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6// sdk
7#include <QtDesigner/qextensionmanager.h>
8#include <QtDesigner/abstractformeditor.h>
9// shared
10
11#include <qlayout_widget_p.h>
12
13#include <QtDesigner/private/ui4_p.h>
14#include <QtDesigner/private/formbuilderextra_p.h>
15
16#include <QtWidgets/qformlayout.h>
17
18#include <QtCore/qhash.h>
19#include <QtCore/qdebug.h>
20#include <QtCore/qtextstream.h>
21#include <QtCore/qbytearray.h>
22#include <QtCore/QRegularExpression> // Remove once there is an editor for lists
23
25
26using namespace Qt::StringLiterals;
27
28static constexpr auto leftMargin = "leftMargin"_L1;
29static constexpr auto topMargin = "topMargin"_L1;
30static constexpr auto rightMargin = "rightMargin"_L1;
31static constexpr auto bottomMargin = "bottomMargin"_L1;
32static constexpr auto horizontalSpacing = "horizontalSpacing"_L1;
33static constexpr auto verticalSpacing = "verticalSpacing"_L1;
34static constexpr auto spacing = "spacing"_L1;
35#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
36static constexpr auto sizeConstraint = "sizeConstraint"_L1;
37#else
38static constexpr auto horizontalSizeConstraint = "horizontalSizeConstraint"_L1;
39static constexpr auto verticalSizeConstraint = "verticalSizeConstraint"_L1;
40#endif
41static constexpr auto boxStretchPropertyC = "stretch"_L1;
42static constexpr auto gridRowStretchPropertyC = "rowStretch"_L1;
43static constexpr auto gridColumnStretchPropertyC = "columnStretch"_L1;
44static constexpr auto gridRowMinimumHeightPropertyC = "rowMinimumHeight"_L1;
45static constexpr auto gridColumnMinimumWidthPropertyC = "columnMinimumWidth"_L1;
46
47namespace {
48 enum LayoutPropertyType {
49 LayoutPropertyNone,
50 LayoutPropertyLeftMargin,
51 LayoutPropertyTopMargin,
52 LayoutPropertyRightMargin,
53 LayoutPropertyBottomMargin,
54 LayoutPropertySpacing,
55 LayoutPropertyHorizontalSpacing,
56 LayoutPropertyVerticalSpacing,
57#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
58 LayoutPropertySizeConstraint,
59#else
60 LayoutPropertyHorizontalSizeConstraint,
61 LayoutPropertyVerticalSizeConstraint,
62#endif
63 LayoutPropertyBoxStretch,
64 LayoutPropertyGridRowStretch,
65 LayoutPropertyGridColumnStretch,
66 LayoutPropertyGridRowMinimumHeight,
67 LayoutPropertyGridColumnMinimumWidth
68 };
69}
70
71// Check for a comma-separated list of integers. Used for
72// per-cell stretch properties and grid per row/column properties.
73// As it works now, they are passed as QByteArray strings. The
74// property sheet refuses all invalid values. This could be
75// replaced by lists once the property editor can handle them.
76
77static bool isIntegerList(const QString &s)
78{
79 // Check for empty string or comma-separated list of integers
80 static const QRegularExpression re(u"^[0-9]+(,[0-9]+)+$"_s);
81 Q_ASSERT(re.isValid());
82 return s.isEmpty() || re.match(s).hasMatch();
83}
84
85// Quick lookup by name
86static LayoutPropertyType layoutPropertyType(const QString &name)
87{
88 static const QHash<QString, LayoutPropertyType> namePropertyMap = {
89 {leftMargin, LayoutPropertyLeftMargin},
90 {topMargin, LayoutPropertyTopMargin},
91 {rightMargin, LayoutPropertyRightMargin},
92 {bottomMargin, LayoutPropertyBottomMargin},
93 {horizontalSpacing, LayoutPropertyHorizontalSpacing},
94 {verticalSpacing, LayoutPropertyVerticalSpacing},
95 {spacing, LayoutPropertySpacing},
96#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
97 {sizeConstraint, LayoutPropertySizeConstraint},
98#else
99 {horizontalSizeConstraint, LayoutPropertyHorizontalSizeConstraint},
100 {verticalSizeConstraint, LayoutPropertyVerticalSizeConstraint},
101#endif
102 {boxStretchPropertyC, LayoutPropertyBoxStretch},
103 {gridRowStretchPropertyC, LayoutPropertyGridRowStretch},
104 {gridColumnStretchPropertyC, LayoutPropertyGridColumnStretch},
105 {gridRowMinimumHeightPropertyC, LayoutPropertyGridRowMinimumHeight},
106 {gridColumnMinimumWidthPropertyC, LayoutPropertyGridColumnMinimumWidth}
107 };
108 return namePropertyMap.value(name, LayoutPropertyNone);
109}
110
111// return the layout margin if it is margin
112static int getLayoutMargin(const QLayout *l, LayoutPropertyType type)
113{
114 int left, top, right, bottom;
115 l->getContentsMargins(&left, &top, &right, &bottom);
116 switch (type) {
117 case LayoutPropertyLeftMargin:
118 return left;
119 case LayoutPropertyTopMargin:
120 return top;
121 case LayoutPropertyRightMargin:
122 return right;
123 case LayoutPropertyBottomMargin:
124 return bottom;
125 default:
126 Q_ASSERT(0);
127 break;
128 }
129 return 0;
130}
131
132// return the layout margin if it is margin
133static void setLayoutMargin(QLayout *l, LayoutPropertyType type, int margin)
134{
135 int left, top, right, bottom;
136 l->getContentsMargins(&left, &top, &right, &bottom);
137 switch (type) {
138 case LayoutPropertyLeftMargin:
139 left = margin;
140 break;
141 case LayoutPropertyTopMargin:
142 top = margin;
143 break;
144 case LayoutPropertyRightMargin:
145 right = margin;
146 break;
147 case LayoutPropertyBottomMargin:
148 bottom = margin;
149 break;
150 default:
151 Q_ASSERT(0);
152 break;
153 }
154 l->setContentsMargins(left, top, right, bottom);
155}
156
157namespace qdesigner_internal {
158
159// ---------- LayoutPropertySheet: This sheet is never visible in
160// the property editor. Rather, the sheet pulled for QLayoutWidget
161// forwards all properties to it. Some properties (grid spacings) must be handled
162// manually, as they are QDOC_PROPERTY only and not visible to introspection. Ditto
163// for the 4 margins.
164
165LayoutPropertySheet::LayoutPropertySheet(QLayout *l, QObject *parent)
166 : QDesignerPropertySheet(l, parent), m_layout(l)
167{
168 const QString layoutGroup = u"Layout"_s;
169 int pindex = createFakeProperty(leftMargin, 0);
170 setPropertyGroup(pindex, layoutGroup);
171
172 pindex = createFakeProperty(topMargin, 0);
173 setPropertyGroup(pindex, layoutGroup);
174
175 pindex = createFakeProperty(rightMargin, 0);
176 setPropertyGroup(pindex, layoutGroup);
177
178 pindex = createFakeProperty(bottomMargin, 0);
179 setPropertyGroup(pindex, layoutGroup);
180
181 const int visibleMask = LayoutProperties::visibleProperties(m_layout);
182 if (visibleMask & LayoutProperties::HorizSpacingProperty) {
183 pindex = createFakeProperty(horizontalSpacing, 0);
184 setPropertyGroup(pindex, layoutGroup);
185
186 pindex = createFakeProperty(verticalSpacing, 0);
187 setPropertyGroup(pindex, layoutGroup);
188
189 setAttribute(indexOf(spacing), true);
190 }
191
192 // Stretch
193 if (visibleMask & LayoutProperties::BoxStretchProperty) {
194 pindex = createFakeProperty(boxStretchPropertyC, QByteArray());
195 setPropertyGroup(pindex, layoutGroup);
196 setAttribute(pindex, true);
197 } else {
198 // Add the grid per-row/column stretch and size limits
199 if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
200 const QByteArray empty;
201 pindex = createFakeProperty(gridRowStretchPropertyC, empty);
202 setPropertyGroup(pindex, layoutGroup);
203 setAttribute(pindex, true);
204 pindex = createFakeProperty(gridColumnStretchPropertyC, empty);
205 setPropertyGroup(pindex, layoutGroup);
206 setAttribute(pindex, true);
207 pindex = createFakeProperty(gridRowMinimumHeightPropertyC, empty);
208 setPropertyGroup(pindex, layoutGroup);
209 setAttribute(pindex, true);
210 pindex = createFakeProperty(gridColumnMinimumWidthPropertyC, empty);
211 setPropertyGroup(pindex, layoutGroup);
212 setAttribute(pindex, true);
213 }
214 }
215 // SizeConstraint cannot possibly be handled as a real property
216 // as it affects the layout parent widget and thus
217 // conflicts with Designer's special layout widget.
218 // It will take effect on the preview only.
219#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
220 pindex = createFakeProperty(sizeConstraint);
221 setPropertyGroup(pindex, layoutGroup);
222#else
223 pindex = createFakeProperty(horizontalSizeConstraint);
224 setPropertyGroup(pindex, layoutGroup);
225 pindex = createFakeProperty(verticalSizeConstraint);
226 setPropertyGroup(pindex, layoutGroup);
227#endif
228}
229
231
232void LayoutPropertySheet::setProperty(int index, const QVariant &value)
233{
234 const LayoutPropertyType type = layoutPropertyType(propertyName(index));
235 if (QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
236 switch (type) {
237 case LayoutPropertyLeftMargin:
238 lw->setLayoutLeftMargin(value.toInt());
239 return;
240 case LayoutPropertyTopMargin:
241 lw->setLayoutTopMargin(value.toInt());
242 return;
243 case LayoutPropertyRightMargin:
244 lw->setLayoutRightMargin(value.toInt());
245 return;
246 case LayoutPropertyBottomMargin:
247 lw->setLayoutBottomMargin(value.toInt());
248 return;
249 default:
250 break;
251 }
252 }
253 switch (type) {
254 case LayoutPropertyLeftMargin:
255 case LayoutPropertyTopMargin:
256 case LayoutPropertyRightMargin:
257 case LayoutPropertyBottomMargin:
258 setLayoutMargin(m_layout, type, value.toInt());
259 return;
260 case LayoutPropertyHorizontalSpacing:
261 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
262 grid->setHorizontalSpacing(value.toInt());
263 return;
264 }
265 if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
266 form->setHorizontalSpacing(value.toInt());
267 return;
268 }
269 break;
270 case LayoutPropertyVerticalSpacing:
271 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
272 grid->setVerticalSpacing(value.toInt());
273 return;
274 }
275 if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
276 form->setVerticalSpacing(value.toInt());
277 return;
278 }
279 break;
280 case LayoutPropertyBoxStretch:
281 // TODO: Remove the regexp check once a proper editor for integer
282 // lists is in place?
283 if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout)) {
284 const QString stretch = value.toString();
285 if (isIntegerList(stretch))
286 QFormBuilderExtra::setBoxLayoutStretch(value.toString(), box);
287 }
288 break;
289 case LayoutPropertyGridRowStretch:
290 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
291 const QString stretch = value.toString();
292 if (isIntegerList(stretch))
293 QFormBuilderExtra::setGridLayoutRowStretch(stretch, grid);
294 }
295 break;
296 case LayoutPropertyGridColumnStretch:
297 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
298 const QString stretch = value.toString();
299 if (isIntegerList(stretch))
300 QFormBuilderExtra::setGridLayoutColumnStretch(value.toString(), grid);
301 }
302 break;
303 case LayoutPropertyGridRowMinimumHeight:
304 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
305 const QString minSize = value.toString();
306 if (isIntegerList(minSize))
307 QFormBuilderExtra::setGridLayoutRowMinimumHeight(minSize, grid);
308 }
309 break;
310 case LayoutPropertyGridColumnMinimumWidth:
311 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
312 const QString minSize = value.toString();
313 if (isIntegerList(minSize))
314 QFormBuilderExtra::setGridLayoutColumnMinimumWidth(minSize, grid);
315 }
316 break;
317 default:
318 break;
319 }
320 QDesignerPropertySheet::setProperty(index, value);
321}
322
324{
325 const LayoutPropertyType type = layoutPropertyType(propertyName(index));
326 if (const QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
327 switch (type) {
328 case LayoutPropertyLeftMargin:
329 return lw->layoutLeftMargin();
330 case LayoutPropertyTopMargin:
331 return lw->layoutTopMargin();
332 case LayoutPropertyRightMargin:
333 return lw->layoutRightMargin();
334 case LayoutPropertyBottomMargin:
335 return lw->layoutBottomMargin();
336 default:
337 break;
338 }
339 }
340 switch (type) {
341 case LayoutPropertyLeftMargin:
342 case LayoutPropertyTopMargin:
343 case LayoutPropertyRightMargin:
344 case LayoutPropertyBottomMargin:
345 return getLayoutMargin(m_layout, type);
346 case LayoutPropertyHorizontalSpacing:
347 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
348 return grid->horizontalSpacing();
349 if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
350 return form->horizontalSpacing();
351 break;
352 case LayoutPropertyVerticalSpacing:
353 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
354 return grid->verticalSpacing();
355 if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
356 return form->verticalSpacing();
357 break;
358 case LayoutPropertyBoxStretch:
359 if (const QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
360 return QVariant(QByteArray(QFormBuilderExtra::boxLayoutStretch(box).toUtf8()));
361 break;
362 case LayoutPropertyGridRowStretch:
363 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
364 return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowStretch(grid).toUtf8()));
365 break;
366 case LayoutPropertyGridColumnStretch:
367 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
368 return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnStretch(grid).toUtf8()));
369 break;
370 case LayoutPropertyGridRowMinimumHeight:
371 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
372 return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowMinimumHeight(grid).toUtf8()));
373 break;
374 case LayoutPropertyGridColumnMinimumWidth:
375 if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
376 return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnMinimumWidth(grid).toUtf8()));
377 break;
378 default:
379 break;
380 }
381 return QDesignerPropertySheet::property(index);
382}
383
385{
386 int left, top, right, bottom;
387 m_layout->getContentsMargins(&left, &top, &right, &bottom);
388 const LayoutPropertyType type = layoutPropertyType(propertyName(index));
389 bool rc = true;
390 switch (type) {
391 case LayoutPropertyLeftMargin:
392 m_layout->setContentsMargins(-1, top, right, bottom);
393 break;
394 case LayoutPropertyTopMargin:
395 m_layout->setContentsMargins(left, -1, right, bottom);
396 break;
397 case LayoutPropertyRightMargin:
398 m_layout->setContentsMargins(left, top, -1, bottom);
399 break;
400 case LayoutPropertyBottomMargin:
401 m_layout->setContentsMargins(left, top, right, -1);
402 break;
403 case LayoutPropertyBoxStretch:
404 if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
405 QFormBuilderExtra::clearBoxLayoutStretch(box);
406 break;
407 case LayoutPropertyGridRowStretch:
408 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
409 QFormBuilderExtra::clearGridLayoutRowStretch(grid);
410 break;
411 case LayoutPropertyGridColumnStretch:
412 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
413 QFormBuilderExtra::clearGridLayoutColumnStretch(grid);
414 break;
415 case LayoutPropertyGridRowMinimumHeight:
416 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
417 QFormBuilderExtra::clearGridLayoutRowMinimumHeight(grid);
418 break;
419 case LayoutPropertyGridColumnMinimumWidth:
420 if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
421 QFormBuilderExtra::clearGridLayoutColumnMinimumWidth(grid);
422 break;
423 default:
424 rc = QDesignerPropertySheet::reset(index);
425 break;
426 }
427 return rc;
428}
429
430void LayoutPropertySheet::setChanged(int index, bool changed)
431{
432 const LayoutPropertyType type = layoutPropertyType(propertyName(index));
433 switch (type) {
434 case LayoutPropertySpacing:
435 if (LayoutProperties::visibleProperties(m_layout) & LayoutProperties::HorizSpacingProperty) {
436 setChanged(indexOf(horizontalSpacing), changed);
437 setChanged(indexOf(verticalSpacing), changed);
438 }
439 break;
440 default:
441 break;
442 }
443 QDesignerPropertySheet::setChanged(index, changed);
444}
445
446void LayoutPropertySheet::stretchAttributesToDom(QDesignerFormEditorInterface *core, QLayout *lt, DomLayout *domLayout)
447{
448 // Check if the respective stretch properties of the layout are changed.
449 // If so, set them to the DOM
450 const int visibleMask = LayoutProperties::visibleProperties(lt);
451 if (!(visibleMask & (LayoutProperties::BoxStretchProperty|LayoutProperties::GridColumnStretchProperty|LayoutProperties::GridRowStretchProperty)))
452 return;
453 const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
454 Q_ASSERT(sheet);
455
456 // Stretch
457 if (visibleMask & LayoutProperties::BoxStretchProperty) {
458 const int index = sheet->indexOf(boxStretchPropertyC);
459 Q_ASSERT(index != -1);
460 if (sheet->isChanged(index))
461 domLayout->setAttributeStretch(sheet->property(index).toString());
462 }
463 if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
464 const int index = sheet->indexOf(gridColumnStretchPropertyC);
465 Q_ASSERT(index != -1);
466 if (sheet->isChanged(index))
467 domLayout->setAttributeColumnStretch(sheet->property(index).toString());
468 }
469 if (visibleMask & LayoutProperties::GridRowStretchProperty) {
470 const int index = sheet->indexOf(gridRowStretchPropertyC);
471 Q_ASSERT(index != -1);
472 if (sheet->isChanged(index))
473 domLayout->setAttributeRowStretch(sheet->property(index).toString());
474 }
475 if (visibleMask & LayoutProperties::GridRowMinimumHeightProperty) {
476 const int index = sheet->indexOf(gridRowMinimumHeightPropertyC);
477 Q_ASSERT(index != -1);
478 if (sheet->isChanged(index))
479 domLayout->setAttributeRowMinimumHeight(sheet->property(index).toString());
480 }
481 if (visibleMask & LayoutProperties::GridColumnMinimumWidthProperty) {
482 const int index = sheet->indexOf(gridColumnMinimumWidthPropertyC);
483 Q_ASSERT(index != -1);
484 if (sheet->isChanged(index))
485 domLayout->setAttributeColumnMinimumWidth(sheet->property(index).toString());
486 }
487}
488
489void LayoutPropertySheet::markChangedStretchProperties(QDesignerFormEditorInterface *core, QLayout *lt, const DomLayout *domLayout)
490{
491 // While the actual values are applied by the form builder, we still need
492 // to mark them as 'changed'.
493 QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
494 Q_ASSERT(sheet);
495 if (!domLayout->attributeStretch().isEmpty())
496 sheet->setChanged(sheet->indexOf(boxStretchPropertyC), true);
497 if (!domLayout->attributeRowStretch().isEmpty())
498 sheet->setChanged(sheet->indexOf(gridRowStretchPropertyC), true);
499 if (!domLayout->attributeColumnStretch().isEmpty())
500 sheet->setChanged(sheet->indexOf(gridColumnStretchPropertyC), true);
501 if (!domLayout->attributeColumnMinimumWidth().isEmpty())
502 sheet->setChanged(sheet->indexOf(gridColumnMinimumWidthPropertyC), true);
503 if (!domLayout->attributeRowMinimumHeight().isEmpty())
504 sheet->setChanged(sheet->indexOf(gridRowMinimumHeightPropertyC), true);
505}
506
507}
508
509QT_END_NAMESPACE
void setProperty(int index, const QVariant &value) override
void setChanged(int index, bool changed) override
QVariant property(int index) const override
static constexpr auto gridRowStretchPropertyC
static constexpr auto gridColumnMinimumWidthPropertyC
static constexpr auto horizontalSpacing
static int getLayoutMargin(const QLayout *l, LayoutPropertyType type)
static constexpr auto horizontalSizeConstraint
static constexpr auto verticalSizeConstraint
static constexpr auto rightMargin
static constexpr auto bottomMargin
static constexpr auto boxStretchPropertyC
static constexpr auto leftMargin
static LayoutPropertyType layoutPropertyType(const QString &name)
static constexpr auto gridColumnStretchPropertyC
static bool isIntegerList(const QString &s)
static constexpr auto gridRowMinimumHeightPropertyC
static void setLayoutMargin(QLayout *l, LayoutPropertyType type, int margin)
static constexpr auto topMargin
static constexpr auto verticalSpacing
static constexpr auto spacing
Auxiliary methods to store/retrieve settings.