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