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