diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 664f4773872..6b9823c1175 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -279,14 +280,14 @@ QLabel *BaseAspect::createLabel() return label; } -void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget) +void BaseAspect::addLabeledItem(Layout &parent, QWidget *widget) { if (QLabel *l = createLabel()) { l->setBuddy(widget); parent.addItem(l); - parent.addItem(Span(std::max(d->m_spanX - 1, 1), LayoutItem(widget))); + parent.addItem(Span(std::max(d->m_spanX - 1, 1), widget)); } else { - parent.addItem(LayoutItem(widget)); + parent.addItem(widget); } } @@ -532,21 +533,20 @@ AspectContainer *BaseAspect::container() const Adds the visual representation of this aspect to the layout with the specified \a parent using a layout builder. */ -void BaseAspect::addToLayout(LayoutItem &) +void BaseAspect::addToLayout(Layout &) { } -void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect) +void addToLayout(Layouting::Layout *iface, BaseAspect &aspect) { - const_cast(aspect).addToLayout(*item); + aspect.addToLayout(*iface); } -void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect) +void addToLayout(Layouting::Layout *item, BaseAspect *aspect) { - const_cast(aspect)->addToLayout(*item); + aspect->addToLayout(*item); } - /*! Updates this aspect's value from user-initiated changes in the widget. @@ -858,15 +858,15 @@ public: aspect->bufferToGui(); } - void addToLayoutFirst(LayoutItem &parent) + void addToLayoutFirst(Layout &parent) { if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Top) { m_checked->addToLayout(parent); - parent.addItem(br); + parent.flush(); } } - void addToLayoutLast(LayoutItem &parent) + void addToLayoutLast(Layout &parent) { if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Right) m_checked->addToLayout(parent); @@ -1140,7 +1140,7 @@ void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished) d->m_autoApplyOnEditingFinished = applyOnEditingFinished; } -void StringAspect::addToLayout(LayoutItem &parent) +void StringAspect::addToLayout(Layout &parent) { d->m_checkerImpl.addToLayoutFirst(parent); @@ -1551,7 +1551,7 @@ PathChooser *FilePathAspect::pathChooser() const return d->m_pathChooserDisplay.data(); } -void FilePathAspect::addToLayout(Layouting::LayoutItem &parent) +void FilePathAspect::addToLayout(Layouting::Layout &parent) { d->m_checkerImpl.addToLayoutFirst(parent); @@ -1769,7 +1769,7 @@ ColorAspect::ColorAspect(AspectContainer *container) ColorAspect::~ColorAspect() = default; -void ColorAspect::addToLayout(Layouting::LayoutItem &parent) +void ColorAspect::addToLayout(Layouting::Layout &parent) { QTC_CHECK(!d->m_colorButton); d->m_colorButton = createSubWidget(); @@ -1946,7 +1946,7 @@ BoolAspect::BoolAspect(AspectContainer *container) */ BoolAspect::~BoolAspect() = default; -void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button) +void BoolAspect::addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button) { switch (d->m_labelPlacement) { case LabelPlacement::Compact: @@ -1955,7 +1955,7 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto break; case LabelPlacement::AtCheckBox: button->setText(labelText()); - parent.addItem(empty()); + parent.addItem(empty); parent.addItem(button); break; case LabelPlacement::InExtraLabel: @@ -1973,20 +1973,18 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto }); } -LayoutItem BoolAspect::adoptButton(QAbstractButton *button) +std::function BoolAspect::adoptButton(QAbstractButton *button) { - LayoutItem parent; - - addToLayoutHelper(parent, button); - - bufferToGui(); - return parent; + return [this, button](Layouting::Layout *layout) { + addToLayoutHelper(*layout, button); + bufferToGui(); + }; } /*! \reimp */ -void BoolAspect::addToLayout(Layouting::LayoutItem &parent) +void BoolAspect::addToLayout(Layouting::Layout &parent) { QCheckBox *checkBox = createSubWidget(); addToLayoutHelper(parent, checkBox); @@ -2092,7 +2090,7 @@ SelectionAspect::~SelectionAspect() = default; /*! \reimp */ -void SelectionAspect::addToLayout(Layouting::LayoutItem &parent) +void SelectionAspect::addToLayout(Layouting::Layout &parent) { QTC_CHECK(d->m_buttonGroup == nullptr); QTC_CHECK(!d->m_comboBox); @@ -2266,7 +2264,7 @@ MultiSelectionAspect::~MultiSelectionAspect() = default; /*! \reimp */ -void MultiSelectionAspect::addToLayout(LayoutItem &builder) +void MultiSelectionAspect::addToLayout(Layout &builder) { QTC_CHECK(d->m_listView == nullptr); if (d->m_allValues.isEmpty()) @@ -2375,7 +2373,7 @@ IntegerAspect::~IntegerAspect() = default; /*! \reimp */ -void IntegerAspect::addToLayout(Layouting::LayoutItem &parent) +void IntegerAspect::addToLayout(Layouting::Layout &parent) { QTC_CHECK(!d->m_spinBox); d->m_spinBox = createSubWidget(); @@ -2477,7 +2475,7 @@ DoubleAspect::~DoubleAspect() = default; /*! \reimp */ -void DoubleAspect::addToLayout(LayoutItem &builder) +void DoubleAspect::addToLayout(Layout &builder) { QTC_CHECK(!d->m_spinBox); d->m_spinBox = createSubWidget(); @@ -2634,7 +2632,7 @@ StringListAspect::~StringListAspect() = default; /*! \reimp */ -void StringListAspect::addToLayout(LayoutItem &parent) +void StringListAspect::addToLayout(Layout &parent) { Q_UNUSED(parent) // TODO - when needed. @@ -2710,7 +2708,7 @@ void FilePathListAspect::bufferToGui() d->undoable.setWithoutUndo(m_buffer); } -void FilePathListAspect::addToLayout(LayoutItem &parent) +void FilePathListAspect::addToLayout(Layout &parent) { d->undoable.setSilently(value()); @@ -2804,7 +2802,7 @@ IntegersAspect::~IntegersAspect() = default; /*! \reimp */ -void IntegersAspect::addToLayout(Layouting::LayoutItem &parent) +void IntegersAspect::addToLayout(Layouting::Layout &parent) { Q_UNUSED(parent) // TODO - when needed. @@ -2841,7 +2839,7 @@ TextDisplay::~TextDisplay() = default; /*! \reimp */ -void TextDisplay::addToLayout(LayoutItem &parent) +void TextDisplay::addToLayout(Layout &parent) { if (!d->m_label) { d->m_label = createSubWidget(d->m_message, d->m_type); @@ -2893,7 +2891,7 @@ public: QList m_items; // Both owned and non-owned. QList m_ownedItems; // Owned only. QStringList m_settingsGroup; - std::function m_layouter; + std::function m_layouter; }; AspectContainer::AspectContainer() @@ -2908,6 +2906,11 @@ AspectContainer::~AspectContainer() qDeleteAll(d->m_ownedItems); } +void AspectContainer::addToLayout(Layouting::Layout &parent) +{ + parent.addItem(layouter()()); +} + /*! \internal */ @@ -2945,12 +2948,12 @@ AspectContainer::const_iterator AspectContainer::end() const return d->m_items.cend(); } -void AspectContainer::setLayouter(const std::function &layouter) +void AspectContainer::setLayouter(const std::function &layouter) { d->m_layouter = layouter; } -std::function AspectContainer::layouter() const +std::function AspectContainer::layouter() const { return d->m_layouter; } @@ -3447,7 +3450,7 @@ private: int m_index; }; -void AspectList::addToLayout(Layouting::LayoutItem &parent) +void AspectList::addToLayout(Layouting::Layout &parent) { using namespace Layouting; @@ -3466,7 +3469,7 @@ void AspectList::addToLayout(Layouting::LayoutItem &parent) addItem(d->createItem()); }); - Column column{noMargin()}; + Column column{noMargin}; forEachItem([&column, this](const std::shared_ptr &item, int idx) { auto removeBtn = new IconButton; @@ -3557,7 +3560,7 @@ bool StringSelectionAspect::guiToBuffer() return oldBuffer != m_buffer; } -void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent) +void StringSelectionAspect::addToLayout(Layouting::Layout &parent) { QTC_ASSERT(m_fillCallback, return); @@ -3630,4 +3633,4 @@ void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent) return addLabeledItem(parent, comboBox); } -} // namespace Utils +} // Utils diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index a5c22bb8584..13c1c22e4fc 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -29,9 +29,7 @@ class QStandardItemModel; class QItemSelectionModel; QT_END_NAMESPACE -namespace Layouting { -class LayoutItem; -} +namespace Layouting { class Layout; } namespace Utils { @@ -64,6 +62,7 @@ class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject public: BaseAspect(AspectContainer *container = nullptr); + BaseAspect(const BaseAspect &) = delete; ~BaseAspect() override; Id id() const; @@ -125,9 +124,7 @@ public: virtual void toMap(Store &map) const; virtual void toActiveMap(Store &map) const { toMap(map); } virtual void volatileToMap(Store &map) const; - - virtual void addToLayout(Layouting::LayoutItem &parent); - + virtual void addToLayout(Layouting::Layout &parent); virtual void readSettings(); virtual void writeSettings() const; @@ -223,7 +220,7 @@ protected: virtual void handleGuiChanged(); QLabel *createLabel(); - void addLabeledItem(Layouting::LayoutItem &parent, QWidget *widget); + void addLabeledItem(Layouting::Layout &parent, QWidget *widget); void setDataCreatorHelper(const DataCreator &creator) const; void setDataClonerHelper(const DataCloner &cloner) const; @@ -276,8 +273,8 @@ private: friend class Internal::CheckableAspectImplementation; }; -QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect); -QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect); +QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect *aspect); +QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect &aspect); template class @@ -439,7 +436,7 @@ public: BoolAspect(AspectContainer *container = nullptr); ~BoolAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; std::function groupChecker(); Utils::CheckableDecider askAgainCheckableDecider(); @@ -452,10 +449,10 @@ public: LabelPlacement labelPlacement = LabelPlacement::InExtraLabel); void setLabelPlacement(LabelPlacement labelPlacement); - Layouting::LayoutItem adoptButton(QAbstractButton *button); + std::function adoptButton(QAbstractButton *button); private: - void addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button); + void addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button); void bufferToGui() override; bool guiToBuffer() override; @@ -504,7 +501,7 @@ public: ColorAspect(AspectContainer *container = nullptr); ~ColorAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; private: void bufferToGui() override; @@ -521,7 +518,7 @@ public: SelectionAspect(AspectContainer *container = nullptr); ~SelectionAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void finish() override; QString stringValue() const; @@ -569,7 +566,7 @@ public: MultiSelectionAspect(AspectContainer *container = nullptr); ~MultiSelectionAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; enum class DisplayStyle { ListView }; void setDisplayStyle(DisplayStyle style); @@ -596,7 +593,7 @@ public: StringAspect(AspectContainer *container = nullptr); ~StringAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; QString operator()() const { return expandedValue(); } QString expandedValue() const; @@ -703,7 +700,7 @@ public: PathChooser *pathChooser() const; // Avoid to use. - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void fromMap(const Utils::Store &map) override; void toMap(Utils::Store &map) const override; @@ -730,7 +727,7 @@ public: IntegerAspect(AspectContainer *container = nullptr); ~IntegerAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void setRange(qint64 min, qint64 max); void setLabel(const QString &label); // FIXME: Use setLabelText @@ -759,7 +756,7 @@ public: DoubleAspect(AspectContainer *container = nullptr); ~DoubleAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void setRange(double min, double max); void setPrefix(const QString &prefix); @@ -831,7 +828,7 @@ public: StringListAspect(AspectContainer *container = nullptr); ~StringListAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void appendValue(const QString &value, bool allowDuplicates = true); void removeValue(const QString &value); @@ -855,7 +852,7 @@ public: bool guiToBuffer() override; void bufferToGui() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void setPlaceHolderText(const QString &placeHolderText); void appendValue(const FilePath &path, bool allowDuplicates = true); @@ -875,7 +872,7 @@ public: IntegersAspect(AspectContainer *container = nullptr); ~IntegersAspect() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; }; class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect @@ -888,7 +885,7 @@ public: InfoLabel::InfoType type = InfoLabel::None); ~TextDisplay() override; - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; void setIconType(InfoLabel::InfoType t); void setText(const QString &message); @@ -939,6 +936,8 @@ public: AspectContainer(const AspectContainer &) = delete; AspectContainer &operator=(const AspectContainer &) = delete; + void addToLayout(Layouting::Layout &parent) override; + void registerAspect(BaseAspect *aspect, bool takeOwnership = false); void registerAspects(const AspectContainer &aspects); @@ -989,8 +988,8 @@ public: const_iterator begin() const; const_iterator end() const; - void setLayouter(const std::function &layouter); - std::function layouter() const; + void setLayouter(const std::function &layouter); + std::function layouter() const; signals: void applied(); @@ -1131,7 +1130,7 @@ public: QVariant volatileVariantValue() const override { return {}; } - void addToLayout(Layouting::LayoutItem &parent) override; + void addToLayout(Layouting::Layout &parent) override; private: std::unique_ptr d; @@ -1143,7 +1142,7 @@ class QTCREATOR_UTILS_EXPORT StringSelectionAspect : public Utils::TypedAspect items)>; using FillCallback = std::function; diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 31c67e4d0f6..029b41c3111 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -3,7 +3,6 @@ #include "layoutbuilder.h" -#include #include #include #include @@ -29,10 +28,23 @@ namespace Layouting { #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) -class FlowLayout final : public QLayout +template +typename X::Implementation *access(const X *x) { - Q_OBJECT + return static_cast(x->ptr); +} +template +void apply(X *x, std::initializer_list ps) +{ + for (auto && p : ps) + p.apply(x); +} + +// FlowLayout + +class FlowLayout : public QLayout +{ public: explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1) : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) @@ -181,29 +193,49 @@ private: \namespace Layouting \inmodule QtCreator - \brief The Layouting namespace contains classes for use with layout builders. + \brief The Layouting namespace contains classes and functions to conveniently + create layouts in code. + + Classes in the namespace help to create create QLayout or QWidget derived class, + instances should be used locally within a function and never stored. + + \sa Layouting::Widget, Layouting::Layout */ +/*! + \class Layouting::Layout + \inmodule QtCreator + + The Layout class is a base class for more specific builder + classes to create QLayout derived objects. + */ + +/*! + \class Layouting::Widget + \inmodule QtCreator + + The Widget class is a base class for more specific builder + classes to create QWidget derived objects. +*/ + /*! \class Layouting::LayoutItem \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. + The LayoutItem class is used for intermediate results + while creating layouts with a concept of rows and spans, such + as Form and Grid. */ -/*! - Constructs a layout item instance representing an empty cell. - */ LayoutItem::LayoutItem() = default; LayoutItem::~LayoutItem() = default; +LayoutItem::LayoutItem(const LayoutModifier &inner) +{ + ownerModifier = inner; +} /*! \fn template LayoutItem(const T &t) @@ -217,44 +249,15 @@ LayoutItem::~LayoutItem() = default; \li \c {QWidget *} \li \c {QLayout *} \endlist - */ +*/ -struct ResultItem +// Object + +Object::Object(std::initializer_list ps) { - ResultItem() = default; - explicit ResultItem(QLayout *l) : layout(l), empty(!l) {} - explicit ResultItem(QWidget *w) : widget(w), empty(!w) {} - - QString text; - QLayout *layout = nullptr; - QWidget *widget = nullptr; - int space = -1; - int stretch = -1; - int span = 1; - bool empty = false; -}; - -struct Slice -{ - Slice() = default; - Slice(QLayout *l) : layout(l) {} - Slice(QWidget *w, bool isLayouting=false) : widget(w), isLayouting(isLayouting) {} - - QLayout *layout = nullptr; - QWidget *widget = nullptr; - - void flush(); - - // Grid-specific - int currentGridColumn = 0; - int currentGridRow = 0; - bool isFormAlignment = false; - bool isLayouting = false; - Qt::Alignment align = {}; // Can be changed to - - // Grid or Form - QList pendingItems; -}; + ptr = new Implementation; + apply(this, ps); +} static QWidget *widgetForItem(QLayoutItem *item) { @@ -262,12 +265,11 @@ static QWidget *widgetForItem(QLayoutItem *item) 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; + if (QLayout *l = item->layout()) { + for (int i = 0, n = l->count(); i < n; ++i) { + if (QWidget *w = widgetForItem(l->itemAt(i))) + return w; + } } return nullptr; } @@ -279,7 +281,7 @@ static QLabel *createLabel(const QString &text) return label; } -static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item) +static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item) { if (QWidget *w = item.widget) { layout->addWidget(w); @@ -287,8 +289,6 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item) layout->addLayout(l); } else if (item.stretch != -1) { layout->addStretch(item.stretch); - } else if (item.space != -1) { - layout->addSpacing(item.space); } else if (!item.text.isEmpty()) { layout->addWidget(createLabel(item.text)); } else if (item.empty) { @@ -298,7 +298,7 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item) } } -static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item) +static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item) { if (QWidget *w = item.widget) { layout->addWidget(w); @@ -306,8 +306,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item) layout->addItem(l); // } else if (item.stretch != -1) { // layout->addStretch(item.stretch); -// } else if (item.space != -1) { -// layout->addSpacing(item.space); } else if (item.empty) { // Nothing to do, but no reason to warn, either } else if (!item.text.isEmpty()) { @@ -317,163 +315,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item) } } -void Slice::flush() -{ - if (pendingItems.empty()) - return; - - if (auto formLayout = qobject_cast(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)); - } - - 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(createLabel(f0.text), f1.layout); - else if (f1.widget) - formLayout->addRow(createLabel(f0.text), f1.widget); - } - } else { - QTC_CHECK(false); - } - - // 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(l->widget())) { - if (QWidget *widget = widgetForItem(f)) - label->setBuddy(widget); - } - } - - } else if (auto gridLayout = qobject_cast(layout)) { - - for (const ResultItem &item : std::as_const(pendingItems)) { - Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment(); - if (item.widget) - gridLayout->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, a); - else if (item.layout) - gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, a); - else if (!item.text.isEmpty()) - gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, a); - currentGridColumn += item.span; - } - ++currentGridRow; - currentGridColumn = 0; - - } else if (auto boxLayout = qobject_cast(layout)) { - - for (const ResultItem &item : std::as_const(pendingItems)) - addItemToBoxLayout(boxLayout, item); - - } else if (auto flowLayout = qobject_cast(layout)) { - - for (const ResultItem &item : std::as_const(pendingItems)) - addItemToFlowLayout(flowLayout, item); - - } else { - QTC_CHECK(false); - } - - pendingItems.clear(); -} - -// LayoutBuilder - -class LayoutBuilder -{ - Q_DISABLE_COPY_MOVE(LayoutBuilder) - -public: - LayoutBuilder(); - ~LayoutBuilder(); - - void addItem(const LayoutItem &item); - void addItems(const LayoutItems &items); - - QList stack; -}; - -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); - } - - for (const LayoutItem &subItem : item.subItems) - addItemHelper(builder, subItem); - - if (item.onExit) - item.onExit(builder); -} - -void doAddText(LayoutBuilder &builder, const QString &text) -{ - ResultItem fi; - fi.text = text; - builder.stack.last().pendingItems.append(fi); -} - -void doAddSpace(LayoutBuilder &builder, const Space &space) -{ - ResultItem fi; - fi.space = space.space; - builder.stack.last().pendingItems.append(fi); -} - -void doAddStretch(LayoutBuilder &builder, const Stretch &stretch) -{ - ResultItem fi; - fi.stretch = stretch.stretch; - builder.stack.last().pendingItems.append(fi); -} - -void doAddLayout(LayoutBuilder &builder, QLayout *layout) -{ - builder.stack.last().pendingItems.append(ResultItem(layout)); -} - -void doAddWidget(LayoutBuilder &builder, QWidget *widget) -{ - builder.stack.last().pendingItems.append(ResultItem(widget)); -} - - /*! \class Layouting::Space \inmodule QtCreator @@ -488,39 +329,67 @@ void doAddWidget(LayoutBuilder &builder, QWidget *widget) \brief The Stretch class represents some stretch in a layout. */ -/*! - \class Layouting::LayoutBuilder - \internal - \inmodule QtCreator - \brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout - and \c QGridLayouts with contents. +// Layout - 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() -*/ - - -LayoutBuilder::LayoutBuilder() = default; - -/*! - \internal - Destructs a layout builder. - */ -LayoutBuilder::~LayoutBuilder() = default; - -void LayoutBuilder::addItem(const LayoutItem &item) +void Layout::span(int cols, int rows) { - addItemHelper(*this, item); + QTC_ASSERT(!pendingItems.empty(), return); + pendingItems.back().spanCols = cols; + pendingItems.back().spanRows = rows; } -void LayoutBuilder::addItems(const LayoutItems &items) +void Layout::noMargin() { - for (const LayoutItem &item : items) - addItemHelper(*this, item); + customMargin({}); +} + +void Layout::normalMargin() +{ + customMargin({9, 9, 9, 9}); +} + +void Layout::customMargin(const QMargins &margin) +{ + access(this)->setContentsMargins(margin); +} + +/*! + Attaches the constructed layout to the provided QWidget \a w. + + This operation can only be performed once per LayoutBuilder instance. + */ +void Layout::attachTo(QWidget *widget) +{ + flush(); + widget->setLayout(access(this)); +} + +/*! + Adds the layout item \a item as sub items. + */ +void Layout::addItem(I item) +{ + item.apply(this); +} + +void Layout::addLayoutItem(const LayoutItem &item) +{ + if (QBoxLayout *lt = asBox()) + addItemToBoxLayout(lt, item); + else if (FlowLayout *lt = asFlow()) + addItemToFlowLayout(lt, item); + else + pendingItems.push_back(item); +} + +/*! + Adds the layout items \a items as sub items. + */ +void Layout::addItems(std::initializer_list items) +{ + for (const I &item : items) + item.apply(this); } /*! @@ -529,485 +398,527 @@ void LayoutBuilder::addItems(const LayoutItems &items) \sa addItem(), addItems() */ -void LayoutItem::addRow(const LayoutItems &items) + +void Layout::addRow(std::initializer_list items) { - addItem(br); - addItems(items); + for (const I &item : items) + item.apply(this); + flush(); } -/*! - Adds the layout item \a item as sub items. - */ -void LayoutItem::addItem(const LayoutItem &item) +void Layout::setSpacing(int spacing) { - subItems.append(item); + access(this)->setSpacing(spacing); } -/*! - Adds the layout items \a items as sub items. - */ -void LayoutItem::addItems(const LayoutItems &items) +void Layout::setColumnStretch(int column, int stretch) { - subItems.append(items); -} - -/*! - Attaches the constructed layout to the provided QWidget \a w. - - This operation can only be performed once per LayoutBuilder instance. - */ - -void LayoutItem::attachTo(QWidget *w) const -{ - LayoutBuilder builder; - - builder.stack.append(w); - addItemHelper(builder, *this); -} - -QWidget *LayoutItem::emerge() -{ - LayoutBuilder builder; - - builder.stack.append(Slice()); - addItemHelper(builder, *this); - - if (builder.stack.empty()) - return nullptr; - - QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr); - ResultItem ri = builder.stack.last().pendingItems.takeFirst(); - - QTC_ASSERT(ri.layout || ri.widget, return nullptr); - - if (ri.layout) { - auto w = new QWidget; - w->setLayout(ri.layout); - return w; + if (auto grid = qobject_cast(access(this))) { + grid->setColumnStretch(column, stretch); + } else { + QTC_CHECK(false); } - - return ri.widget; } -static void layoutExit(LayoutBuilder &builder) +void addToWidget(Widget *widget, const Layout &layout) { - builder.stack.last().flush(); - QLayout *layout = builder.stack.last().layout; - builder.stack.pop_back(); - - if (builder.stack.last().isLayouting) { - builder.stack.last().pendingItems.append(ResultItem(layout)); - } else if (QWidget *widget = builder.stack.last().widget) { - widget->setLayout(layout); - } else - builder.stack.last().pendingItems.append(ResultItem(layout)); + layout.flush_(); + access(widget)->setLayout(access(&layout)); } -template -static void layoutingWidgetExit(LayoutBuilder &builder) +void addToLayout(Layout *layout, const Widget &inner) { - const Slice slice = builder.stack.last(); - T *w = qobject_cast(slice.widget); - for (const ResultItem &ri : slice.pendingItems) { - if (ri.widget) { - w->addWidget(ri.widget); - } else if (ri.layout) { - auto child = new QWidget; - child->setLayout(ri.layout); - w->addWidget(child); + LayoutItem item; + item.widget = access(&inner); + layout->addLayoutItem(item); +} + +void addToLayout(Layout *layout, QWidget *inner) +{ + LayoutItem item; + item.widget = inner; + layout->addLayoutItem(item); +} + +void addToLayout(Layout *layout, QLayout *inner) +{ + LayoutItem item; + item.layout = inner; + layout->addLayoutItem(item); +} + +void addToLayout(Layout *layout, const Layout &inner) +{ + inner.flush_(); + LayoutItem item; + item.layout = access(&inner); + layout->addLayoutItem(item); +} + +void addToLayout(Layout *layout, const LayoutModifier &inner) +{ + inner(layout); +} + +void addToLayout(Layout *layout, const QString &inner) +{ + LayoutItem item; + item.text = inner; + layout->addLayoutItem(item); +} + +void empty(Layout *iface) +{ + LayoutItem item; + item.empty = true; + iface->addLayoutItem(item); +} + +void hr(Layout *layout) +{ + layout->addLayoutItem(createHr()); +} + +void br(Layout *iface) +{ + iface->flush(); +} + +void st(Layout *iface) +{ + LayoutItem item; + item.stretch = 1; + iface->addLayoutItem(item); +} + +void noMargin(Layout *iface) +{ + iface->noMargin(); +} + +void normalMargin(Layout *iface) +{ + iface->normalMargin(); +} + +QFormLayout *Layout::asForm() +{ + return qobject_cast(access(this)); +} + +QGridLayout *Layout::asGrid() +{ + return qobject_cast(access(this)); +} + +QBoxLayout *Layout::asBox() +{ + return qobject_cast(access(this)); +} + +FlowLayout *Layout::asFlow() +{ + return dynamic_cast(access(this)); +} + +void Layout::flush() +{ + if (pendingItems.empty()) + return; + + if (QGridLayout *lt = asGrid()) { + for (const LayoutItem &item : std::as_const(pendingItems)) { + Qt::Alignment a; + if (currentGridColumn == 0 && useFormAlignment) { + // if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) { + // a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment); + } + if (item.widget) + lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a); + else if (item.layout) + lt->addLayout(item.layout, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a); + else if (!item.text.isEmpty()) + lt->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a); + currentGridColumn += item.spanCols; + // Intentionally not used, use 'br'/'empty' for vertical progress. + // currentGridRow += item.spanRows; } + ++currentGridRow; + currentGridColumn = 0; + pendingItems.clear(); + return; } - builder.stack.pop_back(); - builder.stack.last().pendingItems.append(ResultItem(w)); -} -static void widgetExit(LayoutBuilder &builder) -{ - QWidget *widget = builder.stack.last().widget; - builder.stack.pop_back(); - builder.stack.last().pendingItems.append(ResultItem(widget)); -} + if (QFormLayout *fl = asForm()) { + if (pendingItems.size() > 2) { + auto hbox = new QHBoxLayout; + hbox->setContentsMargins(0, 0, 0, 0); + for (size_t i = 1; i < pendingItems.size(); ++i) + addItemToBoxLayout(hbox, pendingItems.at(i)); + while (pendingItems.size() > 1) + pendingItems.pop_back(); + pendingItems.push_back(hbox); + } -Column::Column(std::initializer_list items) -{ - subItems = items; - onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); }; - onExit = layoutExit; -} + if (pendingItems.size() == 1) { // Only one item given, so this spans both columns. + const LayoutItem &f0 = pendingItems.at(0); + if (auto layout = f0.layout) + fl->addRow(layout); + else if (auto widget = f0.widget) + fl->addRow(widget); + } else if (pendingItems.size() == 2) { // Normal case, both columns used. + LayoutItem &f1 = pendingItems[1]; + const LayoutItem &f0 = pendingItems.at(0); + if (!f1.widget && !f1.layout && !f1.text.isEmpty()) + f1.widget = createLabel(f1.text); -Row::Row(std::initializer_list items) -{ - subItems = items; - onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); }; - onExit = layoutExit; -} + // QFormLayout accepts only widgets or text in the first column. + // FIXME: Should we be more generous? + if (f0.widget) { + if (f1.layout) + fl->addRow(f0.widget, f1.layout); + else if (f1.widget) + fl->addRow(f0.widget, f1.widget); + } else { + if (f1.layout) + fl->addRow(createLabel(f0.text), f1.layout); + else if (f1.widget) + fl->addRow(createLabel(f0.text), f1.widget); + } + } else { + QTC_CHECK(false); + } -Flow::Flow(std::initializer_list items) -{ - subItems = items; - onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); }; - onExit = layoutExit; -} - -Grid::Grid(std::initializer_list 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 items) -{ - subItems = items; - onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); }; - onExit = layoutExit; -} - -LayoutItem br() -{ - LayoutItem item; - item.onAdd = [](LayoutBuilder &builder) { - builder.stack.last().flush(); - }; - return item; -} - -LayoutItem empty() -{ - LayoutItem item; - item.onAdd = [](LayoutBuilder &builder) { - ResultItem ri; - ri.empty = true; - builder.stack.last().pendingItems.append(ri); - }; - return item; -} - -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; -} - -LayoutItem noMargin() -{ - return customMargin({}); -} - -LayoutItem normalMargin() -{ - return customMargin({9, 9, 9, 9}); -} - -LayoutItem customMargin(const QMargins &margin) -{ - LayoutItem item; - item.onAdd = [margin](LayoutBuilder &builder) { - if (auto layout = builder.stack.last().layout) - layout->setContentsMargins(margin); - else if (auto widget = builder.stack.last().widget) - widget->setContentsMargins(margin); - }; - return item; -} - -LayoutItem withFormAlignment() -{ - LayoutItem item; - item.onAdd = [](LayoutBuilder &builder) { - if (builder.stack.size() >= 2) { - if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) { - const Qt::Alignment align(widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment)); - builder.stack.last().align = align; + // Set up label as buddy if possible. + const int lastRow = fl->rowCount() - 1; + QLayoutItem *l = fl->itemAt(lastRow, QFormLayout::LabelRole); + QLayoutItem *f = fl->itemAt(lastRow, QFormLayout::FieldRole); + if (l && f) { + if (QLabel *label = qobject_cast(l->widget())) { + if (QWidget *widget = widgetForItem(f)) + label->setBuddy(widget); } } - }; - return item; + + pendingItems.clear(); + return; + } + + QTC_CHECK(false); // The other layouts shouldn't use flush() +} + +void Layout::flush_() const +{ + const_cast(this)->flush(); +} + +void withFormAlignment(Layout *iface) +{ + iface->useFormAlignment = true; +} + +// Flow + +Flow::Flow(std::initializer_list ps) +{ + ptr = new FlowLayout; + apply(this, ps); + flush(); +} + +// Row & Column + +Row::Row(std::initializer_list ps) +{ + ptr = new QHBoxLayout; + apply(this, ps); + flush(); +} + +Column::Column(std::initializer_list ps) +{ + ptr = new QVBoxLayout; + apply(this, ps); + flush(); +} + +// Grid + +Grid::Grid() +{ + ptr = new QGridLayout; +} + +Grid::Grid(std::initializer_list ps) +{ + ptr = new QGridLayout; + apply(this, ps); + flush(); +} + +// Form + +Form::Form() +{ + ptr = new QFormLayout; +} + +Form::Form(std::initializer_list ps) +{ + auto lt = new QFormLayout; + ptr = lt; + lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + apply(this, ps); + flush(); +} + +void Layout::fieldGrowthPolicy(int policy) +{ + if (auto lt = asForm()) + lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy)); +} + +QWidget *Layout::emerge() const +{ + const_cast(this)->flush(); + QWidget *widget = new QWidget; + widget->setLayout(access(this)); + return widget; } // "Widgets" -template -void setupWidget(LayoutItem *item) +Widget::Widget(std::initializer_list ps) { - item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); }; - item->onExit = widgetExit; -}; - -Widget::Widget(std::initializer_list items) -{ - this->subItems = items; - setupWidget(this); + ptr = new Implementation; + apply(this, ps); } -Group::Group(std::initializer_list items) +void Widget::resize(int w, int h) { - this->subItems = items; - setupWidget(this); + access(this)->resize(w, h); } -Stack::Stack(std::initializer_list items) +void Widget::setLayout(const Layout &layout) { - // We use a QStackedWidget instead of a QStackedLayout here because the latter will call - // "setVisible()" when a child is added, which can lead to the widget being spawned as a - // top-level widget. This can lead to the focus shifting away from the main application. - subItems = items; - onAdd = [](LayoutBuilder &builder) { - builder.stack.append(Slice(new QStackedWidget, true)); - }; - onExit = layoutingWidgetExit; + access(this)->setLayout(access(&layout)); } -PushButton::PushButton(std::initializer_list items) +void Widget::setWindowTitle(const QString &title) { - this->subItems = items; - setupWidget(this); + access(this)->setWindowTitle(title); } -SpinBox::SpinBox(std::initializer_list items) +void Widget::setToolTip(const QString &title) { - this->subItems = items; - setupWidget(this); + access(this)->setToolTip(title); } -TextEdit::TextEdit(std::initializer_list items) +void Widget::show() { - this->subItems = items; - setupWidget(this); + access(this)->show(); } -Splitter::Splitter(std::initializer_list items) +void Widget::noMargin(int) { - subItems = items; - onAdd = [](LayoutBuilder &builder) { - auto splitter = new QSplitter; - splitter->setOrientation(Qt::Vertical); - builder.stack.append(Slice(splitter, true)); - }; - onExit = layoutingWidgetExit; + customMargin({}); } -ToolBar::ToolBar(std::initializer_list items) +void Widget::normalMargin(int) { - subItems = items; - onAdd = [](LayoutBuilder &builder) { - auto toolbar = new QToolBar; - toolbar->setOrientation(Qt::Horizontal); - builder.stack.append(Slice(toolbar, true)); - }; - onExit = layoutingWidgetExit; + customMargin({9, 9, 9, 9}); } -TabWidget::TabWidget(std::initializer_list items) +void Widget::customMargin(const QMargins &margin) { - this->subItems = items; - setupWidget(this); + access(this)->setContentsMargins(margin); } -// Special Tab - -Tab::Tab(const QString &tabName, const LayoutItem &item) +QWidget *Widget::emerge() const { - 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(builder.stack.last().widget); - QTC_ASSERT(tabWidget, return); - tabWidget->addTab(inner, tabName); - }; + return access(this); +} + +// Label + +Label::Label(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +Label::Label(const QString &text) +{ + ptr = new Implementation; + setText(text); +} + +void Label::setText(const QString &text) +{ + access(this)->setText(text); +} + +// Group + +Group::Group(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +void Group::setTitle(const QString &title) +{ + access(this)->setTitle(title); + access(this)->setObjectName(title); +} + +void Group::setGroupChecker(const std::function &checker) +{ + checker(access(this)); +} + +// SpinBox + +SpinBox::SpinBox(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +void SpinBox::setValue(int val) +{ + access(this)->setValue(val); +} + +void SpinBox::onTextChanged(const std::function &func) +{ + QObject::connect(access(this), &QSpinBox::textChanged, func); +} + +// TextEdit + +TextEdit::TextEdit(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +void TextEdit::setText(const QString &text) +{ + access(this)->setText(text); +} + +// PushButton + +PushButton::PushButton(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +void PushButton::setText(const QString &text) +{ + access(this)->setText(text); +} + +void PushButton::onClicked(const std::function &func, QObject *guard) +{ + QObject::connect(access(this), &QAbstractButton::clicked, guard, func); +} + +// Stack + +// We use a QStackedWidget instead of a QStackedLayout here because the latter will call +// "setVisible()" when a child is added, which can lead to the widget being spawned as a +// top-level widget. This can lead to the focus shifting away from the main application. +Stack::Stack(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +void addToStack(Stack *stack, const Widget &inner) +{ + access(stack)->addWidget(inner.emerge()); +} + +void addToStack(Stack *stack, const Layout &inner) +{ + inner.flush_(); + access(stack)->addWidget(inner.emerge()); +} + +void addToStack(Stack *stack, QWidget *inner) +{ + access(stack)->addWidget(inner); +} + +// Splitter + +Splitter::Splitter(std::initializer_list ps) +{ + ptr = new Implementation; + access(this)->setOrientation(Qt::Vertical); + apply(this, ps); +} + +void addToSplitter(Splitter *splitter, QWidget *inner) +{ + access(splitter)->addWidget(inner); +} + +void addToSplitter(Splitter *splitter, const Widget &inner) +{ + access(splitter)->addWidget(inner.emerge()); +} + +void addToSplitter(Splitter *splitter, const Layout &inner) +{ + inner.flush_(); + access(splitter)->addWidget(inner.emerge()); +} + +// ToolBar + +ToolBar::ToolBar(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); + access(this)->setOrientation(Qt::Horizontal); +} + +// TabWidget + +TabWidget::TabWidget(std::initializer_list ps) +{ + ptr = new Implementation; + apply(this, ps); +} + +Tab::Tab(const QString &tabName, const Layout &inner) + : tabName(tabName), inner(inner) +{} + +void addToTabWidget(TabWidget *tabWidget, const Tab &tab) +{ + access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName); } // Special If -If::If(bool condition, const LayoutItems &items, const LayoutItems &other) +If::If(bool condition, + const std::initializer_list ifcase, + const std::initializer_list thencase) + : used(condition ? ifcase : thencase) +{} + +void addToLayout(Layout *layout, const If &inner) { - subItems.append(condition ? items : other); + for (const Layout::I &item : inner.used) + item.apply(layout); } -// Special Application - -Application::Application(std::initializer_list items) -{ - subItems = items; - setupWidget(this); - onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget. -} - -int Application::exec(int &argc, char *argv[]) -{ - QApplication app(argc, argv); - LayoutBuilder builder; - addItemHelper(builder, *this); - if (QWidget *widget = builder.stack.last().widget) - widget->show(); - return app.exec(); -} - -// "Properties" - -LayoutItem title(const QString &title) -{ - return [title](QObject *target) { - if (auto groupBox = qobject_cast(target)) { - groupBox->setTitle(title); - groupBox->setObjectName(title); - } else if (auto widget = qobject_cast(target)) { - widget->setWindowTitle(title); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem windowTitle(const QString &windowTitle) -{ - return [windowTitle](QObject *target) { - if (auto widget = qobject_cast(target)) { - widget->setWindowTitle(windowTitle); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem text(const QString &text) -{ - return [text](QObject *target) { - if (auto button = qobject_cast(target)) { - button->setText(text); - } else if (auto textEdit = qobject_cast(target)) { - textEdit->setText(text); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem tooltip(const QString &toolTip) -{ - return [toolTip](QObject *target) { - if (auto widget = qobject_cast(target)) { - widget->setToolTip(toolTip); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem spacing(int spacing) -{ - return [spacing](QObject *target) { - if (auto layout = qobject_cast(target)) { - layout->setSpacing(spacing); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem resize(int w, int h) -{ - return [w, h](QObject *target) { - if (auto widget = qobject_cast(target)) { - widget->resize(w, h); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem columnStretch(int column, int stretch) -{ - return [column, stretch](QObject *target) { - if (auto grid = qobject_cast(target)) { - grid->setColumnStretch(column, stretch); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy) -{ - return [policy](QObject *target) { - if (auto form = qobject_cast(target)) { - form->setFieldGrowthPolicy(policy); - } else { - QTC_CHECK(false); - } - }; -} - - -// Id based setters - -LayoutItem id(ID &out) -{ - return [&out](QObject *target) { out.ob = target; }; -} - -void setText(ID id, const QString &text) -{ - if (auto textEdit = qobject_cast(id.ob)) - textEdit->setText(text); -} - -// Signals - -LayoutItem onClicked(const std::function &func, QObject *guard) -{ - return [func, guard](QObject *target) { - if (auto button = qobject_cast(target)) { - QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem onTextChanged(const std::function &func, QObject *guard) -{ - return [func, guard](QObject *target) { - if (auto button = qobject_cast(target)) { - QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func); - } else { - QTC_CHECK(false); - } - }; -} - -LayoutItem onValueChanged(const std::function &func, QObject *guard) -{ - return [func, guard](QObject *target) { - if (auto button = qobject_cast(target)) { - QObject::connect(button, &QSpinBox::valueChanged, guard ? guard : target, func); - } else { - QTC_CHECK(false); - } - }; -} - -// Convenience +// Specials QWidget *createHr(QWidget *parent) { @@ -1017,59 +928,49 @@ QWidget *createHr(QWidget *parent) return frame; } -// Singletons. +Span::Span(int cols, const Layout::I &item) + : item(item), spanCols(cols) +{} -LayoutItem::LayoutItem(const LayoutItem &t) +Span::Span(int cols, int rows, const Layout::I &item) + : item(item), spanCols(cols), spanRows(rows) +{} + +void addToLayout(Layout *layout, const Span &inner) { - operator=(t); + layout->addItem(inner.item); + if (layout->pendingItems.empty()) { + QTC_CHECK(inner.spanCols == 1 && inner.spanRows == 1); + return; + } + layout->pendingItems.back().spanCols = inner.spanCols; + layout->pendingItems.back().spanRows = inner.spanRows; } -void createItem(LayoutItem *item, LayoutItem(*t)()) +LayoutModifier spacing(int space) { - *item = t(); + return [space](Layout *iface) { iface->setSpacing(space); }; } -void createItem(LayoutItem *item, const std::function &t) +void addToLayout(Layout *layout, const Space &inner) { - item->setter = t; + if (auto lt = layout->asBox()) + lt->addSpacing(inner.space); } -void createItem(LayoutItem *item, QWidget *t) +void addToLayout(Layout *layout, const Stretch &inner) { - if (auto l = qobject_cast(t)) - l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse); - - item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); }; + if (auto lt = layout->asBox()) + lt->addStretch(inner.stretch); } -void createItem(LayoutItem *item, QLayout *t) -{ - item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); }; -} +// void createItem(LayoutItem *item, QWidget *t) +// { +// if (auto l = qobject_cast(t)) +// l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse); -void createItem(LayoutItem *item, const QString &t) -{ - item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); }; -} +// item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(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; - }; -} } // Layouting - -#include "layoutbuilder.moc" diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index c9fac7d8383..020ac32f9d0 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -3,12 +3,12 @@ #pragma once -#include -#include +#include #include -#include -#include +#include +#include +#include #if defined(UTILS_LIBRARY) # define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT @@ -19,251 +19,536 @@ #endif QT_BEGIN_NAMESPACE +class QBoxLayout; +class QFormLayout; +class QGridLayout; +class QGroupBox; +class QHBoxLayout; +class QLabel; class QLayout; class QMargins; class QObject; +class QPushButton; +class QSpinBox; +class QSplitter; +class QStackedWidget; +class QTabWidget; +class QTextEdit; +class QToolBar; +class QVBoxLayout; class QWidget; -template T qobject_cast(QObject *object); QT_END_NAMESPACE namespace Layouting { -// LayoutItem +class NestId {}; -class LayoutBuilder; -class LayoutItem; -using LayoutItems = QList; +template +class IdAndArg +{ +public: + IdAndArg(const T1 &id, const T2 &arg) : id(id), arg(arg) {} + const T1 id; + const T2 arg; // FIXME: Could be const &, but this would currently break bindTo(). +}; + +// The main dispatcher + +void doit(auto x, auto id, auto p); + +template class BuilderItem +{ +public: + // Nested child object + template + BuilderItem(Inner && p) + { + apply = [&p](X *x) { doit(x, NestId{}, std::forward(p)); }; + } + + // Property setter + template + BuilderItem(IdAndArg && idarg) + { + apply = [&idarg](X *x) { doit(x, idarg.id, idarg.arg); }; + } + + std::function apply; +}; + + +////////////////////////////////////////////// + +// +// Basic +// + +class QTCREATOR_UTILS_EXPORT Thing +{ +public: + void *ptr; // The product. +}; + +class QTCREATOR_UTILS_EXPORT Object : public Thing +{ +public: + using Implementation = QObject; + using I = BuilderItem; + + Object() = default; + Object(std::initializer_list ps); +}; + +// +// Layouts +// + +class FlowLayout; +class Layout; +using LayoutModifier = std::function; +// using LayoutModifier = void(*)(Layout *); class QTCREATOR_UTILS_EXPORT LayoutItem { public: - using Setter = std::function; - - LayoutItem(); ~LayoutItem(); + LayoutItem(); + LayoutItem(QLayout *l) : layout(l) {} + LayoutItem(QWidget *w) : widget(w) {} + LayoutItem(const QString &t) : text(t) {} + LayoutItem(const LayoutModifier &inner); - LayoutItem(const LayoutItem &t); - LayoutItem &operator=(const LayoutItem &t) = default; - - template LayoutItem(const T &t) - { - if constexpr (std::is_base_of_v) - LayoutItem::operator=(t); - else - createItem(this, t); - } - - void attachTo(QWidget *w) const; - QWidget *emerge(); - - void addItem(const LayoutItem &item); - void addItems(const LayoutItems &items); - void addRow(const LayoutItems &items); - - std::function onAdd; - std::function onExit; - std::function setter; - LayoutItems subItems; + QString text; + QLayout *layout = nullptr; + QWidget *widget = nullptr; + int stretch = -1; + int spanCols = 1; + int spanRows = 1; + bool empty = false; + LayoutModifier ownerModifier; + //Qt::Alignment align = {}; }; -// Special items - -class QTCREATOR_UTILS_EXPORT Space +class QTCREATOR_UTILS_EXPORT Layout : public Object { public: - explicit Space(int space) : space(space) {} - const int space; + using Implementation = QLayout; + using I = BuilderItem; + + Layout() = default; + Layout(Implementation *w) { ptr = w; } + + void span(int cols, int rows); + void noMargin(); + void normalMargin(); + void customMargin(const QMargins &margin); + void setColumnStretch(int cols, int rows); + void setSpacing(int space); + + void attachTo(QWidget *); + void addItem(I item); + void addItems(std::initializer_list items); + void addRow(std::initializer_list items); + void addLayoutItem(const LayoutItem &item); + + void flush(); + void flush_() const; + void fieldGrowthPolicy(int policy); + + QWidget *emerge() const; + + QFormLayout *asForm(); + QGridLayout *asGrid(); + QBoxLayout *asBox(); + FlowLayout *asFlow(); + + // Grid-only + int currentGridColumn = 0; + int currentGridRow = 0; + //Qt::Alignment align = {}; + bool useFormAlignment = false; + + std::vector pendingItems; +}; + +class QTCREATOR_UTILS_EXPORT Column : public Layout +{ +public: + using Implementation = QVBoxLayout; + using I = BuilderItem; + + Column(std::initializer_list ps); +}; + +class QTCREATOR_UTILS_EXPORT Row : public Layout +{ +public: + using Implementation = QHBoxLayout; + using I = BuilderItem; + + Row(std::initializer_list ps); +}; + +class QTCREATOR_UTILS_EXPORT Form : public Layout +{ +public: + using Implementation = QFormLayout; + using I = BuilderItem
; + + Form(); + Form(std::initializer_list ps); +}; + +class QTCREATOR_UTILS_EXPORT Grid : public Layout +{ +public: + using Implementation = QGridLayout; + using I = BuilderItem; + + Grid(); + Grid(std::initializer_list ps); +}; + +class QTCREATOR_UTILS_EXPORT Flow : public Layout +{ +public: + Flow(std::initializer_list ps); }; class QTCREATOR_UTILS_EXPORT Stretch { public: - explicit Stretch(int stretch = 1) : stretch(stretch) {} - const int stretch; + explicit Stretch(int stretch) : stretch(stretch) {} + + int stretch; +}; + +class QTCREATOR_UTILS_EXPORT Space +{ +public: + explicit Space(int space) : space(space) {} + + int space; }; class QTCREATOR_UTILS_EXPORT Span { public: - Span(int span, const LayoutItem &item) : span(span), item(item) {} - const int span; - LayoutItem item; + Span(int cols, const Layout::I &item); + Span(int cols, int rows, const Layout::I &item); + + Layout::I item; + int spanCols = 1; + int spanRows = 1; }; -class QTCREATOR_UTILS_EXPORT Column : public LayoutItem +// +// Widgets +// + +class QTCREATOR_UTILS_EXPORT Widget : public Object { public: - Column(std::initializer_list items); + using Implementation = QWidget; + using I = BuilderItem; + + Widget() = default; + Widget(std::initializer_list ps); + Widget(Implementation *w) { ptr = w; } + + QWidget *emerge() const; + + void show(); + void resize(int, int); + void setLayout(const Layout &layout); + void setWindowTitle(const QString &); + void setToolTip(const QString &); + void noMargin(int = 0); + void normalMargin(int = 0); + void customMargin(const QMargins &margin); }; -class QTCREATOR_UTILS_EXPORT Row : public LayoutItem +class QTCREATOR_UTILS_EXPORT Label : public Widget { public: - Row(std::initializer_list items); + using Implementation = QLabel; + using I = BuilderItem