1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qdesigner_widgetitem_p.h"
30#include "qdesigner_widget_p.h"
31#include "widgetfactory_p.h"
32
33#include <QtDesigner/abstractformwindow.h>
34#include <QtDesigner/qextensionmanager.h>
35#include <QtDesigner/abstractformeditor.h>
36#include <QtDesigner/container.h>
37#include <QtDesigner/abstractwidgetdatabase.h>
38
39#include <QtWidgets/qboxlayout.h>
40#include <QtWidgets/qgridlayout.h>
41#include <QtWidgets/qformlayout.h>
42#include <QtWidgets/qapplication.h>
43
44#include <QtCore/qtextstream.h>
45#include <QtCore/qdebug.h>
46#include <private/qlayout_p.h>
47
48QT_BEGIN_NAMESPACE
49
50enum { DebugWidgetItem = 0 };
51enum { MinimumLength = 10 };
52
53// Widget item creation function to be registered as factory method with
54// QLayoutPrivate
55static QWidgetItem *createDesignerWidgetItem(const QLayout *layout, QWidget *widget)
56{
57 Qt::Orientations orientations;
58 if (qdesigner_internal::QDesignerWidgetItem::check(layout, w: widget, ptrToOrientations: &orientations)) {
59 if (DebugWidgetItem)
60 qDebug() << "QDesignerWidgetItem: Creating on " << layout << widget << orientations;
61 return new qdesigner_internal::QDesignerWidgetItem(layout, widget, orientations);
62 }
63 if (DebugWidgetItem)
64 qDebug() << "QDesignerWidgetItem: Noncontainer: " << layout << widget;
65
66 return nullptr;
67}
68
69static QString sizePolicyToString(const QSizePolicy &p)
70{
71 QString rc; {
72 QTextStream str(&rc);
73 str << "Control=" << p.controlType() << " expdirs=" << p.expandingDirections()
74 << " hasHeightForWidth=" << p.hasHeightForWidth()
75 << " H: Policy=" << p.horizontalPolicy()
76 << " stretch=" << p.horizontalStretch()
77 << " V: Policy=" << p.verticalPolicy()
78 << " stretch=" << p.verticalStretch();
79 }
80 return rc;
81}
82
83// Find the layout the item is contained in, recursing over
84// child layouts
85static const QLayout *findLayoutOfItem(const QLayout *haystack, const QLayoutItem *needle)
86{
87 const int count = haystack->count();
88 for (int i = 0; i < count; i++) {
89 QLayoutItem *item = haystack->itemAt(index: i);
90 if (item == needle)
91 return haystack;
92 if (QLayout *childLayout = item->layout())
93 if (const QLayout *containing = findLayoutOfItem(haystack: childLayout, needle))
94 return containing;
95 }
96 return nullptr;
97}
98
99
100namespace qdesigner_internal {
101
102// ------------------ QDesignerWidgetItem
103QDesignerWidgetItem::QDesignerWidgetItem(const QLayout *containingLayout, QWidget *w, Qt::Orientations o) :
104 QWidgetItemV2(w),
105 m_orientations(o),
106 m_nonLaidOutMinSize(w->minimumSizeHint()),
107 m_nonLaidOutSizeHint(w->sizeHint()),
108 m_cachedContainingLayout(containingLayout)
109{
110 // Initialize the minimum size to prevent nonlaid-out frames/widgets
111 // from being slammed to zero
112 const QSize minimumSize = w->minimumSize();
113 if (!minimumSize.isEmpty())
114 m_nonLaidOutMinSize = minimumSize;
115 expand(s: &m_nonLaidOutMinSize);
116 expand(s: &m_nonLaidOutSizeHint);
117 w->installEventFilter(filterObj: this);
118 connect(sender: containingLayout, signal: &QObject::destroyed, receiver: this, slot: &QDesignerWidgetItem::layoutChanged);
119 if (DebugWidgetItem )
120 qDebug() << "QDesignerWidgetItem" << w << sizePolicyToString(p: w->sizePolicy()) << m_nonLaidOutMinSize << m_nonLaidOutSizeHint;
121}
122
123void QDesignerWidgetItem::expand(QSize *s) const
124{
125 // Expand the size if its too small
126 if (m_orientations & Qt::Horizontal && s->width() <= 0)
127 s->setWidth(MinimumLength);
128 if (m_orientations & Qt::Vertical && s->height() <= 0)
129 s->setHeight(MinimumLength);
130}
131
132QSize QDesignerWidgetItem::minimumSize() const
133{
134 // Just track the size in case we are laid-out or stretched.
135 const QSize baseMinSize = QWidgetItemV2::minimumSize();
136 QWidget * w = constWidget();
137 if (w->layout() || subjectToStretch(layout: containingLayout(), w)) {
138 m_nonLaidOutMinSize = baseMinSize;
139 return baseMinSize;
140 }
141 // Nonlaid out: Maintain last laid-out size
142 const QSize rc = baseMinSize.expandedTo(otherSize: m_nonLaidOutMinSize);
143 if (DebugWidgetItem > 1)
144 qDebug() << "minimumSize" << constWidget() << baseMinSize << rc;
145 return rc;
146}
147
148QSize QDesignerWidgetItem::sizeHint() const
149{
150 // Just track the size in case we are laid-out or stretched.
151 const QSize baseSizeHint = QWidgetItemV2::sizeHint();
152 QWidget * w = constWidget();
153 if (w->layout() || subjectToStretch(layout: containingLayout(), w)) {
154 m_nonLaidOutSizeHint = baseSizeHint;
155 return baseSizeHint;
156 }
157 // Nonlaid out: Maintain last laid-out size
158 const QSize rc = baseSizeHint.expandedTo(otherSize: m_nonLaidOutSizeHint);
159 if (DebugWidgetItem > 1)
160 qDebug() << "sizeHint" << constWidget() << baseSizeHint << rc;
161 return rc;
162}
163
164bool QDesignerWidgetItem::subjectToStretch(const QLayout *layout, QWidget *w)
165{
166 if (!layout)
167 return false;
168 // Are we under some stretch factor?
169 if (const QBoxLayout *bl = qobject_cast<const QBoxLayout *>(object: layout)) {
170 const int index = bl->indexOf(w);
171 Q_ASSERT(index != -1);
172 return bl->stretch(index) != 0;
173 }
174 if (const QGridLayout *cgl = qobject_cast<const QGridLayout *>(object: layout)) {
175 QGridLayout *gl = const_cast<QGridLayout *>(cgl);
176 const int index = cgl->indexOf(w);
177 Q_ASSERT(index != -1);
178 int row, column, rowSpan, columnSpan;
179 gl->getItemPosition (idx: index, row: &row, column: &column, rowSpan: &rowSpan, columnSpan: &columnSpan);
180 const int rend = row + rowSpan;
181 const int cend = column + columnSpan;
182 for (int r = row; r < rend; r++)
183 if (cgl->rowStretch(row: r) != 0)
184 return true;
185 for (int c = column; c < cend; c++)
186 if (cgl->columnStretch(column: c) != 0)
187 return true;
188 }
189 return false;
190}
191
192/* Return the orientations mask for a layout, specifying
193 * in which directions squeezing should be prevented. */
194static Qt::Orientations layoutOrientation(const QLayout *layout)
195{
196 if (const QBoxLayout *bl = qobject_cast<const QBoxLayout *>(object: layout)) {
197 const QBoxLayout::Direction direction = bl->direction();
198 return direction == QBoxLayout::LeftToRight || direction == QBoxLayout::RightToLeft ? Qt::Horizontal : Qt::Vertical;
199 }
200 if (qobject_cast<const QFormLayout*>(object: layout))
201 return Qt::Vertical;
202 return Qt::Horizontal|Qt::Vertical;
203}
204
205// Check for a non-container extension container
206bool QDesignerWidgetItem::isContainer(const QDesignerFormEditorInterface *core, QWidget *w)
207{
208 if (!WidgetFactory::isFormEditorObject(o: w))
209 return false;
210 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
211 const int widx = wdb->indexOfObject(object: w);
212 if (widx == -1 || !wdb->item(index: widx)->isContainer())
213 return false;
214 if (qt_extension<QDesignerContainerExtension*>(manager: core->extensionManager(), object: w))
215 return false;
216 return true;
217}
218
219bool QDesignerWidgetItem::check(const QLayout *layout, QWidget *w, Qt::Orientations *ptrToOrientations)
220{
221 // Check for form-editor non-containerextension-containers (QFrame, etc)
222 // within laid-out form editor widgets. No check for managed() here as we
223 // want container pages and widgets in the process of being morphed as
224 // well. Avoid nested layouts (as the effective stretch cannot be easily
225 // computed and may mess things up).
226 if (ptrToOrientations)
227 *ptrToOrientations = {};
228
229 const QObject *layoutParent = layout->parent();
230 if (!layoutParent || !layoutParent->isWidgetType() || !WidgetFactory::isFormEditorObject(o: layoutParent))
231 return false;
232
233 QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w);
234 if (!fw || !isContainer(core: fw->core(), w))
235 return false;
236
237 // If it is a box, restrict to its orientation
238 if (ptrToOrientations)
239 *ptrToOrientations = layoutOrientation(layout);
240
241 return true;
242}
243
244QSize QDesignerWidgetItem::nonLaidOutMinSize() const
245{
246 return m_nonLaidOutMinSize;
247}
248
249void QDesignerWidgetItem::setNonLaidOutMinSize(const QSize &s)
250{
251 if (DebugWidgetItem > 1)
252 qDebug() << "setNonLaidOutMinSize" << constWidget() << s;
253 m_nonLaidOutMinSize = s;
254}
255
256QSize QDesignerWidgetItem::nonLaidOutSizeHint() const
257{
258 return m_nonLaidOutSizeHint;
259}
260
261void QDesignerWidgetItem::setNonLaidOutSizeHint(const QSize &s)
262{
263 if (DebugWidgetItem > 1)
264 qDebug() << "setNonLaidOutSizeHint" << constWidget() << s;
265 m_nonLaidOutSizeHint = s;
266}
267
268void QDesignerWidgetItem::install()
269{
270 QLayoutPrivate::widgetItemFactoryMethod = createDesignerWidgetItem;
271}
272
273void QDesignerWidgetItem::deinstall()
274{
275 QLayoutPrivate::widgetItemFactoryMethod = nullptr;
276}
277
278const QLayout *QDesignerWidgetItem::containingLayout() const
279{
280 if (!m_cachedContainingLayout) {
281 if (QWidget *parentWidget = constWidget()->parentWidget())
282 if (QLayout *parentLayout = parentWidget->layout()) {
283 m_cachedContainingLayout = findLayoutOfItem(haystack: parentLayout, needle: this);
284 if (m_cachedContainingLayout) {
285 connect(sender: m_cachedContainingLayout, signal: &QObject::destroyed,
286 receiver: this, slot: &QDesignerWidgetItem::layoutChanged);
287 }
288 }
289 if (DebugWidgetItem)
290 qDebug() << Q_FUNC_INFO << " found " << m_cachedContainingLayout << " after reparenting " << constWidget();
291 }
292 return m_cachedContainingLayout;
293}
294
295void QDesignerWidgetItem::layoutChanged()
296{
297 if (DebugWidgetItem)
298 qDebug() << Q_FUNC_INFO;
299 m_cachedContainingLayout = nullptr;
300}
301
302bool QDesignerWidgetItem::eventFilter(QObject * /* watched */, QEvent *event)
303{
304 if (event->type() == QEvent::ParentChange)
305 layoutChanged();
306 return false;
307}
308
309// ------------------ QDesignerWidgetItemInstaller
310
311int QDesignerWidgetItemInstaller::m_instanceCount = 0;
312
313QDesignerWidgetItemInstaller::QDesignerWidgetItemInstaller()
314{
315 if (m_instanceCount++ == 0) {
316 if (DebugWidgetItem)
317 qDebug() << "QDesignerWidgetItemInstaller: installing";
318 QDesignerWidgetItem::install();
319 }
320}
321
322QDesignerWidgetItemInstaller::~QDesignerWidgetItemInstaller()
323{
324 if (--m_instanceCount == 0) {
325 if (DebugWidgetItem)
326 qDebug() << "QDesignerWidgetItemInstaller: deinstalling";
327 QDesignerWidgetItem::deinstall();
328 }
329}
330
331}
332
333QT_END_NAMESPACE
334

source code of qttools/src/designer/src/lib/shared/qdesigner_widgetitem.cpp