2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2020 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2020-09-18 12:11:40 +02:00
|
|
|
|
|
|
|
|
#include "layoutbuilder.h"
|
|
|
|
|
|
2023-04-25 11:32:55 +02:00
|
|
|
#include <QDebug>
|
2020-09-18 12:11:40 +02:00
|
|
|
#include <QFormLayout>
|
|
|
|
|
#include <QGridLayout>
|
2021-02-18 14:18:34 +01:00
|
|
|
#include <QGroupBox>
|
2023-04-24 16:40:01 +02:00
|
|
|
#include <QLabel>
|
2022-08-01 18:18:02 +02:00
|
|
|
#include <QPushButton>
|
2022-09-01 11:20:58 +02:00
|
|
|
#include <QStackedLayout>
|
2023-04-27 08:24:43 +02:00
|
|
|
#include <QSpacerItem>
|
2023-01-11 14:43:41 +01:00
|
|
|
#include <QSplitter>
|
2020-10-08 07:24:19 +02:00
|
|
|
#include <QStyle>
|
2022-11-18 10:44:10 +01:00
|
|
|
#include <QTabWidget>
|
2023-04-25 16:58:21 +02:00
|
|
|
#include <QTextEdit>
|
2023-04-27 08:24:43 +02:00
|
|
|
#include <QApplication>
|
2020-09-18 12:11:40 +02:00
|
|
|
|
2023-04-25 10:15:07 +02:00
|
|
|
namespace Layouting {
|
2020-09-18 12:11:40 +02:00
|
|
|
|
2023-04-25 11:32:55 +02:00
|
|
|
// That's cut down qtcassert.{c,h} to avoid the dependency.
|
|
|
|
|
#define QTC_STRINGIFY_HELPER(x) #x
|
|
|
|
|
#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
|
|
|
|
|
#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
|
|
|
|
|
#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
|
|
|
|
|
#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
|
|
|
|
|
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
\class Layouting::LayoutItem
|
2020-09-21 09:39:54 +02:00
|
|
|
\inmodule QtCreator
|
|
|
|
|
|
|
|
|
|
\brief The LayoutItem class represents widgets, layouts, and aggregate
|
|
|
|
|
items for use in conjunction with layout builders.
|
|
|
|
|
|
|
|
|
|
Layout items are typically implicitly constructed when adding items to a
|
|
|
|
|
\c LayoutBuilder instance using \c LayoutBuilder::addItem() or
|
|
|
|
|
\c LayoutBuilder::addItems() and never stored in user code.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
Constructs a layout item instance representing an empty cell.
|
|
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
LayoutItem::LayoutItem() = default;
|
|
|
|
|
|
|
|
|
|
LayoutItem::~LayoutItem() = default;
|
2020-09-21 09:39:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
2023-04-24 16:40:01 +02:00
|
|
|
\fn template <class T> LayoutItem(const T &t)
|
2020-09-21 09:39:54 +02:00
|
|
|
|
2023-04-24 16:40:01 +02:00
|
|
|
Constructs a layout item proxy for \a t.
|
2020-09-21 09:39:54 +02:00
|
|
|
|
2023-04-24 16:40:01 +02:00
|
|
|
T could be
|
|
|
|
|
\list
|
|
|
|
|
\li \c {QString}
|
|
|
|
|
\li \c {QWidget *}
|
|
|
|
|
\li \c {QLayout *}
|
|
|
|
|
\endlist
|
2020-09-21 09:39:54 +02:00
|
|
|
*/
|
2021-02-18 14:18:34 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
struct ResultItem
|
|
|
|
|
{
|
|
|
|
|
ResultItem() = default;
|
|
|
|
|
explicit ResultItem(QLayout *l) : layout(l) {}
|
|
|
|
|
explicit ResultItem(QWidget *w) : widget(w) {}
|
2020-09-21 09:39:54 +02:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
QString text;
|
|
|
|
|
QLayout *layout = nullptr;
|
|
|
|
|
QWidget *widget = nullptr;
|
|
|
|
|
int space = -1;
|
|
|
|
|
int stretch = -1;
|
|
|
|
|
int span = 1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct LayoutBuilder::Slice
|
2021-03-11 19:02:42 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
Slice() = default;
|
|
|
|
|
Slice(QLayout *l) : layout(l) {}
|
|
|
|
|
Slice(QWidget *w) : widget(w) {}
|
|
|
|
|
Slice(QWidget *w, AttachType a) : widget(w), attachType(a) {}
|
|
|
|
|
|
2022-02-15 10:41:13 +01:00
|
|
|
QLayout *layout = nullptr;
|
2023-04-27 08:24:43 +02:00
|
|
|
QWidget *widget = nullptr;
|
|
|
|
|
|
|
|
|
|
void flush();
|
|
|
|
|
|
|
|
|
|
int currentGridColumn = 0;
|
|
|
|
|
int currentGridRow = 0;
|
|
|
|
|
|
|
|
|
|
AttachType attachType = WithMargins;
|
|
|
|
|
QList<ResultItem> pendingItems;
|
|
|
|
|
};
|
2021-03-11 19:02:42 +01:00
|
|
|
|
2022-07-14 18:16:31 +02:00
|
|
|
static QWidget *widgetForItem(QLayoutItem *item)
|
|
|
|
|
{
|
|
|
|
|
if (QWidget *w = item->widget())
|
|
|
|
|
return w;
|
|
|
|
|
if (item->spacerItem())
|
|
|
|
|
return nullptr;
|
|
|
|
|
QLayout *l = item->layout();
|
|
|
|
|
if (!l)
|
|
|
|
|
return nullptr;
|
|
|
|
|
for (int i = 0, n = l->count(); i < n; ++i) {
|
|
|
|
|
if (QWidget *w = widgetForItem(l->itemAt(i)))
|
|
|
|
|
return w;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 12:49:50 +01:00
|
|
|
static QLabel *createLabel(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
auto label = new QLabel(text);
|
|
|
|
|
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
|
|
|
return label;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
|
2022-07-15 09:24:41 +02:00
|
|
|
{
|
|
|
|
|
if (QWidget *w = item.widget) {
|
|
|
|
|
layout->addWidget(w);
|
|
|
|
|
} else if (QLayout *l = item.layout) {
|
|
|
|
|
layout->addLayout(l);
|
2023-04-27 08:24:43 +02:00
|
|
|
} else if (item.stretch != -1) {
|
|
|
|
|
layout->addStretch(item.stretch);
|
|
|
|
|
} else if (item.space != -1) {
|
|
|
|
|
layout->addSpacing(item.space);
|
2022-07-15 09:24:41 +02:00
|
|
|
} else if (!item.text.isEmpty()) {
|
2023-01-19 12:49:50 +01:00
|
|
|
layout->addWidget(createLabel(item.text));
|
2022-07-15 09:24:41 +02:00
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutBuilder::Slice::flush()
|
2021-03-11 19:02:42 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
if (pendingItems.empty())
|
2021-03-11 19:02:42 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
if (auto formLayout = qobject_cast<QFormLayout *>(layout)) {
|
|
|
|
|
|
|
|
|
|
// If there are more than two items, we cram the last ones in one hbox.
|
|
|
|
|
if (pendingItems.size() > 2) {
|
|
|
|
|
auto hbox = new QHBoxLayout;
|
|
|
|
|
hbox->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
for (int i = 1; i < pendingItems.size(); ++i)
|
|
|
|
|
addItemToBoxLayout(hbox, pendingItems.at(i));
|
|
|
|
|
while (pendingItems.size() > 1)
|
|
|
|
|
pendingItems.pop_back();
|
|
|
|
|
pendingItems.append(ResultItem(hbox));
|
|
|
|
|
}
|
2021-03-11 19:02:42 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
if (pendingItems.size() == 1) { // One one item given, so this spans both columns.
|
|
|
|
|
const ResultItem &f0 = pendingItems.at(0);
|
|
|
|
|
if (auto layout = f0.layout)
|
|
|
|
|
formLayout->addRow(layout);
|
|
|
|
|
else if (auto widget = f0.widget)
|
|
|
|
|
formLayout->addRow(widget);
|
|
|
|
|
} else if (pendingItems.size() == 2) { // Normal case, both columns used.
|
|
|
|
|
ResultItem &f1 = pendingItems[1];
|
|
|
|
|
const ResultItem &f0 = pendingItems.at(0);
|
|
|
|
|
if (!f1.widget && !f1.layout && !f1.text.isEmpty())
|
|
|
|
|
f1.widget = createLabel(f1.text);
|
|
|
|
|
|
|
|
|
|
if (f0.widget) {
|
|
|
|
|
if (f1.layout)
|
|
|
|
|
formLayout->addRow(f0.widget, f1.layout);
|
|
|
|
|
else if (f1.widget)
|
|
|
|
|
formLayout->addRow(f0.widget, f1.widget);
|
|
|
|
|
} else {
|
|
|
|
|
if (f1.layout)
|
|
|
|
|
formLayout->addRow(f0.text, f1.layout);
|
|
|
|
|
else if (f1.widget)
|
|
|
|
|
formLayout->addRow(f0.text, f1.widget);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
2021-03-11 19:02:42 +01:00
|
|
|
}
|
2023-04-27 08:24:43 +02:00
|
|
|
|
|
|
|
|
// Set up label as buddy if possible.
|
|
|
|
|
const int lastRow = formLayout->rowCount() - 1;
|
|
|
|
|
QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
|
|
|
|
|
QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
|
|
|
|
|
if (l && f) {
|
|
|
|
|
if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
|
|
|
|
|
if (QWidget *widget = widgetForItem(f))
|
|
|
|
|
label->setBuddy(widget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (auto gridLayout = qobject_cast<QGridLayout *>(layout)) {
|
|
|
|
|
|
|
|
|
|
for (const ResultItem &item : std::as_const(pendingItems)) {
|
|
|
|
|
Qt::Alignment align = {};
|
|
|
|
|
// if (attachType == Layouting::WithFormAlignment && currentGridColumn == 0)
|
|
|
|
|
// align = Qt::Alignment(m_widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
|
|
|
|
|
if (item.widget)
|
|
|
|
|
gridLayout->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, align);
|
|
|
|
|
else if (item.layout)
|
|
|
|
|
gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, align);
|
|
|
|
|
else if (!item.text.isEmpty())
|
|
|
|
|
gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, align);
|
|
|
|
|
currentGridColumn += item.span;
|
|
|
|
|
}
|
|
|
|
|
++currentGridRow;
|
|
|
|
|
currentGridColumn = 0;
|
|
|
|
|
|
|
|
|
|
} else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
|
|
|
|
|
|
|
|
|
|
for (const ResultItem &item : std::as_const(pendingItems))
|
|
|
|
|
addItemToBoxLayout(boxLayout, item);
|
|
|
|
|
|
|
|
|
|
} else if (auto stackLayout = qobject_cast<QStackedLayout *>(layout)) {
|
|
|
|
|
for (const ResultItem &item : std::as_const(pendingItems)) {
|
|
|
|
|
if (item.widget)
|
|
|
|
|
stackLayout->addWidget(item.widget);
|
|
|
|
|
else
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 19:02:42 +01:00
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
pendingItems.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
|
|
|
|
|
{
|
|
|
|
|
if (item.onAdd)
|
|
|
|
|
item.onAdd(builder);
|
|
|
|
|
|
|
|
|
|
if (item.setter) {
|
|
|
|
|
if (QWidget *widget = builder.stack.last().widget)
|
|
|
|
|
item.setter(widget);
|
|
|
|
|
else if (QLayout *layout = builder.stack.last().layout)
|
|
|
|
|
item.setter(layout);
|
|
|
|
|
else
|
|
|
|
|
QTC_CHECK(false);
|
2022-07-14 18:16:31 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
for (const LayoutItem &subItem : item.subItems)
|
|
|
|
|
addItemHelper(builder, subItem);
|
|
|
|
|
|
|
|
|
|
if (item.onExit)
|
|
|
|
|
item.onExit(builder);
|
2021-03-11 19:02:42 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void doAddText(LayoutBuilder &builder, const QString &text)
|
2021-03-11 19:02:42 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
ResultItem fi;
|
|
|
|
|
fi.text = text;
|
|
|
|
|
builder.stack.last().pendingItems.append(fi);
|
|
|
|
|
}
|
2021-03-11 19:02:42 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void doAddSpace(LayoutBuilder &builder, const Space &space)
|
|
|
|
|
{
|
|
|
|
|
ResultItem fi;
|
|
|
|
|
fi.space = space.space;
|
|
|
|
|
builder.stack.last().pendingItems.append(fi);
|
|
|
|
|
}
|
2021-03-11 19:02:42 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
|
|
|
|
|
{
|
|
|
|
|
ResultItem fi;
|
|
|
|
|
fi.stretch = stretch.stretch;
|
|
|
|
|
builder.stack.last().pendingItems.append(fi);
|
2021-03-11 19:02:42 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void doAddLayout(LayoutBuilder &builder, QLayout *layout)
|
|
|
|
|
{
|
|
|
|
|
builder.stack.last().pendingItems.append(ResultItem(layout));
|
|
|
|
|
}
|
2021-03-11 19:02:42 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void doAddWidget(LayoutBuilder &builder, QWidget *widget)
|
2021-03-11 19:02:42 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
builder.stack.last().pendingItems.append(ResultItem(widget));
|
2021-03-11 19:02:42 +01:00
|
|
|
}
|
2021-02-18 14:18:34 +01:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
|
2021-02-18 14:18:34 +01:00
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
\class Layouting::Space
|
2021-02-18 14:18:34 +01:00
|
|
|
\inmodule QtCreator
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
\brief The Layouting::Space class represents some empty space in a layout.
|
2021-02-18 14:18:34 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
\class Layouting::Stretch
|
2021-02-18 14:18:34 +01:00
|
|
|
\inmodule QtCreator
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
\brief The Layouting::Stretch class represents some stretch in a layout.
|
2021-02-18 14:18:34 +01:00
|
|
|
*/
|
2020-09-21 09:39:54 +02:00
|
|
|
|
|
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
\class LayoutBuilder
|
2020-09-21 09:39:54 +02:00
|
|
|
\inmodule QtCreator
|
|
|
|
|
|
|
|
|
|
\brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
|
|
|
|
|
and \c QGridLayouts with contents.
|
|
|
|
|
|
|
|
|
|
Filling a layout with items happens item-by-item, row-by-row.
|
|
|
|
|
|
|
|
|
|
A LayoutBuilder instance is typically used locally within a function and never stored.
|
|
|
|
|
|
|
|
|
|
\sa addItem(), addItems(), addRow(), finishRow()
|
|
|
|
|
*/
|
|
|
|
|
|
2021-03-11 19:02:42 +01:00
|
|
|
LayoutBuilder::LayoutBuilder() = default;
|
2020-09-18 12:11:40 +02:00
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
|
|
|
|
Destructs a layout builder.
|
|
|
|
|
*/
|
2021-03-11 19:02:42 +01:00
|
|
|
LayoutBuilder::~LayoutBuilder() = default;
|
2020-09-18 12:11:40 +02:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutBuilder::addItem(const LayoutItem &item)
|
|
|
|
|
{
|
|
|
|
|
addItemHelper(*this, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutBuilder::addItems(const LayoutItems &items)
|
|
|
|
|
{
|
|
|
|
|
for (const LayoutItem &item : items)
|
|
|
|
|
addItemHelper(*this, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutBuilder::addRow(const LayoutItems &items)
|
|
|
|
|
{
|
|
|
|
|
addItem(br);
|
|
|
|
|
addItems(items);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
|
|
|
|
Instructs a layout builder to finish the current row.
|
|
|
|
|
This is implicitly called by LayoutBuilder's destructor.
|
|
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutItem::finishRow()
|
2020-09-18 12:11:40 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
addItem(br);
|
2020-09-18 12:11:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
|
|
|
|
This starts a new row containing \a items. The row can be further extended by
|
|
|
|
|
other items using \c addItem() or \c addItems().
|
|
|
|
|
|
|
|
|
|
\sa finishRow(), addItem(), addItems()
|
|
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutItem::addRow(const LayoutItems &items)
|
2020-09-18 04:54:41 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
finishRow();
|
|
|
|
|
addItems(items);
|
2020-09-18 12:11:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
Adds the layout item \a item as sub items.
|
2020-09-21 09:39:54 +02:00
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutItem::addItem(const LayoutItem &item)
|
2020-09-18 12:11:40 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
subItems.append(item);
|
2020-09-18 12:11:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
2023-04-27 08:24:43 +02:00
|
|
|
Adds the layout items \a items as sub items.
|
2020-09-21 09:39:54 +02:00
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
void LayoutItem::addItems(const LayoutItems &items)
|
2021-02-18 14:18:34 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
subItems.append(items);
|
2021-02-18 14:18:34 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
2021-03-11 19:02:42 +01:00
|
|
|
Attach the constructed layout to the provided \c QWidget \a parent.
|
|
|
|
|
|
|
|
|
|
This operation can only be performed once per LayoutBuilder instance.
|
2020-09-21 09:39:54 +02:00
|
|
|
*/
|
2023-04-27 08:24:43 +02:00
|
|
|
|
|
|
|
|
void LayoutItem::attachTo(QWidget *w, AttachType attachType) const
|
2020-09-18 12:11:40 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
LayoutBuilder builder;
|
|
|
|
|
|
|
|
|
|
builder.stack.append({w, attachType});
|
|
|
|
|
addItemHelper(builder, *this);
|
2021-03-11 19:02:42 +01:00
|
|
|
}
|
2020-09-18 12:11:40 +02:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
QWidget *LayoutItem::emerge(Layouting::AttachType attachType)
|
2021-03-11 19:02:42 +01:00
|
|
|
{
|
|
|
|
|
auto w = new QWidget;
|
2023-04-27 08:24:43 +02:00
|
|
|
attachTo(w, attachType);
|
2021-03-11 19:02:42 +01:00
|
|
|
return w;
|
2020-09-18 12:11:40 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
bool LayoutBuilder::isForm() const
|
|
|
|
|
{
|
|
|
|
|
return qobject_cast<QFormLayout *>(stack.last().layout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void layoutExit(LayoutBuilder &builder)
|
|
|
|
|
{
|
|
|
|
|
builder.stack.last().flush();
|
|
|
|
|
QLayout *layout = builder.stack.last().layout;
|
|
|
|
|
if (builder.stack.back().attachType == WithoutMargins)
|
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
builder.stack.pop_back();
|
|
|
|
|
|
|
|
|
|
if (QWidget *widget = builder.stack.last().widget)
|
|
|
|
|
widget->setLayout(layout);
|
|
|
|
|
else
|
|
|
|
|
builder.stack.last().pendingItems.append(ResultItem(layout));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void widgetExit(LayoutBuilder &builder)
|
|
|
|
|
{
|
|
|
|
|
QWidget *widget = builder.stack.last().widget;
|
|
|
|
|
if (builder.stack.back().attachType == WithoutMargins)
|
|
|
|
|
widget->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
builder.stack.pop_back();
|
|
|
|
|
builder.stack.last().pendingItems.append(ResultItem(widget));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Column::Column(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
|
|
|
|
|
onExit = layoutExit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Row::Row(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
|
|
|
|
|
onExit = layoutExit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Grid::Grid(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
|
|
|
|
|
onExit = layoutExit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QFormLayout *newFormLayout()
|
|
|
|
|
{
|
|
|
|
|
auto formLayout = new QFormLayout;
|
|
|
|
|
formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
|
|
|
|
return formLayout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Form::Form(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
|
|
|
|
|
onExit = layoutExit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stack::Stack(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QStackedLayout); };
|
|
|
|
|
onExit = layoutExit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LayoutItem br()
|
|
|
|
|
{
|
|
|
|
|
LayoutItem item;
|
|
|
|
|
item.onAdd = [](LayoutBuilder &builder) {
|
|
|
|
|
builder.stack.last().flush();
|
|
|
|
|
};
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LayoutItem empty()
|
|
|
|
|
{
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 09:39:54 +02:00
|
|
|
/*!
|
2021-03-11 19:02:42 +01:00
|
|
|
Constructs a layout extender to extend an existing \a layout.
|
|
|
|
|
|
|
|
|
|
This constructor can be used to continue the work of previous layout building.
|
|
|
|
|
The type of the underlying layout and previous contents will be retained,
|
|
|
|
|
new items will be added below existing ones.
|
2020-09-21 09:39:54 +02:00
|
|
|
*/
|
2020-09-18 04:54:41 +02:00
|
|
|
|
2022-07-28 11:01:05 +02:00
|
|
|
LayoutExtender::LayoutExtender(QLayout *layout, Layouting::AttachType attachType)
|
2021-02-18 14:18:34 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
Slice slice;
|
|
|
|
|
slice.layout = layout;
|
|
|
|
|
if (auto gridLayout = qobject_cast<QGridLayout *>(layout))
|
|
|
|
|
slice.currentGridRow = gridLayout->rowCount();
|
|
|
|
|
slice.attachType = attachType;
|
|
|
|
|
stack.append(slice);
|
2021-02-18 14:18:34 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
LayoutExtender::~LayoutExtender() = default;
|
2022-11-18 10:44:10 +01:00
|
|
|
|
2022-08-01 18:18:02 +02:00
|
|
|
// "Widgets"
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
template <class T>
|
|
|
|
|
void setupWidget(LayoutItem *item)
|
2022-08-10 17:13:21 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
|
|
|
|
|
item->onExit = widgetExit;
|
|
|
|
|
};
|
2022-07-21 11:10:34 +02:00
|
|
|
|
2022-11-18 10:44:10 +01:00
|
|
|
Group::Group(std::initializer_list<LayoutItem> items)
|
2021-03-02 12:48:46 +01:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
this->subItems = items;
|
|
|
|
|
setupWidget<QGroupBox>(this);
|
2021-03-02 12:48:46 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-18 10:44:10 +01:00
|
|
|
PushButton::PushButton(std::initializer_list<LayoutItem> items)
|
2022-08-01 18:18:02 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
this->subItems = items;
|
|
|
|
|
setupWidget<QPushButton>(this);
|
2022-08-01 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-25 16:58:21 +02:00
|
|
|
TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
this->subItems = items;
|
|
|
|
|
setupWidget<QTextEdit>(this);
|
2023-04-25 16:58:21 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-11 14:43:41 +01:00
|
|
|
Splitter::Splitter(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
this->subItems = items;
|
|
|
|
|
setupWidget<QSplitter>(this); // FIXME: Default was Qt::Vertical)
|
|
|
|
|
}
|
2022-11-18 10:44:10 +01:00
|
|
|
|
2023-04-25 17:21:39 +02:00
|
|
|
TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
|
2023-04-27 08:24:43 +02:00
|
|
|
{
|
|
|
|
|
this->subItems = items;
|
|
|
|
|
setupWidget<QTabWidget>(this);
|
2022-11-18 10:44:10 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
// Special Tab
|
2022-08-01 18:18:02 +02:00
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
Tab::Tab(const QString &tabName, const LayoutItem &item)
|
2023-04-25 17:21:39 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
onAdd = [item](LayoutBuilder &builder) {
|
|
|
|
|
auto tab = new QWidget;
|
|
|
|
|
builder.stack.append(tab);
|
|
|
|
|
item.attachTo(tab);
|
|
|
|
|
};
|
|
|
|
|
onExit = [tabName](LayoutBuilder &builder) {
|
|
|
|
|
QWidget *inner = builder.stack.last().widget;
|
|
|
|
|
builder.stack.pop_back();
|
|
|
|
|
auto tabWidget = qobject_cast<QTabWidget *>(builder.stack.last().widget);
|
|
|
|
|
QTC_ASSERT(tabWidget, return);
|
|
|
|
|
tabWidget->addTab(inner, tabName);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Special Application
|
|
|
|
|
|
|
|
|
|
Application::Application(std::initializer_list<LayoutItem> items)
|
|
|
|
|
{
|
|
|
|
|
subItems = items;
|
|
|
|
|
setupWidget<QWidget>(this);
|
|
|
|
|
onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Application::exec(int &argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
auto app = new QApplication(argc, argv);
|
|
|
|
|
LayoutBuilder builder;
|
|
|
|
|
addItemHelper(builder, *this);
|
|
|
|
|
if (QWidget *widget = builder.stack.last().widget)
|
|
|
|
|
widget->show();
|
|
|
|
|
return app->exec();
|
2023-04-25 17:21:39 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
// "Properties"
|
|
|
|
|
|
2023-04-25 17:21:39 +02:00
|
|
|
LayoutItem title(const QString &title)
|
2022-07-21 12:10:56 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [title](QObject *target) {
|
2022-07-21 12:10:56 +02:00
|
|
|
if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
|
|
|
|
|
groupBox->setTitle(title);
|
|
|
|
|
groupBox->setObjectName(title);
|
2023-04-27 08:24:43 +02:00
|
|
|
} else if (auto widget = qobject_cast<QWidget *>(target)) {
|
|
|
|
|
widget->setWindowTitle(title);
|
2022-07-21 12:10:56 +02:00
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
2023-04-27 08:24:43 +02:00
|
|
|
};
|
2022-07-21 12:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-25 17:21:39 +02:00
|
|
|
LayoutItem onClicked(const std::function<void ()> &func, QObject *guard)
|
2022-08-01 18:18:02 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [func, guard](QObject *target) {
|
2022-08-01 18:18:02 +02:00
|
|
|
if (auto button = qobject_cast<QAbstractButton *>(target)) {
|
|
|
|
|
QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func);
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
2023-04-27 08:24:43 +02:00
|
|
|
};
|
2022-08-01 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-25 17:21:39 +02:00
|
|
|
LayoutItem text(const QString &text)
|
2022-08-01 18:18:02 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [text](QObject *target) {
|
2022-08-01 18:18:02 +02:00
|
|
|
if (auto button = qobject_cast<QAbstractButton *>(target)) {
|
|
|
|
|
button->setText(text);
|
2023-04-25 16:58:21 +02:00
|
|
|
} else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
|
|
|
|
|
textEdit->setText(text);
|
2022-08-01 18:18:02 +02:00
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
2023-04-27 08:24:43 +02:00
|
|
|
};
|
2022-08-01 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-25 17:21:39 +02:00
|
|
|
LayoutItem tooltip(const QString &toolTip)
|
2022-08-01 18:18:02 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [toolTip](QObject *target) {
|
2022-08-01 18:18:02 +02:00
|
|
|
if (auto widget = qobject_cast<QWidget *>(target)) {
|
|
|
|
|
widget->setToolTip(toolTip);
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
2023-04-27 08:24:43 +02:00
|
|
|
};
|
2023-04-25 17:21:39 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
LayoutItem spacing(int spacing)
|
2023-04-25 17:21:39 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [spacing](QObject *target) {
|
|
|
|
|
if (auto layout = qobject_cast<QLayout *>(target)) {
|
|
|
|
|
layout->setSpacing(spacing);
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-04-25 17:21:39 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 08:24:43 +02:00
|
|
|
LayoutItem resize(int w, int h)
|
2023-04-25 17:21:39 +02:00
|
|
|
{
|
2023-04-27 08:24:43 +02:00
|
|
|
return [w, h](QObject *target) {
|
|
|
|
|
if (auto widget = qobject_cast<QWidget *>(target)) {
|
|
|
|
|
widget->resize(w, h);
|
|
|
|
|
} else {
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-08-01 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-26 23:18:00 +02:00
|
|
|
QWidget *createHr(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
auto frame = new QFrame(parent);
|
|
|
|
|
frame->setFrameShape(QFrame::HLine);
|
|
|
|
|
frame->setFrameShadow(QFrame::Sunken);
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 13:51:52 +01:00
|
|
|
// Singletons.
|
2023-04-27 08:24:43 +02:00
|
|
|
|
|
|
|
|
LayoutItem::LayoutItem(const LayoutItem &t)
|
|
|
|
|
{
|
|
|
|
|
operator=(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, LayoutItem(*t)())
|
|
|
|
|
{
|
|
|
|
|
*item = t();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
|
|
|
|
|
{
|
|
|
|
|
item->setter = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, QWidget *t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, QLayout *t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, const QString &t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, const Space &t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, const Stretch &t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createItem(LayoutItem *item, const Span &t)
|
|
|
|
|
{
|
|
|
|
|
item->onAdd = [t](LayoutBuilder &builder) {
|
|
|
|
|
addItemHelper(builder, t.item);
|
|
|
|
|
builder.stack.last().pendingItems.last().span = t.span;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LayoutItem hr()
|
|
|
|
|
{
|
|
|
|
|
LayoutItem item;
|
|
|
|
|
item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LayoutItem st()
|
|
|
|
|
{
|
|
|
|
|
LayoutItem item;
|
|
|
|
|
item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
|
|
|
|
|
return item;
|
|
|
|
|
}
|
2022-07-22 18:54:04 +02:00
|
|
|
|
2023-04-25 10:15:07 +02:00
|
|
|
} // Layouting
|