From 434c624b6f60375a5f6f3ace50ccf3dd93664f2f Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 18 Feb 2021 14:18:34 +0100 Subject: [PATCH] LayoutBuilder: Add support for VBoxLayout and HBoxLayout Change-Id: Ieeef8244a05ffb1b642843c6471f92e2b154cf8a Reviewed-by: David Schulz --- src/libs/utils/layoutbuilder.cpp | 129 ++++++++++++++++++++++++++++--- src/libs/utils/layoutbuilder.h | 114 ++++++++++++++++++++++++--- 2 files changed, 222 insertions(+), 21 deletions(-) diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 2edb07c343c..b2f40ff259a 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -43,8 +44,12 @@ namespace Utils { The LayoutType enum describes the type of \c QLayout a layout builder operates on. - \value FormLayout - \value GridLayout + \value Form + \value Grid + \value HBox + \value VBox + \value HBoxWithMargins + \value VBoxWithMargins */ /*! @@ -90,14 +95,41 @@ LayoutBuilder::LayoutItem::LayoutItem(QWidget *widget, int span, Alignment align \sa BaseAspect::addToLayout() */ -LayoutBuilder::LayoutItem::LayoutItem(BaseAspect *aspect) - : aspect(aspect) +LayoutBuilder::LayoutItem::LayoutItem(BaseAspect &aspect, int span, Alignment align) + : aspect(&aspect), span(span), align(align) +{} + +LayoutBuilder::LayoutItem::LayoutItem(BaseAspect *aspect, int span, Alignment align) + : aspect(aspect), span(span), align(align) {} /*! Constructs a layout item containing some static \a text. */ -LayoutBuilder::LayoutItem::LayoutItem(const QString &text) : text(text) {} +LayoutBuilder::LayoutItem::LayoutItem(const QString &text, int span, Alignment align) + : text(text), span(span), align(align) +{} + +/*! + Constructs a layout item from the contents of another LayoutBuilder + */ +LayoutBuilder::LayoutItem::LayoutItem(const LayoutBuilder &builder, int span, Alignment align) + : widget(builder.parentWidget()), span(span), align(align) +{} + +/*! + \class Utils::LayoutBuilder::Space + \inmodule QtCreator + + \brief The LayoutBuilder::Space class represents some empty space in a layout. + */ + +/*! + \class Utils::LayoutBuilder::Stretch + \inmodule QtCreator + + \brief The LayoutBuilder::Stretch class represents some stretch in a layout. + */ /*! \class Utils::LayoutBuilder @@ -121,13 +153,44 @@ LayoutBuilder::LayoutItem::LayoutItem(const QString &text) : text(text) {} */ LayoutBuilder::LayoutBuilder(QWidget *parent, LayoutType layoutType) { - if (layoutType == FormLayout) { + init(parent, layoutType); +} + +LayoutBuilder::LayoutBuilder(LayoutType layoutType, const LayoutItems &items) +{ + init(new QWidget, layoutType); + addItems(items); +} + +void LayoutBuilder::init(QWidget *parent, LayoutType layoutType) +{ + switch (layoutType) { + case Form: m_formLayout = new QFormLayout(parent); - m_formLayout->setContentsMargins(0, 0, 0, 0); m_formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); - } else { + break; + case Grid: m_gridLayout = new QGridLayout(parent); - m_gridLayout->setContentsMargins(0, 0, 0, 0); + break; + case HBox: + case HBoxWithMargins: + m_boxLayout = new QHBoxLayout(parent); + break; + case VBox: + case VBoxWithMargins: + m_boxLayout = new QVBoxLayout(parent); + break; + } + + switch (layoutType) { + case Form: + case Grid: + case HBox: + case VBox: + layout()->setContentsMargins(0, 0, 0, 0); + break; + default: + break; } } @@ -192,7 +255,7 @@ LayoutBuilder &LayoutBuilder::addRow(const LayoutItem &item) \sa finishRow(), addItem(), addItems() */ -LayoutBuilder &LayoutBuilder::addRow(const QList &items) +LayoutBuilder &LayoutBuilder::addRow(const LayoutItems &items) { return finishRow().addItems(items); } @@ -255,9 +318,17 @@ QLayout *LayoutBuilder::layout() const { if (m_formLayout) return m_formLayout; + if (m_boxLayout) + return m_boxLayout; return m_gridLayout; } +QWidget *LayoutBuilder::parentWidget() const +{ + QLayout *l = layout(); + return l ? l->parentWidget() : nullptr; +} + /*! Adds the layout item \a item to the current row. */ @@ -277,6 +348,16 @@ LayoutBuilder &LayoutBuilder::addItem(const LayoutItem &item) m_gridLayout->addWidget(widget, m_currentGridRow, m_currentGridColumn, 1, item.span, align); } m_currentGridColumn += item.span; + if (item.linebreak) + finishRow(); + } else if (m_boxLayout) { + if (auto widget = item.widget) { + m_boxLayout->addWidget(widget); + } else if (item.stretch != 0) { + m_boxLayout->addStretch(item.stretch); + } else if (item.space != 0) { + m_boxLayout->addSpacing(item.space); + } } else { m_pendingFormItems.append(item); } @@ -294,4 +375,32 @@ LayoutBuilder &LayoutBuilder::addItems(const QList &i return *this; } +void LayoutBuilder::attachTo(QWidget *w, bool stretchAtBottom) +{ + LayoutBuilder builder(w, VBoxWithMargins); + builder.addItem(*this); + if (stretchAtBottom) + builder.addItem(Stretch()); +} + +namespace Layouting { + +Group::Group(std::initializer_list items) + : LayoutBuilder(new QGroupBox, VBoxWithMargins) +{ + addItems(items); +} + +Layouting::Group &Layouting::Group::withTitle(const QString &title) +{ + if (auto box = qobject_cast(parentWidget())) + box->setTitle(title); + return *this; +} + +Box::Box(LayoutType type, const LayoutItems &items) + : LayoutBuilder(type, items) +{} + +} // Layouting } // Utils diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 1a2ad1f5e51..acedd473f55 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -31,6 +31,7 @@ #include QT_BEGIN_NAMESPACE +class QBoxLayout; class QFormLayout; class QGridLayout; class QLayout; @@ -44,22 +45,29 @@ class BaseAspect; class QTCREATOR_UTILS_EXPORT LayoutBuilder { public: - enum LayoutType { GridLayout, FormLayout }; + enum LayoutType { + Form, // Plain QFormLayout, without contentMargins + Grid, // Plain QGridLayout, without contentMargins + HBox, // Plain QHBoxLayout, without contentMargins + VBox, // Plain QVBoxLayout, without contentMargins + HBoxWithMargins, // QHBoxLayout with margins + VBoxWithMargins, // QVBoxLayout with margins + // Compat + FormLayout = Form, // FIXME: Remove + GridLayout = Grid, // FIXME: Remove + }; enum Alignment { DefaultAlignment, AlignAsFormLabel }; - explicit LayoutBuilder(QWidget *parent, LayoutType layoutType = FormLayout); - explicit LayoutBuilder(QLayout *layout); // Adds to existing layout. - - ~LayoutBuilder(); - class QTCREATOR_UTILS_EXPORT LayoutItem { public: LayoutItem(); LayoutItem(QLayout *layout, int span = 1, Alignment align = {}); LayoutItem(QWidget *widget, int span = 1, Alignment align = {}); - LayoutItem(BaseAspect *aspect); - LayoutItem(const QString &text); + LayoutItem(BaseAspect *aspect, int span = 1, Alignment align = {}); // Remove + LayoutItem(BaseAspect &aspect, int span = 1, Alignment align = {}); + LayoutItem(const QString &text, int span = 1, Alignment align = {}); + LayoutItem(const LayoutBuilder &builder, int span = 1, Alignment align = {}); QLayout *layout = nullptr; QWidget *widget = nullptr; @@ -67,25 +75,109 @@ public: QString text; int span = 1; Alignment align; + int space = 0; + int stretch = 0; + bool linebreak = false; }; + using LayoutItems = QList; + + class QTCREATOR_UTILS_EXPORT Space : public LayoutItem + { + public: + explicit Space(int space_) { space = space_; } + }; + + class QTCREATOR_UTILS_EXPORT Stretch : public LayoutItem + { + public: + explicit Stretch(int stretch_ = 1) { stretch = stretch_; } + }; + + class QTCREATOR_UTILS_EXPORT Break : public LayoutItem + { + public: + Break() { linebreak = true; } + }; + + explicit LayoutBuilder(QWidget *parent, LayoutType layoutType = Form); + explicit LayoutBuilder(QLayout *layout); // Adds to existing layout. + explicit LayoutBuilder(LayoutType layoutType, const LayoutItems &items = {}); + + LayoutBuilder(const LayoutBuilder &) = delete; + LayoutBuilder(LayoutBuilder &&) = default; + LayoutBuilder &operator=(const LayoutBuilder &) = delete; + LayoutBuilder &operator=(LayoutBuilder &&) = default; + + ~LayoutBuilder(); + LayoutBuilder &addItem(const LayoutItem &item); - LayoutBuilder &addItems(const QList &items); + LayoutBuilder &addItems(const LayoutItems &items); LayoutBuilder &finishRow(); LayoutBuilder &addRow(const LayoutItem &item); - LayoutBuilder &addRow(const QList &items); + LayoutBuilder &addRow(const LayoutItems &items); QLayout *layout() const; + QWidget *parentWidget() const; + + void attachTo(QWidget *w, bool stretchAtBottom = true); private: void flushPendingFormItems(); + void init(QWidget *parent, LayoutType layoutType); QFormLayout *m_formLayout = nullptr; QGridLayout *m_gridLayout = nullptr; - QList m_pendingFormItems; + QBoxLayout *m_boxLayout = nullptr; + LayoutItems m_pendingFormItems; int m_currentGridRow = 0; int m_currentGridColumn = 0; }; +namespace Layouting { + +class QTCREATOR_UTILS_EXPORT Group : public LayoutBuilder +{ +public: + Group(std::initializer_list items); + + Group &withTitle(const QString &title); +}; + +class QTCREATOR_UTILS_EXPORT Box : public LayoutBuilder +{ +public: + Box(LayoutType type, const LayoutItems &items); +}; + +class QTCREATOR_UTILS_EXPORT Column : public Box +{ +public: + Column(std::initializer_list items) + : Box(VBox, items) + {} +}; + +class QTCREATOR_UTILS_EXPORT Row : public Box +{ +public: + Row(std::initializer_list items) + : Box(HBox, items) + {} +}; + +class QTCREATOR_UTILS_EXPORT Grid : public Box +{ +public: + Grid(std::initializer_list items) + : Box(GridLayout, items) + {} +}; + +using Stretch = LayoutBuilder::Stretch; +using Space = LayoutBuilder::Space; +using Break = LayoutBuilder::Break; + +} } // namespace Utils